Blame malloc/tst-malloc-too-large.c

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