Fix integer overflows in internal memalign and malloc functions [BZ #22343]
diff mbox series

Message ID 20180117202641.GA58783@aloka.lostca.se
State New
Headers show
Series
  • Fix integer overflows in internal memalign and malloc functions [BZ #22343]
Related show

Commit Message

Arjun Shankar Jan. 17, 2018, 8:26 p.m. UTC
When posix_memalign is called with an alignment less than MALLOC_ALIGNMENT
and a requested size close to SIZE_MAX, it falls back to malloc code
(because the alignment of a block returned by malloc is sufficient to
satisfy the call).  In this case, an integer overflow in _int_malloc leads
to posix_memalign incorrectly returning successfully.

Upon fixing this and writing a somewhat thorough regression test, it was
discovered that when posix_memalign is called with an alignment larger than
MALLOC_ALIGNMENT (so it uses _int_memalign instead) and a requested size
close to SIZE_MAX, a different integer overflow in _int_memalign leads to
posix_memalign incorrectly returning successfully.

Both integer overflows affect other memory allocation functions that use
_int_malloc (one affected malloc in x86) or _int_memalign as well.

This commit fixes both integer overflows.  In addition to this, it adds a
regression test to guard against false successful allocations by the
following memory allocation functions when called with too-large allocation
sizes and, where relevant, various valid alignments:
malloc, realloc, calloc, reallocarray, memalign, posix_memalign,
aligned_alloc, valloc, and pvalloc.

ChangeLog:

2018-01-16  Arjun Shankar  <arjun@redhat.com>

	[BZ #22343]
	* malloc/malloc.c (checked_request2size): call REQUEST_OUT_OF_RANGE
	after padding.
	(_int_memalign): check for integer overflow before calling
	_int_malloc.
	* malloc/tst-malloc-too-large.c: New test.
	* malloc/Makefile: Add tst-malloc-too-large.
---
v1 discussion: https://sourceware.org/ml/libc-alpha/2018-01/msg00133.html

v2:
 * uses braces nested within parentheses for checked_request2size
 * increases timeout for tst-malloc-too-large to 5 seconds; I realized that
   the test runs for 1.4s on my fairly modern laptop)

 malloc/Makefile               |   1 +
 malloc/malloc.c               |  30 +++--
 malloc/tst-malloc-too-large.c | 254 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 277 insertions(+), 8 deletions(-)
 create mode 100644 malloc/tst-malloc-too-large.c

Comments

Florian Weimer Jan. 17, 2018, 8:52 p.m. UTC | #1
On 01/17/2018 09:26 PM, Arjun Shankar wrote:
> +#define TIMEOUT 5

The default timeout is 20 seconds (scaled by the timeout factor), so 
this reduces the timeout to one fourth of the default value.  I think 
you should remove this.

(I haven't looked at the other parts of the patch.)

Thanks,
Florian
Carlos O'Donell Jan. 17, 2018, 9:45 p.m. UTC | #2
On 01/17/2018 12:52 PM, Florian Weimer wrote:
> On 01/17/2018 09:26 PM, Arjun Shankar wrote:
>> +#define TIMEOUT 5
> 
> The default timeout is 20 seconds (scaled by the timeout factor), so this reduces the timeout to one fourth of the default value.  I think you should remove this.
> 
> (I haven't looked at the other parts of the patch.)

support/test-driver.h:    DEFAULT_TIMEOUT = 20,
 
You are absolutely right, I thought the default was 2 seconds, but
that's just my age showing... the old test-skeleton.c had a 2 second
timeout. That has changed to 20 seconds with the new support framework.
Carlos O'Donell Jan. 17, 2018, 10:01 p.m. UTC | #3
On 01/17/2018 12:26 PM, Arjun Shankar wrote:
> When posix_memalign is called with an alignment less than MALLOC_ALIGNMENT
> and a requested size close to SIZE_MAX, it falls back to malloc code
> (because the alignment of a block returned by malloc is sufficient to
> satisfy the call).  In this case, an integer overflow in _int_malloc leads
> to posix_memalign incorrectly returning successfully.
> 
> Upon fixing this and writing a somewhat thorough regression test, it was
> discovered that when posix_memalign is called with an alignment larger than
> MALLOC_ALIGNMENT (so it uses _int_memalign instead) and a requested size
> close to SIZE_MAX, a different integer overflow in _int_memalign leads to
> posix_memalign incorrectly returning successfully.
> 
> Both integer overflows affect other memory allocation functions that use
> _int_malloc (one affected malloc in x86) or _int_memalign as well.
> 
> This commit fixes both integer overflows.  In addition to this, it adds a
> regression test to guard against false successful allocations by the
> following memory allocation functions when called with too-large allocation
> sizes and, where relevant, various valid alignments:
> malloc, realloc, calloc, reallocarray, memalign, posix_memalign,
> aligned_alloc, valloc, and pvalloc.

OK with the removal of the timeout (new support framework has 20s default 
timeout which I didn't know about!), and formatting adjustment.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> ChangeLog:
> 
> 2018-01-16  Arjun Shankar  <arjun@redhat.com>
> 
> 	[BZ #22343]
> 	* malloc/malloc.c (checked_request2size): call REQUEST_OUT_OF_RANGE
> 	after padding.
> 	(_int_memalign): check for integer overflow before calling
> 	_int_malloc.
> 	* malloc/tst-malloc-too-large.c: New test.
> 	* malloc/Makefile: Add tst-malloc-too-large.
> ---
> v1 discussion: https://sourceware.org/ml/libc-alpha/2018-01/msg00133.html
> 
> v2:
>  * uses braces nested within parentheses for checked_request2size
>  * increases timeout for tst-malloc-too-large to 5 seconds; I realized that
>    the test runs for 1.4s on my fairly modern laptop)

OK.

> 
>  malloc/Makefile               |   1 +
>  malloc/malloc.c               |  30 +++--
>  malloc/tst-malloc-too-large.c | 254 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 277 insertions(+), 8 deletions(-)
>  create mode 100644 malloc/tst-malloc-too-large.c
> 
> diff --git a/malloc/Makefile b/malloc/Makefile
> index 4266c2b66b..17873e67c4 100644
> --- a/malloc/Makefile
> +++ b/malloc/Makefile
> @@ -36,6 +36,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
>  	 tst-alloc_buffer \
>  	 tst-malloc-tcache-leak \
>  	 tst-malloc_info \
> +	 tst-malloc-too-large \

OK.

>  
>  tests-static := \
>  	 tst-interpose-static-nothread \
> diff --git a/malloc/malloc.c b/malloc/malloc.c
> index f5aafd2c05..740bb16799 100644
> --- a/malloc/malloc.c
> +++ b/malloc/malloc.c
> @@ -1224,14 +1224,21 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>     MINSIZE :                                                      \
>     ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
>  
> -/*  Same, except also perform argument check */
> -
> -#define checked_request2size(req, sz)                             \
> -  if (REQUEST_OUT_OF_RANGE (req)) {					      \
> -      __set_errno (ENOMEM);						      \
> -      return 0;								      \
> -    }									      \
> -  (sz) = request2size (req);
> +/* Same, except also perform an argument and result check.  First, we check
> +   that the padding done by request2size didn't result in an integer
> +   overflow.  Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
> +   size isn't so large that a later alignment would lead to another integer
> +   overflow.  */
> +#define checked_request2size(req, sz)   \
> +    ({                                  \
> +      (sz) = request2size (req);        \
> +      if (((sz) < (req))                \
> +          || REQUEST_OUT_OF_RANGE (sz)) \
> +        {                               \
> +          __set_errno (ENOMEM);         \
> +          return 0;                     \
> +        }                               \
> +    })

My apologies, let me clarify again what I was looking for.

({ start on the first char as-if it were a function.

e.g.

#define checked_request2size(req, sz) \
({								\
  (sz) = request2size (req);					\
  if (((sz) < (req))                				\
      || REQUEST_OUT_OF_RANGE (sz)) 				\
    {                               				\
      __set_errno (ENOMEM);         				\
      return 0;                     				\
    }                               				\
})

\'s tabbed+spaced out to column 79, but first \ is right beside the
function (IMO the best style).

OK with those changes.

>  
>  /*
>     --------------- Physical chunk operations ---------------
> @@ -4678,6 +4685,13 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
>     */
>  
>  
> +  /* Check for overflow.  */
> +  if (nb > SIZE_MAX - alignment - MINSIZE)
> +    {
> +      __set_errno (ENOMEM);
> +      return 0;
> +    }

OK.

> +
>    /* Call malloc with worst case padding to hit alignment. */
>  
>    m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
> diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c
> new file mode 100644
> index 0000000000..1ab3ef1764
> --- /dev/null
> +++ b/malloc/tst-malloc-too-large.c
> @@ -0,0 +1,254 @@
> +/* Test and verify that too-large memory allocations fail with ENOMEM.

OK.

> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +/* Bug 22375 reported a regression in malloc where if after malloc'ing then
> +   free'ing a small block of memory, malloc is then called with a really
> +   large size argument (close to SIZE_MAX): instead of returning NULL and
> +   setting errno to ENOMEM, malloc incorrectly returns the previously
> +   allocated block instead.  Bug 22343 reported a similar case where
> +   posix_memalign incorrectly returns successfully when called with an with
> +   a really large size argument.
> +
> +   Both of these were caused by integer overflows in the allocator when it
> +   was trying to pad the requested size to allow for book-keeping or
> +   alignment.  This test guards against such bugs by repeatedly allocating
> +   and freeing small blocks of memory then trying to allocate various block
> +   sizes larger than the memory bus width of 64-bit targets, or almost
> +   as large as SIZE_MAX on 32-bit targets supported by glibc.  In each case,
> +   it verifies that such impossibly large allocations correctly fail.  */
> +
> +
> +#include <stdlib.h>
> +#include <malloc.h>
> +#include <errno.h>
> +#include <stdint.h>
> +#include <sys/resource.h>
> +#include <libc-diag.h>
> +#include <support/check.h>
> +#include <unistd.h>
> +#include <sys/param.h>
> +
> +
> +/* This function prepares for each 'too-large memory allocation' test by
> +   performing a small successful malloc/free and resetting errno prior to
> +   the actual test.  */
> +static void
> +test_setup (void)
> +{
> +  void *volatile ptr = malloc (16);
> +  TEST_VERIFY_EXIT (ptr != NULL);
> +  free (ptr);
> +  errno = 0;
> +}
> +
> +
> +/* This function tests each of:
> +   - malloc (SIZE)
> +   - realloc (PTR_FOR_REALLOC, SIZE)
> +   - for various values of NMEMB:
> +    - calloc (NMEMB, SIZE/NMEMB)
> +    - calloc (SIZE/NMEMB, NMEMB)
> +    - reallocarray (PTR_FOR_REALLOC, NMEMB, SIZE/NMEMB)
> +    - reallocarray (PTR_FOR_REALLOC, SIZE/NMEMB, NMEMB)
> +   and precedes each of these tests with a small malloc/free before it.  */
> +static void
> +test_large_allocations (size_t size)
> +{
> +  void * ptr_to_realloc;
> +
> +  test_setup ();
> +  TEST_VERIFY (malloc (size) == NULL);
> +  TEST_VERIFY (errno == ENOMEM);
> +
> +  ptr_to_realloc = malloc (16);
> +  TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
> +  test_setup ();
> +  TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
> +  TEST_VERIFY (errno == ENOMEM);
> +  free (ptr_to_realloc);
> +
> +  for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
> +    if ((size % nmemb) == 0)
> +      {
> +        test_setup ();
> +        TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
> +        TEST_VERIFY (errno == ENOMEM);
> +
> +        test_setup ();
> +        TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
> +        TEST_VERIFY (errno == ENOMEM);
> +
> +        ptr_to_realloc = malloc (16);
> +        TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
> +        test_setup ();
> +        TEST_VERIFY (reallocarray (ptr_to_realloc, nmemb, size / nmemb) == NULL);
> +        TEST_VERIFY (errno == ENOMEM);
> +        free (ptr_to_realloc);
> +
> +        ptr_to_realloc = malloc (16);
> +        TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
> +        test_setup ();
> +        TEST_VERIFY (reallocarray (ptr_to_realloc, size / nmemb, nmemb) == NULL);
> +        TEST_VERIFY (errno == ENOMEM);
> +        free (ptr_to_realloc);
> +      }
> +    else
> +      break;
> +}

OK.

> +
> +
> +static long pagesize;
> +
> +/* This function tests the following aligned memory allocation functions
> +   using several valid alignments and precedes each allocation test with a
> +   small malloc/free before it:
> +   memalign, posix_memalign, aligned_alloc, valloc, pvalloc.  */
> +static void
> +test_large_aligned_allocations (size_t size)
> +{
> +  /* PTR stores the result of posix_memalign but since all those calls

s/PTR/ptr/g

When writing 'PTR' you are referring to the value of ptr, and since here
you speak about the pointer itself you use just the name ptr.

Please see:
https://www.gnu.org/prep/standards/standards.html#Comments

> +     should fail, posix_memalign should never touch PTR.  We set it to

s/tough PTR/change ptr/g

> +     NULL here and later on we check that it remains NULL after each
> +     posix_memalign call.  */
> +  void * ptr = NULL;
> +
> +  size_t align;
> +
> +  /* All aligned memory allocation functions expect an alignment that is a
> +     power of 2.  Given this, we test each of them with every valid
> +     alignment from 1 thru PAGESIZE.  */

OK.

> +  for (align = 1; align <= pagesize; align *= 2)
> +    {
> +      test_setup ();
> +      TEST_VERIFY (memalign (align, size) == NULL);
> +      TEST_VERIFY (errno == ENOMEM);
> +
> +      /* posix_memalign expects an alignment that is a power of 2 *and* a
> +         multiple of sizeof (void *).  */
> +      if ((align % sizeof (void *)) == 0)
> +        {
> +          test_setup ();
> +          TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
> +          TEST_VERIFY (ptr == NULL);
> +        }
> +
> +      /* aligned_alloc expects a size that is a multiple of alignment.  */
> +      if ((size % align) == 0)
> +        {
> +          test_setup ();
> +          TEST_VERIFY (aligned_alloc (align, size) == NULL);
> +          TEST_VERIFY (errno == ENOMEM);
> +        }
> +    }
> +
> +  /* Both valloc and pvalloc return page-aligned memory.  */
> +
> +  test_setup ();
> +  TEST_VERIFY (valloc (size) == NULL);
> +  TEST_VERIFY (errno == ENOMEM);
> +
> +  test_setup ();
> +  TEST_VERIFY (pvalloc (size) == NULL);
> +  TEST_VERIFY (errno == ENOMEM);
> +}
> +

OK.

> +
> +#define FOURTEEN_ON_BITS ((1UL << 14) - 1)
> +#define FIFTY_ON_BITS ((1UL << 50) - 1)
> +
> +
> +static int
> +do_test (void)
> +{
> +
> +#if __WORDSIZE >= 64
> +
> +  /* This test assumes that none of the supported targets have an address
> +     bus wider than 50 bits, and that therefore allocations for sizes wider
> +     than 50 bits will fail.  Here, we ensure that the assumption continues
> +     to be true in the future when we might have address buses wider than 50
> +     bits.  */
> +
> +  struct rlimit alloc_size_limit
> +    = {
> +        .rlim_cur = FIFTY_ON_BITS,
> +        .rlim_max = FIFTY_ON_BITS
> +      };
> +
> +  setrlimit (RLIMIT_AS, &alloc_size_limit);
> +
> +#endif /* __WORDSIZE >= 64 */
> +
> +  DIAG_PUSH_NEEDS_COMMENT;
> +#if __GNUC_PREREQ (7, 0)
> +  /* GCC 7 warns about too-large allocations; here we want to test
> +     that they fail.  */
> +  DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
> +#endif
> +
> +  /* Aligned memory allocation functions need to be tested up to alignment
> +     size equivalent to page size, which should be a power of 2.  */
> +  pagesize = sysconf (_SC_PAGESIZE);
> +  TEST_VERIFY_EXIT (powerof2 (pagesize));
> +
> +  /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
> +     in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
> +
> +     We can expect that this range of allocation sizes will always lead to
> +     an allocation failure on both 64 and 32 bit targets, because:
> +
> +     1. no currently supported 64-bit target has an address bus wider than
> +     50 bits -- and (2^64 - 2^14) is much wider than that;
> +
> +     2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
> +     addressable, glibc itself is more than 2^14 bytes in size, and
> +     therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
> +     available.  */
> +
> +  for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
> +    {
> +      test_large_allocations (SIZE_MAX - i);
> +      test_large_aligned_allocations (SIZE_MAX - i);
> +    }
> +
> +#if __WORDSIZE >= 64
> +  /* On 64-bit targets, we need to test a much wider range of too-large
> +     sizes, so we test at intervals of (1 << 50) that allocation sizes
> +     ranging from SIZE_MAX down to (1 << 50) fail:
> +     The 14 MSBs are decremented starting from "all ON" going down to 1,
> +     the 50 LSBs are "all ON" and then "all OFF" during every iteration.  */
> +  for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
> +    {
> +      size_t size = (msbs << 50) | FIFTY_ON_BITS;
> +      test_large_allocations (size);
> +      test_large_aligned_allocations (size);
> +
> +      size = msbs << 50;
> +      test_large_allocations (size);
> +      test_large_aligned_allocations (size);
> +    }
> +#endif /* __WORDSIZE >= 64 */
> +
> +  DIAG_POP_NEEDS_COMMENT;
> +
> +  return 0;
> +}
> +
> +
> +#define TIMEOUT 5
> +#include <support/test-driver.c>
> 

OK.
Florian Weimer Jan. 18, 2018, 8:04 a.m. UTC | #4
On 01/17/2018 10:45 PM, Carlos O'Donell wrote:
> You are absolutely right, I thought the default was 2 seconds, but
> that's just my age showing... the old test-skeleton.c had a 2 second
> timeout. That has changed to 20 seconds with the new support framework.

The original change of the timeout was made in test-skeleton.c and 
predates support/.  I had to check because I did not remember changing it.

Thanks,
Florian
Carlos O'Donell Jan. 19, 2018, 8:30 a.m. UTC | #5
On 01/18/2018 12:04 AM, Florian Weimer wrote:
> On 01/17/2018 10:45 PM, Carlos O'Donell wrote:
>> You are absolutely right, I thought the default was 2 seconds, but
>> that's just my age showing... the old test-skeleton.c had a 2 second
>> timeout. That has changed to 20 seconds with the new support framework.
> 
> The original change of the timeout was made in test-skeleton.c and
> predates support/.  I had to check because I did not remember
> changing it.
You are right, sorry, I see it was changed in a28605b22946c708f0a5c4f06307e1a17650ced8
on January 19th 2016 (2 years to the day). Not really "old" enough for it to
have seeped into our collective expectations :-)

Patch
diff mbox series

diff --git a/malloc/Makefile b/malloc/Makefile
index 4266c2b66b..17873e67c4 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -36,6 +36,7 @@  tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-alloc_buffer \
 	 tst-malloc-tcache-leak \
 	 tst-malloc_info \
+	 tst-malloc-too-large \
 
 tests-static := \
 	 tst-interpose-static-nothread \
diff --git a/malloc/malloc.c b/malloc/malloc.c
index f5aafd2c05..740bb16799 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1224,14 +1224,21 @@  nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    MINSIZE :                                                      \
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
 
-/*  Same, except also perform argument check */
-
-#define checked_request2size(req, sz)                             \
-  if (REQUEST_OUT_OF_RANGE (req)) {					      \
-      __set_errno (ENOMEM);						      \
-      return 0;								      \
-    }									      \
-  (sz) = request2size (req);
+/* Same, except also perform an argument and result check.  First, we check
+   that the padding done by request2size didn't result in an integer
+   overflow.  Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
+   size isn't so large that a later alignment would lead to another integer
+   overflow.  */
+#define checked_request2size(req, sz)   \
+    ({                                  \
+      (sz) = request2size (req);        \
+      if (((sz) < (req))                \
+          || REQUEST_OUT_OF_RANGE (sz)) \
+        {                               \
+          __set_errno (ENOMEM);         \
+          return 0;                     \
+        }                               \
+    })
 
 /*
    --------------- Physical chunk operations ---------------
@@ -4678,6 +4685,13 @@  _int_memalign (mstate av, size_t alignment, size_t bytes)
    */
 
 
+  /* Check for overflow.  */
+  if (nb > SIZE_MAX - alignment - MINSIZE)
+    {
+      __set_errno (ENOMEM);
+      return 0;
+    }
+
   /* Call malloc with worst case padding to hit alignment. */
 
   m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c
new file mode 100644
index 0000000000..1ab3ef1764
--- /dev/null
+++ b/malloc/tst-malloc-too-large.c
@@ -0,0 +1,254 @@ 
+/* Test and verify that too-large memory allocations fail with ENOMEM.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Bug 22375 reported a regression in malloc where if after malloc'ing then
+   free'ing a small block of memory, malloc is then called with a really
+   large size argument (close to SIZE_MAX): instead of returning NULL and
+   setting errno to ENOMEM, malloc incorrectly returns the previously
+   allocated block instead.  Bug 22343 reported a similar case where
+   posix_memalign incorrectly returns successfully when called with an with
+   a really large size argument.
+
+   Both of these were caused by integer overflows in the allocator when it
+   was trying to pad the requested size to allow for book-keeping or
+   alignment.  This test guards against such bugs by repeatedly allocating
+   and freeing small blocks of memory then trying to allocate various block
+   sizes larger than the memory bus width of 64-bit targets, or almost
+   as large as SIZE_MAX on 32-bit targets supported by glibc.  In each case,
+   it verifies that such impossibly large allocations correctly fail.  */
+
+
+#include <stdlib.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/resource.h>
+#include <libc-diag.h>
+#include <support/check.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+
+/* This function prepares for each 'too-large memory allocation' test by
+   performing a small successful malloc/free and resetting errno prior to
+   the actual test.  */
+static void
+test_setup (void)
+{
+  void *volatile ptr = malloc (16);
+  TEST_VERIFY_EXIT (ptr != NULL);
+  free (ptr);
+  errno = 0;
+}
+
+
+/* This function tests each of:
+   - malloc (SIZE)
+   - realloc (PTR_FOR_REALLOC, SIZE)
+   - for various values of NMEMB:
+    - calloc (NMEMB, SIZE/NMEMB)
+    - calloc (SIZE/NMEMB, NMEMB)
+    - reallocarray (PTR_FOR_REALLOC, NMEMB, SIZE/NMEMB)
+    - reallocarray (PTR_FOR_REALLOC, SIZE/NMEMB, NMEMB)
+   and precedes each of these tests with a small malloc/free before it.  */
+static void
+test_large_allocations (size_t size)
+{
+  void * ptr_to_realloc;
+
+  test_setup ();
+  TEST_VERIFY (malloc (size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+
+  ptr_to_realloc = malloc (16);
+  TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+  test_setup ();
+  TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+  free (ptr_to_realloc);
+
+  for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
+    if ((size % nmemb) == 0)
+      {
+        test_setup ();
+        TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
+        TEST_VERIFY (errno == ENOMEM);
+
+        test_setup ();
+        TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
+        TEST_VERIFY (errno == ENOMEM);
+
+        ptr_to_realloc = malloc (16);
+        TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+        test_setup ();
+        TEST_VERIFY (reallocarray (ptr_to_realloc, nmemb, size / nmemb) == NULL);
+        TEST_VERIFY (errno == ENOMEM);
+        free (ptr_to_realloc);
+
+        ptr_to_realloc = malloc (16);
+        TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+        test_setup ();
+        TEST_VERIFY (reallocarray (ptr_to_realloc, size / nmemb, nmemb) == NULL);
+        TEST_VERIFY (errno == ENOMEM);
+        free (ptr_to_realloc);
+      }
+    else
+      break;
+}
+
+
+static long pagesize;
+
+/* This function tests the following aligned memory allocation functions
+   using several valid alignments and precedes each allocation test with a
+   small malloc/free before it:
+   memalign, posix_memalign, aligned_alloc, valloc, pvalloc.  */
+static void
+test_large_aligned_allocations (size_t size)
+{
+  /* PTR stores the result of posix_memalign but since all those calls
+     should fail, posix_memalign should never touch PTR.  We set it to
+     NULL here and later on we check that it remains NULL after each
+     posix_memalign call.  */
+  void * ptr = NULL;
+
+  size_t align;
+
+  /* All aligned memory allocation functions expect an alignment that is a
+     power of 2.  Given this, we test each of them with every valid
+     alignment from 1 thru PAGESIZE.  */
+  for (align = 1; align <= pagesize; align *= 2)
+    {
+      test_setup ();
+      TEST_VERIFY (memalign (align, size) == NULL);
+      TEST_VERIFY (errno == ENOMEM);
+
+      /* posix_memalign expects an alignment that is a power of 2 *and* a
+         multiple of sizeof (void *).  */
+      if ((align % sizeof (void *)) == 0)
+        {
+          test_setup ();
+          TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
+          TEST_VERIFY (ptr == NULL);
+        }
+
+      /* aligned_alloc expects a size that is a multiple of alignment.  */
+      if ((size % align) == 0)
+        {
+          test_setup ();
+          TEST_VERIFY (aligned_alloc (align, size) == NULL);
+          TEST_VERIFY (errno == ENOMEM);
+        }
+    }
+
+  /* Both valloc and pvalloc return page-aligned memory.  */
+
+  test_setup ();
+  TEST_VERIFY (valloc (size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+
+  test_setup ();
+  TEST_VERIFY (pvalloc (size) == NULL);
+  TEST_VERIFY (errno == ENOMEM);
+}
+
+
+#define FOURTEEN_ON_BITS ((1UL << 14) - 1)
+#define FIFTY_ON_BITS ((1UL << 50) - 1)
+
+
+static int
+do_test (void)
+{
+
+#if __WORDSIZE >= 64
+
+  /* This test assumes that none of the supported targets have an address
+     bus wider than 50 bits, and that therefore allocations for sizes wider
+     than 50 bits will fail.  Here, we ensure that the assumption continues
+     to be true in the future when we might have address buses wider than 50
+     bits.  */
+
+  struct rlimit alloc_size_limit
+    = {
+        .rlim_cur = FIFTY_ON_BITS,
+        .rlim_max = FIFTY_ON_BITS
+      };
+
+  setrlimit (RLIMIT_AS, &alloc_size_limit);
+
+#endif /* __WORDSIZE >= 64 */
+
+  DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+  /* GCC 7 warns about too-large allocations; here we want to test
+     that they fail.  */
+  DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
+
+  /* Aligned memory allocation functions need to be tested up to alignment
+     size equivalent to page size, which should be a power of 2.  */
+  pagesize = sysconf (_SC_PAGESIZE);
+  TEST_VERIFY_EXIT (powerof2 (pagesize));
+
+  /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
+     in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
+
+     We can expect that this range of allocation sizes will always lead to
+     an allocation failure on both 64 and 32 bit targets, because:
+
+     1. no currently supported 64-bit target has an address bus wider than
+     50 bits -- and (2^64 - 2^14) is much wider than that;
+
+     2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
+     addressable, glibc itself is more than 2^14 bytes in size, and
+     therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
+     available.  */
+
+  for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
+    {
+      test_large_allocations (SIZE_MAX - i);
+      test_large_aligned_allocations (SIZE_MAX - i);
+    }
+
+#if __WORDSIZE >= 64
+  /* On 64-bit targets, we need to test a much wider range of too-large
+     sizes, so we test at intervals of (1 << 50) that allocation sizes
+     ranging from SIZE_MAX down to (1 << 50) fail:
+     The 14 MSBs are decremented starting from "all ON" going down to 1,
+     the 50 LSBs are "all ON" and then "all OFF" during every iteration.  */
+  for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
+    {
+      size_t size = (msbs << 50) | FIFTY_ON_BITS;
+      test_large_allocations (size);
+      test_large_aligned_allocations (size);
+
+      size = msbs << 50;
+      test_large_allocations (size);
+      test_large_aligned_allocations (size);
+    }
+#endif /* __WORDSIZE >= 64 */
+
+  DIAG_POP_NEEDS_COMMENT;
+
+  return 0;
+}
+
+
+#define TIMEOUT 5
+#include <support/test-driver.c>