Blame SOURCES/glibc-rh1548002.patch

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