(root)/
glibc-2.38/
malloc/
tst-malloc-too-large.c
       1  /* Test and verify that too-large memory allocations fail with ENOMEM.
       2     Copyright (C) 2018-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Bug 22375 reported a regression in malloc where if after malloc'ing then
      20     free'ing a small block of memory, malloc is then called with a really
      21     large size argument (close to SIZE_MAX): instead of returning NULL and
      22     setting errno to ENOMEM, malloc incorrectly returns the previously
      23     allocated block instead.  Bug 22343 reported a similar case where
      24     posix_memalign incorrectly returns successfully when called with an with
      25     a really large size argument.
      26  
      27     Both of these were caused by integer overflows in the allocator when it
      28     was trying to pad the requested size to allow for book-keeping or
      29     alignment.  This test guards against such bugs by repeatedly allocating
      30     and freeing small blocks of memory then trying to allocate various block
      31     sizes larger than the memory bus width of 64-bit targets, or almost
      32     as large as SIZE_MAX on 32-bit targets supported by glibc.  In each case,
      33     it verifies that such impossibly large allocations correctly fail.  */
      34  
      35  
      36  #include <stdlib.h>
      37  #include <malloc.h>
      38  #include <errno.h>
      39  #include <stdint.h>
      40  #include <sys/resource.h>
      41  #include <libc-diag.h>
      42  #include <support/check.h>
      43  #include <unistd.h>
      44  #include <sys/param.h>
      45  
      46  
      47  /* This function prepares for each 'too-large memory allocation' test by
      48     performing a small successful malloc/free and resetting errno prior to
      49     the actual test.  */
      50  static void
      51  test_setup (void)
      52  {
      53    void *volatile ptr = malloc (16);
      54    TEST_VERIFY_EXIT (ptr != NULL);
      55    free (ptr);
      56    errno = 0;
      57  }
      58  
      59  
      60  /* This function tests each of:
      61     - malloc (SIZE)
      62     - realloc (PTR_FOR_REALLOC, SIZE)
      63     - for various values of NMEMB:
      64      - calloc (NMEMB, SIZE/NMEMB)
      65      - calloc (SIZE/NMEMB, NMEMB)
      66      - reallocarray (PTR_FOR_REALLOC, NMEMB, SIZE/NMEMB)
      67      - reallocarray (PTR_FOR_REALLOC, SIZE/NMEMB, NMEMB)
      68     and precedes each of these tests with a small malloc/free before it.  */
      69  static void
      70  test_large_allocations (size_t size)
      71  {
      72    void * ptr_to_realloc;
      73  
      74    test_setup ();
      75    DIAG_PUSH_NEEDS_COMMENT;
      76  #if __GNUC_PREREQ (7, 0)
      77    /* GCC 7 warns about too-large allocations; here we want to test
      78       that they fail.  */
      79    DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
      80  #endif
      81    TEST_VERIFY (malloc (size) == NULL);
      82  #if __GNUC_PREREQ (7, 0)
      83    DIAG_POP_NEEDS_COMMENT;
      84  #endif
      85    TEST_VERIFY (errno == ENOMEM);
      86  
      87    ptr_to_realloc = malloc (16);
      88    TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
      89    test_setup ();
      90  #if __GNUC_PREREQ (7, 0)
      91    DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
      92  #endif
      93    TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
      94  #if __GNUC_PREREQ (7, 0)
      95    DIAG_POP_NEEDS_COMMENT;
      96  #endif
      97    TEST_VERIFY (errno == ENOMEM);
      98  #if __GNUC_PREREQ (12, 0)
      99    /* Ignore a warning about using a pointer made indeterminate by
     100       a prior call to realloc().  */
     101    DIAG_IGNORE_NEEDS_COMMENT (12, "-Wuse-after-free");
     102  #endif
     103    free (ptr_to_realloc);
     104  #if __GNUC_PREREQ (12, 0)
     105    DIAG_POP_NEEDS_COMMENT;
     106  #endif
     107  
     108    for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
     109      if ((size % nmemb) == 0)
     110        {
     111          test_setup ();
     112          TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
     113          TEST_VERIFY (errno == ENOMEM);
     114  
     115          test_setup ();
     116          TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
     117          TEST_VERIFY (errno == ENOMEM);
     118  
     119          ptr_to_realloc = malloc (16);
     120          TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
     121          test_setup ();
     122          TEST_VERIFY (reallocarray (ptr_to_realloc, nmemb, size / nmemb) == NULL);
     123          TEST_VERIFY (errno == ENOMEM);
     124  #if __GNUC_PREREQ (12, 0)
     125    /* Ignore a warning about using a pointer made indeterminate by
     126       a prior call to realloc().  */
     127    DIAG_IGNORE_NEEDS_COMMENT (12, "-Wuse-after-free");
     128  #endif
     129          free (ptr_to_realloc);
     130  #if __GNUC_PREREQ (12, 0)
     131    DIAG_POP_NEEDS_COMMENT;
     132  #endif
     133  
     134          ptr_to_realloc = malloc (16);
     135          TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
     136          test_setup ();
     137          TEST_VERIFY (reallocarray (ptr_to_realloc, size / nmemb, nmemb) == NULL);
     138          TEST_VERIFY (errno == ENOMEM);
     139  #if __GNUC_PREREQ (12, 0)
     140    /* Ignore a warning about using a pointer made indeterminate by
     141       a prior call to realloc().  */
     142    DIAG_IGNORE_NEEDS_COMMENT (12, "-Wuse-after-free");
     143  #endif
     144          free (ptr_to_realloc);
     145  #if __GNUC_PREREQ (12, 0)
     146    DIAG_POP_NEEDS_COMMENT;
     147  #endif
     148        }
     149      else
     150        break;
     151  }
     152  
     153  
     154  static long pagesize;
     155  
     156  /* This function tests the following aligned memory allocation functions
     157     using several valid alignments and precedes each allocation test with a
     158     small malloc/free before it:
     159     memalign, posix_memalign, aligned_alloc, valloc, pvalloc.  */
     160  static void
     161  test_large_aligned_allocations (size_t size)
     162  {
     163    /* ptr stores the result of posix_memalign but since all those calls
     164       should fail, posix_memalign should never change ptr.  We set it to
     165       NULL here and later on we check that it remains NULL after each
     166       posix_memalign call.  */
     167    void * ptr = NULL;
     168  
     169    size_t align;
     170  
     171    /* All aligned memory allocation functions expect an alignment that is a
     172       power of 2.  Given this, we test each of them with every valid
     173       alignment from 1 thru PAGESIZE.  */
     174    for (align = 1; align <= pagesize; align *= 2)
     175      {
     176        test_setup ();
     177  #if __GNUC_PREREQ (7, 0)
     178        DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
     179  #endif
     180        TEST_VERIFY (memalign (align, size) == NULL);
     181  #if __GNUC_PREREQ (7, 0)
     182        DIAG_POP_NEEDS_COMMENT;
     183  #endif
     184        TEST_VERIFY (errno == ENOMEM);
     185  
     186        /* posix_memalign expects an alignment that is a power of 2 *and* a
     187           multiple of sizeof (void *).  */
     188        if ((align % sizeof (void *)) == 0)
     189          {
     190            test_setup ();
     191            TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
     192            TEST_VERIFY (ptr == NULL);
     193          }
     194  
     195        /* aligned_alloc expects a size that is a multiple of alignment.  */
     196        if ((size % align) == 0)
     197          {
     198            test_setup ();
     199  #if __GNUC_PREREQ (7, 0)
     200  	  DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
     201  #endif
     202            TEST_VERIFY (aligned_alloc (align, size) == NULL);
     203  #if __GNUC_PREREQ (7, 0)
     204  	  DIAG_POP_NEEDS_COMMENT;
     205  #endif
     206            TEST_VERIFY (errno == ENOMEM);
     207          }
     208      }
     209  
     210    /* Both valloc and pvalloc return page-aligned memory.  */
     211  
     212    test_setup ();
     213  #if __GNUC_PREREQ (7, 0)
     214    DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
     215  #endif
     216    TEST_VERIFY (valloc (size) == NULL);
     217  #if __GNUC_PREREQ (7, 0)
     218    DIAG_POP_NEEDS_COMMENT;
     219  #endif
     220    TEST_VERIFY (errno == ENOMEM);
     221  
     222    test_setup ();
     223  #if __GNUC_PREREQ (7, 0)
     224    DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
     225  #endif
     226    TEST_VERIFY (pvalloc (size) == NULL);
     227  #if __GNUC_PREREQ (7, 0)
     228    DIAG_POP_NEEDS_COMMENT;
     229  #endif
     230    TEST_VERIFY (errno == ENOMEM);
     231  }
     232  
     233  
     234  #define FOURTEEN_ON_BITS ((1UL << 14) - 1)
     235  #define FIFTY_ON_BITS ((1UL << 50) - 1)
     236  
     237  
     238  static int
     239  do_test (void)
     240  {
     241  
     242  #if __WORDSIZE >= 64
     243  
     244    /* This test assumes that none of the supported targets have an address
     245       bus wider than 50 bits, and that therefore allocations for sizes wider
     246       than 50 bits will fail.  Here, we ensure that the assumption continues
     247       to be true in the future when we might have address buses wider than 50
     248       bits.  */
     249  
     250    struct rlimit alloc_size_limit
     251      = {
     252          .rlim_cur = FIFTY_ON_BITS,
     253          .rlim_max = FIFTY_ON_BITS
     254        };
     255  
     256    setrlimit (RLIMIT_AS, &alloc_size_limit);
     257  
     258  #endif /* __WORDSIZE >= 64 */
     259  
     260    DIAG_PUSH_NEEDS_COMMENT;
     261  #if __GNUC_PREREQ (7, 0)
     262    /* GCC 7 warns about too-large allocations; here we want to test
     263       that they fail.  */
     264    DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
     265  #endif
     266  
     267    /* Aligned memory allocation functions need to be tested up to alignment
     268       size equivalent to page size, which should be a power of 2.  */
     269    pagesize = sysconf (_SC_PAGESIZE);
     270    TEST_VERIFY_EXIT (powerof2 (pagesize));
     271  
     272    /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
     273       in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
     274  
     275       We can expect that this range of allocation sizes will always lead to
     276       an allocation failure on both 64 and 32 bit targets, because:
     277  
     278       1. no currently supported 64-bit target has an address bus wider than
     279       50 bits -- and (2^64 - 2^14) is much wider than that;
     280  
     281       2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
     282       addressable, glibc itself is more than 2^14 bytes in size, and
     283       therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
     284       available.  */
     285  
     286    for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
     287      {
     288        test_large_allocations (SIZE_MAX - i);
     289        test_large_aligned_allocations (SIZE_MAX - i);
     290      }
     291  
     292    /* Allocation larger than PTRDIFF_MAX does play well with C standard,
     293       since pointer subtraction within the object might overflow ptrdiff_t
     294       resulting in undefined behavior.  To prevent it malloc function fail
     295       for such allocations.  */
     296    for (size_t i = 1; i <= FOURTEEN_ON_BITS; i++)
     297      {
     298        test_large_allocations (PTRDIFF_MAX + i);
     299        test_large_aligned_allocations (PTRDIFF_MAX + i);
     300      }
     301  
     302  #if __WORDSIZE >= 64
     303    /* On 64-bit targets, we need to test a much wider range of too-large
     304       sizes, so we test at intervals of (1 << 50) that allocation sizes
     305       ranging from SIZE_MAX down to (1 << 50) fail:
     306       The 14 MSBs are decremented starting from "all ON" going down to 1,
     307       the 50 LSBs are "all ON" and then "all OFF" during every iteration.  */
     308    for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
     309      {
     310        size_t size = (msbs << 50) | FIFTY_ON_BITS;
     311        test_large_allocations (size);
     312        test_large_aligned_allocations (size);
     313  
     314        size = msbs << 50;
     315        test_large_allocations (size);
     316        test_large_aligned_allocations (size);
     317      }
     318  #endif /* __WORDSIZE >= 64 */
     319  
     320    DIAG_POP_NEEDS_COMMENT;
     321  
     322    return 0;
     323  }
     324  
     325  
     326  #include <support/test-driver.c>