(root)/
glibc-2.38/
malloc/
tst-dynarray-fail.c
       1  /* Test allocation failures with dynamic arrays.
       2     Copyright (C) 2017-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  /* This test is separate from tst-dynarray because it cannot run under
      20     valgrind.  */
      21  
      22  #include "tst-dynarray-shared.h"
      23  
      24  #include <mcheck.h>
      25  #include <stdio.h>
      26  #include <support/check.h>
      27  #include <support/support.h>
      28  #include <support/xunistd.h>
      29  #include <sys/mman.h>
      30  #include <sys/resource.h>
      31  #include <unistd.h>
      32  
      33  /* Data structure to fill up the heap.  */
      34  struct heap_filler
      35  {
      36    struct heap_filler *next;
      37  };
      38  
      39  /* Allocate objects until the heap is full.  */
      40  static struct heap_filler *
      41  fill_heap (void)
      42  {
      43    size_t pad = 4096;
      44    struct heap_filler *head = NULL;
      45    while (true)
      46      {
      47        struct heap_filler *new_head = malloc (sizeof (*new_head) + pad);
      48        if (new_head == NULL)
      49          {
      50            if (pad > 0)
      51              {
      52                /* Try again with smaller allocations.  */
      53                pad = 0;
      54                continue;
      55              }
      56            else
      57              break;
      58          }
      59        new_head->next = head;
      60        head = new_head;
      61      }
      62    return head;
      63  }
      64  
      65  /* Free the heap-filling allocations, so that we can continue testing
      66     and detect memory leaks elsewhere.  */
      67  static void
      68  free_fill_heap (struct heap_filler *head)
      69  {
      70    while (head != NULL)
      71      {
      72        struct heap_filler *next = head->next;
      73        free (head);
      74        head = next;
      75      }
      76  }
      77  
      78  /* Check allocation failures for int arrays (without an element free
      79     function).  */
      80  static void
      81  test_int_fail (void)
      82  {
      83    /* Exercise failure in add/emplace.
      84  
      85       do_add: Use emplace (false) or add (true) to add elements.
      86       do_finalize: Perform finalization at the end (instead of free).  */
      87    for (int do_add = 0; do_add < 2; ++do_add)
      88      for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
      89        {
      90          struct dynarray_int dyn;
      91          dynarray_int_init (&dyn);
      92          size_t count = 0;
      93          while (true)
      94            {
      95              if (do_add)
      96                {
      97                  dynarray_int_add (&dyn, 0);
      98                  if (dynarray_int_has_failed (&dyn))
      99                    break;
     100                }
     101              else
     102                {
     103                  int *place = dynarray_int_emplace (&dyn);
     104                  if (place == NULL)
     105                    break;
     106                  TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
     107                  *place = 0;
     108                }
     109              ++count;
     110            }
     111          printf ("info: %s: failure after %zu elements\n", __func__, count);
     112          TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn));
     113          if (do_finalize)
     114            {
     115              struct int_array result = { (int *) (uintptr_t) -1, -1 };
     116              TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
     117              TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
     118              TEST_VERIFY_EXIT (result.length == (size_t) -1);
     119            }
     120          else
     121            dynarray_int_free (&dyn);
     122          CHECK_INIT_STATE (int, &dyn);
     123        }
     124  
     125    /* Exercise failure in finalize.  */
     126    for (int do_add = 0; do_add < 2; ++do_add)
     127      {
     128        struct dynarray_int dyn;
     129        dynarray_int_init (&dyn);
     130        for (unsigned int i = 0; i < 10000; ++i)
     131          {
     132            if (do_add)
     133              {
     134                dynarray_int_add (&dyn, i);
     135                TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
     136              }
     137            else
     138              {
     139                int *place = dynarray_int_emplace (&dyn);
     140                TEST_VERIFY_EXIT (place != NULL);
     141                *place = i;
     142              }
     143          }
     144        TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
     145        struct heap_filler *heap_filler = fill_heap ();
     146        struct int_array result = { (int *) (uintptr_t) -1, -1 };
     147        TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
     148        TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
     149        TEST_VERIFY_EXIT (result.length == (size_t) -1);
     150        CHECK_INIT_STATE (int, &dyn);
     151        free_fill_heap (heap_filler);
     152      }
     153  
     154    /* Exercise failure in resize.  */
     155    {
     156      struct dynarray_int dyn;
     157      dynarray_int_init (&dyn);
     158      struct heap_filler *heap_filler = fill_heap ();
     159      TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
     160      TEST_VERIFY (dynarray_int_has_failed (&dyn));
     161      free_fill_heap (heap_filler);
     162  
     163      dynarray_int_init (&dyn);
     164      TEST_VERIFY (dynarray_int_resize (&dyn, 1));
     165      heap_filler = fill_heap ();
     166      TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
     167      TEST_VERIFY (dynarray_int_has_failed (&dyn));
     168      free_fill_heap (heap_filler);
     169  
     170      dynarray_int_init (&dyn);
     171      TEST_VERIFY (dynarray_int_resize (&dyn, 1000));
     172      heap_filler = fill_heap ();
     173      TEST_VERIFY (!dynarray_int_resize (&dyn, 2000));
     174      TEST_VERIFY (dynarray_int_has_failed (&dyn));
     175      free_fill_heap (heap_filler);
     176    }
     177  }
     178  
     179  /* Check allocation failures for char * arrays (which automatically
     180     free the pointed-to strings).  */
     181  static void
     182  test_str_fail (void)
     183  {
     184    /* Exercise failure in add/emplace.
     185  
     186       do_add: Use emplace (false) or add (true) to add elements.
     187       do_finalize: Perform finalization at the end (instead of free).  */
     188    for (int do_add = 0; do_add < 2; ++do_add)
     189      for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
     190        {
     191          struct dynarray_str dyn;
     192          dynarray_str_init (&dyn);
     193          size_t count = 0;
     194          while (true)
     195            {
     196              char **place;
     197              if (do_add)
     198                {
     199                  dynarray_str_add (&dyn, NULL);
     200                  if (dynarray_str_has_failed (&dyn))
     201                    break;
     202                  else
     203                    place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1);
     204                }
     205              else
     206                {
     207                  place = dynarray_str_emplace (&dyn);
     208                  if (place == NULL)
     209                    break;
     210                }
     211              TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
     212              TEST_VERIFY_EXIT (*place == NULL);
     213              *place = strdup ("placeholder");
     214              if (*place == NULL)
     215                {
     216                  /* Second loop to wait for failure of
     217                     dynarray_str_emplace.  */
     218                  while (true)
     219                    {
     220                      if (do_add)
     221                        {
     222                          dynarray_str_add (&dyn, NULL);
     223                          if (dynarray_str_has_failed (&dyn))
     224                            break;
     225                        }
     226                      else
     227                        {
     228                          char **place = dynarray_str_emplace (&dyn);
     229                          if (place == NULL)
     230                            break;
     231                          TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
     232                          *place = NULL;
     233                        }
     234                      ++count;
     235                    }
     236                  break;
     237                }
     238              ++count;
     239            }
     240          printf ("info: %s: failure after %zu elements\n", __func__, count);
     241          TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn));
     242          if (do_finalize)
     243            {
     244              struct str_array result = { (char **) (uintptr_t) -1, -1 };
     245              TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
     246              TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
     247              TEST_VERIFY_EXIT (result.length == (size_t) -1);
     248            }
     249          else
     250            dynarray_str_free (&dyn);
     251          TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
     252          TEST_VERIFY_EXIT (dyn.u.dynarray_header.array == dyn.scratch);
     253          TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
     254          TEST_VERIFY_EXIT (dyn.u.dynarray_header.allocated > 0);
     255        }
     256  
     257    /* Exercise failure in finalize.  */
     258    for (int do_add = 0; do_add < 2; ++do_add)
     259      {
     260        struct dynarray_str dyn;
     261        dynarray_str_init (&dyn);
     262        for (unsigned int i = 0; i < 1000; ++i)
     263          {
     264            if (do_add)
     265              dynarray_str_add (&dyn, xstrdup ("placeholder"));
     266            else
     267              {
     268                char **place = dynarray_str_emplace (&dyn);
     269                TEST_VERIFY_EXIT (place != NULL);
     270                TEST_VERIFY_EXIT (*place == NULL);
     271                *place = xstrdup ("placeholder");
     272              }
     273          }
     274        TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
     275        struct heap_filler *heap_filler = fill_heap ();
     276        struct str_array result = { (char **) (uintptr_t) -1, -1 };
     277        TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
     278        TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
     279        TEST_VERIFY_EXIT (result.length == (size_t) -1);
     280        TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
     281        TEST_VERIFY_EXIT (dyn.u.dynarray_header.array == dyn.scratch);
     282        TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
     283        TEST_VERIFY_EXIT (dyn.u.dynarray_header.allocated > 0);
     284        free_fill_heap (heap_filler);
     285      }
     286  
     287    /* Exercise failure in resize.  */
     288    {
     289      struct dynarray_str dyn;
     290      dynarray_str_init (&dyn);
     291      struct heap_filler *heap_filler = fill_heap ();
     292      TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
     293      TEST_VERIFY (dynarray_str_has_failed (&dyn));
     294      free_fill_heap (heap_filler);
     295  
     296      dynarray_str_init (&dyn);
     297      TEST_VERIFY (dynarray_str_resize (&dyn, 1));
     298      *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
     299      heap_filler = fill_heap ();
     300      TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
     301      TEST_VERIFY (dynarray_str_has_failed (&dyn));
     302      free_fill_heap (heap_filler);
     303  
     304      dynarray_str_init (&dyn);
     305      TEST_VERIFY (dynarray_str_resize (&dyn, 1000));
     306      *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
     307      heap_filler = fill_heap ();
     308      TEST_VERIFY (!dynarray_str_resize (&dyn, 2000));
     309      TEST_VERIFY (dynarray_str_has_failed (&dyn));
     310      free_fill_heap (heap_filler);
     311    }
     312  }
     313  
     314  /* Test if mmap can allocate a page.  This is necessary because
     315     setrlimit does not fail even if it reduces the RLIMIT_AS limit
     316     below what is currently needed by the process.  */
     317  static bool
     318  mmap_works (void)
     319  {
     320    void *ptr =  mmap (NULL, 1, PROT_READ | PROT_WRITE,
     321                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
     322    if (ptr == MAP_FAILED)
     323      return false;
     324    xmunmap (ptr, 1);
     325    return true;
     326  }
     327  
     328  /* Set the RLIMIT_AS limit to the value in *LIMIT.  */
     329  static void
     330  xsetrlimit_as (const struct rlimit *limit)
     331  {
     332    if (setrlimit (RLIMIT_AS, limit) != 0)
     333      FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m",
     334                  (unsigned long) limit->rlim_cur);
     335  }
     336  
     337  /* Approximately this many bytes can be allocated after
     338     reduce_rlimit_as has run.  */
     339  enum { as_limit_reserve = 2 * 1024 * 1024 };
     340  
     341  /* Limit the size of the process, so that memory allocation in
     342     allocate_thread will eventually fail, without impacting the entire
     343     system.  By default, a dynamic limit which leaves room for 2 MiB is
     344     activated.  The TEST_RLIMIT_AS environment variable overrides
     345     it.  */
     346  static void
     347  reduce_rlimit_as (void)
     348  {
     349    struct rlimit limit;
     350    if (getrlimit (RLIMIT_AS, &limit) != 0)
     351      FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
     352  
     353    /* Use the TEST_RLIMIT_AS setting if available.  */
     354    {
     355      long target = 0;
     356      const char *variable = "TEST_RLIMIT_AS";
     357      const char *target_str = getenv (variable);
     358      if (target_str != NULL)
     359        {
     360          target = atoi (target_str);
     361          if (target <= 0)
     362            FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str);
     363          printf ("info: setting RLIMIT_AS to %ld MiB\n", target);
     364          target *= 1024 * 1024;      /* Convert to megabytes.  */
     365          limit.rlim_cur = target;
     366          xsetrlimit_as (&limit);
     367          return;
     368        }
     369    }
     370  
     371    /* Otherwise, try to find the limit with a binary search.  */
     372    unsigned long low = 1 << 20;
     373    limit.rlim_cur = low;
     374    xsetrlimit_as (&limit);
     375  
     376    /* Find working upper limit.  */
     377    unsigned long high = 1 << 30;
     378    while (true)
     379      {
     380        limit.rlim_cur = high;
     381        xsetrlimit_as (&limit);
     382        if (mmap_works ())
     383          break;
     384        if (2 * high < high)
     385          FAIL_EXIT1 ("cannot find upper AS limit");
     386        high *= 2;
     387      }
     388  
     389    /* Perform binary search.  */
     390    while ((high - low) > 128 * 1024)
     391      {
     392        unsigned long middle = (low + high) / 2;
     393        limit.rlim_cur = middle;
     394        xsetrlimit_as (&limit);
     395        if (mmap_works ())
     396          high = middle;
     397        else
     398          low = middle;
     399      }
     400  
     401    unsigned long target = high + as_limit_reserve;
     402    limit.rlim_cur = target;
     403    xsetrlimit_as (&limit);
     404    printf ("info: RLIMIT_AS limit: %lu bytes\n", target);
     405  }
     406  
     407  static int
     408  do_test (void)
     409  {
     410    mtrace ();
     411    reduce_rlimit_as ();
     412    test_int_fail ();
     413    test_str_fail ();
     414    return 0;
     415  }
     416  
     417  #define TIMEOUT 90
     418  #include <support/test-driver.c>