maint.mk: Update system header list for #include syntax checks.
[gnulib.git] / tests / test-free.c
blob70e481b59923310ff8566cb7c34d1d254ad0351b
1 /* Test of free() function.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2020. */
19 #include <config.h>
21 /* Specification. */
22 #include <stdlib.h>
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
34 #include "macros.h"
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)
43 volatile int err = errno;
44 return err;
47 static int (* volatile get_errno_func) (void) = get_errno;
49 int
50 main ()
52 /* Check that free() preserves errno. */
54 errno = 1789; /* Liberté, égalité, fraternité. */
55 free (NULL);
56 ASSERT_NO_STDIO (get_errno_func () == 1789);
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++)
66 errno = 1789;
67 free (ptrs[i]);
68 ASSERT_NO_STDIO (get_errno_func () == 1789);
70 #undef N
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++)
80 errno = 1789;
81 free (ptrs[i]);
82 ASSERT_NO_STDIO (get_errno_func () == 1789);
84 #undef N
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++)
94 errno = 1789;
95 free (ptrs[i]);
96 ASSERT_NO_STDIO (get_errno_func () == 1789);
98 #undef N
101 /* Skip this test when an address sanitizer is in use, because it would report
102 a "heap buffer overflow". */
103 #ifndef __has_feature
104 #define __has_feature(a) 0
105 #endif
106 #if !(defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer))
107 /* Test a less common code path.
108 When malloc() is based on mmap(), free() can sometimes call munmap().
109 munmap() usually succeeds, but fails in a particular situation: when
110 - it has to unmap the middle part of a VMA, and
111 - the number of VMAs of a process is limited and the limit is
112 already reached.
113 The latter condition is fulfilled on Linux, when the file
114 /proc/sys/vm/max_map_count exists. This file contains the limit
115 - for Linux >= 2.4.19: 65536 (DEFAULT_MAX_MAP_COUNT in linux/include/linux/sched.h)
116 - for Linux >= 2.6.31: 65530 (DEFAULT_MAX_MAP_COUNT in linux/include/linux/mm.h).
117 But do not test it with glibc < 2.15, since that triggers a glibc internal
118 abort: "malloc.c:3551: munmap_chunk: Assertion `ret == 0' failed."
120 #if defined __linux__ && !(__GLIBC__ == 2 && __GLIBC_MINOR__ < 15)
121 if (open ("/proc/sys/vm/max_map_count", O_RDONLY) >= 0)
123 /* Preparations. */
124 size_t pagesize = getpagesize ();
125 void *firstpage_backup = malloc (pagesize);
126 void *lastpage_backup = malloc (pagesize);
127 /* Allocate a large memory area, as a bumper, so that the MAP_FIXED
128 allocation later will not overwrite parts of the memory areas
129 allocated to ld.so or libc.so. */
130 void *bumper_region =
131 mmap (NULL, 0x1000000, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
132 /* A file descriptor pointing to a regular file. */
133 int fd = open ("test-free", O_RDONLY);
135 if (firstpage_backup != NULL && lastpage_backup != NULL
136 && bumper_region != (void *)(-1)
137 && fd >= 0)
139 /* Do a large memory allocation. */
140 size_t big_size = 0x1000000;
141 void * volatile ptr = malloc (big_size - 0x100);
142 char *ptr_aligned = (char *) ((uintptr_t) ptr & ~(pagesize - 1));
143 /* This large memory allocation allocated a memory area
144 from ptr_aligned to ptr_aligned + big_size.
145 Enlarge this memory area by adding a page before and a page
146 after it. */
147 memcpy (firstpage_backup, ptr_aligned, pagesize);
148 memcpy (lastpage_backup, ptr_aligned + big_size - pagesize, pagesize);
149 if (mmap (ptr_aligned - pagesize, pagesize + big_size + pagesize,
150 PROT_READ | PROT_WRITE,
151 MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)
152 != (void *)(-1))
154 memcpy (ptr_aligned, firstpage_backup, pagesize);
155 memcpy (ptr_aligned + big_size - pagesize, lastpage_backup, pagesize);
157 /* Now add as many mappings as we can.
158 Stop at 65536, in order not to crash the machine (in case the
159 limit has been increased by the system administrator). */
160 size_t i;
161 for (i = 0; i < 65536; i++)
162 if (mmap (NULL, pagesize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)
163 == (void *)(-1))
164 break;
165 /* Now the number of VMAs of this process has hopefully attained
166 its limit. */
168 errno = 1789;
169 /* This call to free() is supposed to call
170 munmap (ptr_aligned, big_size);
171 which increases the number of VMAs by 1, which is supposed
172 to fail. */
173 free (ptr);
174 ASSERT_NO_STDIO (get_errno_func () == 1789);
178 #endif
179 #endif
181 return test_exit_status;