(root)/
glibc-2.38/
malloc/
tst-malloc-thread-fail.c
       1  /* Test allocation function behavior on allocation failure.
       2     Copyright (C) 2015-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 License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     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; see the file COPYING.LIB.  If
      17     not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  /* This test case attempts to trigger various unusual conditions
      20     related to allocation failures, notably switching to a different
      21     arena, and falling back to mmap (via sysmalloc).  */
      22  
      23  #include <errno.h>
      24  #include <malloc.h>
      25  #include <pthread.h>
      26  #include <stdbool.h>
      27  #include <stdint.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <sys/resource.h>
      31  #include <sys/wait.h>
      32  #include <unistd.h>
      33  
      34  /* Wrapper for calloc with an optimization barrier.  */
      35  static void *
      36  __attribute__ ((noinline, noclone))
      37  allocate_zeroed (size_t a, size_t b)
      38  {
      39    return calloc (a, b);
      40  }
      41  
      42  /* System page size, as determined by sysconf (_SC_PAGE_SIZE).  */
      43  static unsigned long page_size;
      44  
      45  /* Test parameters. */
      46  static size_t allocation_size;
      47  static size_t alignment;
      48  static enum {
      49    with_malloc,
      50    with_realloc,
      51    with_aligned_alloc,
      52    with_memalign,
      53    with_posix_memalign,
      54    with_valloc,
      55    with_pvalloc,
      56    with_calloc,
      57    last_allocation_function = with_calloc
      58  } allocation_function;
      59  
      60  /* True if an allocation function uses the alignment test
      61     parameter.  */
      62  const static bool alignment_sensitive[last_allocation_function + 1] =
      63    {
      64      [with_aligned_alloc] = true,
      65      [with_memalign] = true,
      66      [with_posix_memalign] = true,
      67    };
      68  
      69  /* Combined pointer/expected alignment result of an allocation
      70     function.  */
      71  struct allocate_result {
      72    void *pointer;
      73    size_t alignment;
      74  };
      75  
      76  /* Call the allocation function specified by allocation_function, with
      77     allocation_size and alignment (if applicable) as arguments.  No
      78     alignment check.  */
      79  static struct allocate_result
      80  allocate_1 (void)
      81  {
      82    switch (allocation_function)
      83      {
      84      case with_malloc:
      85        return (struct allocate_result)
      86          {malloc (allocation_size), _Alignof (max_align_t)};
      87      case with_realloc:
      88        {
      89          void *p = realloc (NULL, 16);
      90          void *q;
      91          if (p == NULL)
      92            q = NULL;
      93          else
      94            {
      95              q = realloc (p, allocation_size);
      96              if (q == NULL)
      97                free (p);
      98            }
      99          return (struct allocate_result) {q, _Alignof (max_align_t)};
     100        }
     101      case with_aligned_alloc:
     102        {
     103          void *p = aligned_alloc (alignment, allocation_size);
     104          return (struct allocate_result) {p, alignment};
     105        }
     106      case with_memalign:
     107        {
     108          void *p = memalign (alignment, allocation_size);
     109          return (struct allocate_result) {p, alignment};
     110        }
     111      case with_posix_memalign:
     112        {
     113          void *p;
     114          if (posix_memalign (&p, alignment, allocation_size))
     115            {
     116              if (errno == ENOMEM)
     117                p = NULL;
     118              else
     119                {
     120                  printf ("error: posix_memalign (p, %zu, %zu): %m\n",
     121                          alignment, allocation_size);
     122                  abort ();
     123                }
     124            }
     125          return (struct allocate_result) {p, alignment};
     126        }
     127      case with_valloc:
     128        {
     129          void *p = valloc (allocation_size);
     130          return (struct allocate_result) {p, page_size};
     131        }
     132      case with_pvalloc:
     133        {
     134          void *p = pvalloc (allocation_size);
     135          return (struct allocate_result) {p, page_size};
     136        }
     137      case with_calloc:
     138        {
     139          char *p = allocate_zeroed (1, allocation_size);
     140          /* Check for non-zero bytes.  */
     141          if (p != NULL)
     142            for (size_t i = 0; i < allocation_size; ++i)
     143              if (p[i] != 0)
     144                {
     145                  printf ("error: non-zero byte at offset %zu\n", i);
     146                  abort ();
     147                }
     148          return (struct allocate_result) {p, _Alignof (max_align_t)};
     149        }
     150      }
     151    abort ();
     152  }
     153  
     154  /* Call allocate_1 and perform the alignment check on the result.  */
     155  static void *
     156  allocate (void)
     157  {
     158    struct allocate_result r = allocate_1 ();
     159    if ((((uintptr_t) r.pointer) & (r.alignment - 1)) != 0)
     160      {
     161        printf ("error: allocation function %d, size %zu not aligned to %zu\n",
     162                (int) allocation_function, allocation_size, r.alignment);
     163        abort ();
     164      }
     165    return r.pointer;
     166  }
     167  
     168  /* Barriers to synchronize thread creation and termination.  */
     169  static pthread_barrier_t start_barrier;
     170  static pthread_barrier_t end_barrier;
     171  
     172  /* Thread function which performs the allocation test.  Called by
     173     pthread_create and from the main thread.  */
     174  static void *
     175  allocate_thread (void *closure)
     176  {
     177    /* Wait for the creation of all threads.  */
     178    {
     179      int ret = pthread_barrier_wait (&start_barrier);
     180      if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
     181        {
     182          errno = ret;
     183          printf ("error: pthread_barrier_wait: %m\n");
     184          abort ();
     185        }
     186    }
     187  
     188    /* Allocate until we run out of memory, creating a single-linked
     189       list.  */
     190    struct list {
     191      struct list *next;
     192    };
     193    struct list *head = NULL;
     194    while (true)
     195      {
     196        struct list *e = allocate ();
     197        if (e == NULL)
     198          break;
     199  
     200        e->next = head;
     201        head = e;
     202      }
     203  
     204    /* Wait for the allocation of all available memory.  */
     205    {
     206      int ret = pthread_barrier_wait (&end_barrier);
     207      if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
     208        {
     209          errno = ret;
     210          printf ("error: pthread_barrier_wait: %m\n");
     211          abort ();
     212        }
     213    }
     214  
     215    /* Free the allocated memory.  */
     216    while (head != NULL)
     217      {
     218        struct list *next = head->next;
     219        free (head);
     220        head = next;
     221      }
     222  
     223    return NULL;
     224  }
     225  
     226  /* Number of threads (plus the main thread.  */
     227  enum { thread_count = 8 };
     228  
     229  /* Thread attribute to request creation of threads with a non-default
     230     stack size which is rather small.  This avoids interfering with the
     231     configured address space limit.  */
     232  static pthread_attr_t small_stack;
     233  
     234  /* Runs one test in multiple threads, all in a subprocess so that
     235     subsequent tests do not interfere with each other.  */
     236  static void
     237  run_one (void)
     238  {
     239    /* Isolate the tests in a subprocess, so that we can start over
     240       from scratch.  */
     241    pid_t pid = fork ();
     242    if (pid == 0)
     243      {
     244        /* In the child process.  Create the allocation threads.  */
     245        pthread_t threads[thread_count];
     246  
     247        for (unsigned i = 0; i < thread_count; ++i)
     248          {
     249            int ret = pthread_create (threads + i, &small_stack, allocate_thread, NULL);
     250            if (ret != 0)
     251              {
     252                errno = ret;
     253                printf ("error: pthread_create: %m\n");
     254                abort ();
     255              }
     256          }
     257  
     258        /* Also run the test on the main thread.  */
     259        allocate_thread (NULL);
     260  
     261        for (unsigned i = 0; i < thread_count; ++i)
     262          {
     263            int ret = pthread_join (threads[i], NULL);
     264            if (ret != 0)
     265              {
     266                errno = ret;
     267                printf ("error: pthread_join: %m\n");
     268                abort ();
     269              }
     270          }
     271        _exit (0);
     272      }
     273    else if (pid < 0)
     274      {
     275        printf ("error: fork: %m\n");
     276        abort ();
     277      }
     278  
     279    /* In the parent process.  Wait for the child process to exit.  */
     280    int status;
     281    if (waitpid (pid, &status, 0) < 0)
     282      {
     283        printf ("error: waitpid: %m\n");
     284        abort ();
     285      }
     286    if (status != 0)
     287      {
     288        printf ("error: exit status %d from child process\n", status);
     289        exit (1);
     290      }
     291  }
     292  
     293  /* Run all applicable allocation functions for the current test
     294     parameters.  */
     295  static void
     296  run_allocation_functions (void)
     297  {
     298    for (int af = 0; af <= last_allocation_function; ++af)
     299      {
     300        /* Run alignment-sensitive functions for non-default
     301           alignments.  */
     302        if (alignment_sensitive[af] != (alignment != 0))
     303          continue;
     304        allocation_function = af;
     305        run_one ();
     306      }
     307  }
     308  
     309  int
     310  do_test (void)
     311  {
     312    /* Limit the number of malloc arenas.  We use a very low number so
     313       that despute the address space limit configured below, all
     314       requested arenas a can be created.  */
     315    if (mallopt (M_ARENA_MAX, 2) == 0)
     316      {
     317        printf ("error: mallopt (M_ARENA_MAX) failed\n");
     318        return 1;
     319      }
     320  
     321    /* Determine the page size.  */
     322    {
     323      long ret = sysconf (_SC_PAGE_SIZE);
     324      if (ret < 0)
     325        {
     326          printf ("error: sysconf (_SC_PAGE_SIZE): %m\n");
     327          return 1;
     328        }
     329      page_size = ret;
     330    }
     331  
     332    /* Limit the size of the process, so that memory allocation in
     333       allocate_thread will eventually fail, without impacting the
     334       entire system.  */
     335    {
     336      struct rlimit limit;
     337      if (getrlimit (RLIMIT_AS, &limit) != 0)
     338        {
     339          printf ("getrlimit (RLIMIT_AS) failed: %m\n");
     340          return 1;
     341        }
     342      long target = 200 * 1024 * 1024;
     343      if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
     344        {
     345          limit.rlim_cur = target;
     346          if (setrlimit (RLIMIT_AS, &limit) != 0)
     347            {
     348              printf ("setrlimit (RLIMIT_AS) failed: %m\n");
     349              return 1;
     350            }
     351        }
     352    }
     353  
     354    /* Initialize thread attribute with a reduced stack size.  */
     355    {
     356      int ret = pthread_attr_init (&small_stack);
     357      if (ret != 0)
     358        {
     359          errno = ret;
     360          printf ("error: pthread_attr_init: %m\n");
     361          abort ();
     362        }
     363      unsigned long stack_size = ((256 * 1024) / page_size) * page_size;
     364      if (stack_size < 4 * page_size)
     365        stack_size = 8 * page_size;
     366      ret = pthread_attr_setstacksize (&small_stack, stack_size);
     367      if (ret != 0)
     368        {
     369          errno = ret;
     370          printf ("error: pthread_attr_setstacksize: %m\n");
     371          abort ();
     372        }
     373    }
     374  
     375    /* Initialize the barriers.  We run thread_count threads, plus 1 for
     376       the main thread.  */
     377    {
     378      int ret = pthread_barrier_init (&start_barrier, NULL, thread_count + 1);
     379      if (ret != 0)
     380        {
     381          errno = ret;
     382          printf ("error: pthread_barrier_init: %m\n");
     383          abort ();
     384        }
     385  
     386      ret = pthread_barrier_init (&end_barrier, NULL, thread_count + 1);
     387      if (ret != 0)
     388        {
     389          errno = ret;
     390          printf ("error: pthread_barrier_init: %m\n");
     391          abort ();
     392        }
     393    }
     394  
     395    allocation_size = 144;
     396    run_allocation_functions ();
     397    allocation_size = page_size;
     398    run_allocation_functions ();
     399  
     400    alignment = 128;
     401    allocation_size = 512;
     402    run_allocation_functions ();
     403  
     404    allocation_size = page_size;
     405    run_allocation_functions ();
     406  
     407    allocation_size = 17 * page_size;
     408    run_allocation_functions ();
     409  
     410    /* Deallocation the barriers and the thread attribute.  */
     411    {
     412      int ret = pthread_barrier_destroy (&end_barrier);
     413      if (ret != 0)
     414        {
     415          errno = ret;
     416          printf ("error: pthread_barrier_destroy: %m\n");
     417          return 1;
     418        }
     419      ret = pthread_barrier_destroy (&start_barrier);
     420      if (ret != 0)
     421        {
     422          errno = ret;
     423          printf ("error: pthread_barrier_destroy: %m\n");
     424          return 1;
     425        }
     426      ret = pthread_attr_destroy (&small_stack);
     427      if (ret != 0)
     428        {
     429          errno = ret;
     430          printf ("error: pthread_attr_destroy: %m\n");
     431          return 1;
     432        }
     433    }
     434  
     435    return 0;
     436  }
     437  
     438  /* The repeated allocations take some time on slow machines.  */
     439  #define TIMEOUT 100
     440  
     441  #define TEST_FUNCTION do_test ()
     442  #include "../test-skeleton.c"