(root)/
m4-1.4.19/
tests/
test-free.c
       1  /* Test of free() function.
       2     Copyright (C) 2020-2021 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation; either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2020.  */
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include <stdlib.h>
      23  
      24  #include <errno.h>
      25  #include <string.h>
      26  #include <unistd.h>
      27  #if defined __linux__
      28  # include <fcntl.h>
      29  # include <stdint.h>
      30  # include <string.h>
      31  # include <sys/mman.h>
      32  #endif
      33  
      34  #include "macros.h"
      35  
      36  /* The indirection through a volatile function pointer is necessary to prevent
      37     a GCC optimization.  Without it, when optimizing, GCC would "know" that errno
      38     is unchanged by calling free(ptr), when ptr was the result of a malloc(...)
      39     call in the same function.  */
      40  static int
      41  get_errno (void)
      42  {
      43    volatile int err = errno;
      44    return err;
      45  }
      46  
      47  static int (* volatile get_errno_func) (void) = get_errno;
      48  
      49  int
      50  main ()
      51  {
      52    /* Check that free() preserves errno.  */
      53    {
      54      errno = 1789; /* Liberté, égalité, fraternité.  */
      55      free (NULL);
      56      ASSERT_NO_STDIO (get_errno_func () == 1789);
      57    }
      58    { /* Small memory allocations.  */
      59      #define N 10000
      60      void * volatile ptrs[N];
      61      size_t i;
      62      for (i = 0; i < N; i++)
      63        ptrs[i] = malloc (15);
      64      for (i = 0; i < N; i++)
      65        {
      66          errno = 1789;
      67          free (ptrs[i]);
      68          ASSERT_NO_STDIO (get_errno_func () == 1789);
      69        }
      70      #undef N
      71    }
      72    { /* Medium memory allocations.  */
      73      #define N 1000
      74      void * volatile ptrs[N];
      75      size_t i;
      76      for (i = 0; i < N; i++)
      77        ptrs[i] = malloc (729);
      78      for (i = 0; i < N; i++)
      79        {
      80          errno = 1789;
      81          free (ptrs[i]);
      82          ASSERT_NO_STDIO (get_errno_func () == 1789);
      83        }
      84      #undef N
      85    }
      86    { /* Large memory allocations.  */
      87      #define N 10
      88      void * volatile ptrs[N];
      89      size_t i;
      90      for (i = 0; i < N; i++)
      91        ptrs[i] = malloc (5318153);
      92      for (i = 0; i < N; i++)
      93        {
      94          errno = 1789;
      95          free (ptrs[i]);
      96          ASSERT_NO_STDIO (get_errno_func () == 1789);
      97        }
      98      #undef N
      99    }
     100  
     101    /* Test a less common code path.
     102       When malloc() is based on mmap(), free() can sometimes call munmap().
     103       munmap() usually succeeds, but fails in a particular situation: when
     104         - it has to unmap the middle part of a VMA, and
     105         - the number of VMAs of a process is limited and the limit is
     106           already reached.
     107       The latter condition is fulfilled on Linux, when the file
     108       /proc/sys/vm/max_map_count exists.  This file contains the limit
     109         - for Linux >= 2.4.19: 65536 (DEFAULT_MAX_MAP_COUNT in linux/include/linux/sched.h)
     110         - for Linux >= 2.6.31: 65530 (DEFAULT_MAX_MAP_COUNT in linux/include/linux/mm.h).
     111       But do not test it with glibc < 2.15, since that triggers a glibc internal
     112       abort: "malloc.c:3551: munmap_chunk: Assertion `ret == 0' failed."
     113     */
     114    #if defined __linux__ && !(__GLIBC__ == 2 && __GLIBC_MINOR__ < 15)
     115    if (open ("/proc/sys/vm/max_map_count", O_RDONLY) >= 0)
     116      {
     117        /* Preparations.  */
     118        size_t pagesize = getpagesize ();
     119        void *firstpage_backup = malloc (pagesize);
     120        void *lastpage_backup = malloc (pagesize);
     121        /* Allocate a large memory area, as a bumper, so that the MAP_FIXED
     122           allocation later will not overwrite parts of the memory areas
     123           allocated to ld.so or libc.so.  */
     124        void *bumper_region =
     125          mmap (NULL, 0x1000000, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
     126        /* A file descriptor pointing to a regular file.  */
     127        int fd = open ("test-free", O_RDONLY);
     128  
     129        if (firstpage_backup != NULL && lastpage_backup != NULL
     130            && bumper_region != (void *)(-1)
     131            && fd >= 0)
     132          {
     133            /* Do a large memory allocation.  */
     134            size_t big_size = 0x1000000;
     135            void * volatile ptr = malloc (big_size - 0x100);
     136            char *ptr_aligned = (char *) ((uintptr_t) ptr & ~(pagesize - 1));
     137            /* This large memory allocation allocated a memory area
     138               from ptr_aligned to ptr_aligned + big_size.
     139               Enlarge this memory area by adding a page before and a page
     140               after it.  */
     141            memcpy (firstpage_backup, ptr_aligned, pagesize);
     142            memcpy (lastpage_backup, ptr_aligned + big_size - pagesize, pagesize);
     143            if (mmap (ptr_aligned - pagesize, pagesize + big_size + pagesize,
     144                      PROT_READ | PROT_WRITE,
     145                      MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)
     146                != (void *)(-1))
     147              {
     148                memcpy (ptr_aligned, firstpage_backup, pagesize);
     149                memcpy (ptr_aligned + big_size - pagesize, lastpage_backup, pagesize);
     150  
     151                /* Now add as many mappings as we can.
     152                   Stop at 65536, in order not to crash the machine (in case the
     153                   limit has been increased by the system administrator).  */
     154                size_t i;
     155                for (i = 0; i < 65536; i++)
     156                  if (mmap (NULL, pagesize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)
     157                      == (void *)(-1))
     158                    break;
     159                /* Now the number of VMAs of this process has hopefully attained
     160                   its limit.  */
     161  
     162                errno = 1789;
     163                /* This call to free() is supposed to call
     164                     munmap (ptr_aligned, big_size);
     165                   which increases the number of VMAs by 1, which is supposed
     166                   to fail.  */
     167                free (ptr);
     168                ASSERT_NO_STDIO (get_errno_func () == 1789);
     169              }
     170          }
     171      }
     172    #endif
     173  
     174    return 0;
     175  }