gettext: Sync with gettext 0.23.
[gnulib.git] / tests / test-free.c
blobbc972ac04b7ee86119065f0e3a116f81cf936f80
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 void (*volatile my_free) (void *) = free;
41 #undef free
42 #define free my_free
44 int
45 main ()
47 /* Check that free() preserves errno. */
49 errno = 1789; /* Liberté, égalité, fraternité. */
50 free (NULL);
51 ASSERT_NO_STDIO (errno == 1789);
53 { /* Small memory allocations. */
54 #define N 10000
55 void *ptrs[N];
56 size_t i;
57 for (i = 0; i < N; i++)
58 ptrs[i] = malloc (15);
59 for (i = 0; i < N; i++)
61 errno = 1789;
62 free (ptrs[i]);
63 ASSERT_NO_STDIO (errno == 1789);
65 #undef N
67 { /* Medium memory allocations. */
68 #define N 1000
69 void *ptrs[N];
70 size_t i;
71 for (i = 0; i < N; i++)
72 ptrs[i] = malloc (729);
73 for (i = 0; i < N; i++)
75 errno = 1789;
76 free (ptrs[i]);
77 ASSERT_NO_STDIO (errno == 1789);
79 #undef N
81 { /* Large memory allocations. */
82 #define N 10
83 void *ptrs[N];
84 size_t i;
85 for (i = 0; i < N; i++)
86 ptrs[i] = malloc (5318153);
87 for (i = 0; i < N; i++)
89 errno = 1789;
90 free (ptrs[i]);
91 ASSERT_NO_STDIO (errno == 1789);
93 #undef N
96 /* Skip this test when an address sanitizer is in use, because it would report
97 a "heap buffer overflow". */
98 #ifndef __has_feature
99 #define __has_feature(a) 0
100 #endif
101 #if !(defined __SANITIZE_ADDRESS__ || __has_feature (address_sanitizer))
102 /* Test a less common code path.
103 When malloc() is based on mmap(), free() can sometimes call munmap().
104 munmap() usually succeeds, but fails in a particular situation: when
105 - it has to unmap the middle part of a VMA, and
106 - the number of VMAs of a process is limited and the limit is
107 already reached.
108 The latter condition is fulfilled on Linux, when the file
109 /proc/sys/vm/max_map_count exists. This file contains the limit
110 - for Linux >= 2.4.19: 65536 (DEFAULT_MAX_MAP_COUNT in linux/include/linux/sched.h)
111 - for Linux >= 2.6.31: 65530 (DEFAULT_MAX_MAP_COUNT in linux/include/linux/mm.h).
112 But do not test it with glibc < 2.15, since that triggers a glibc internal
113 abort: "malloc.c:3551: munmap_chunk: Assertion `ret == 0' failed."
115 #if defined __linux__ && !(__GLIBC__ == 2 && __GLIBC_MINOR__ < 15)
116 if (open ("/proc/sys/vm/max_map_count", O_RDONLY) >= 0)
118 /* Preparations. */
119 size_t pagesize = sysconf (_SC_PAGESIZE);
120 void *firstpage_backup = malloc (pagesize);
121 void *lastpage_backup = malloc (pagesize);
122 /* Allocate a large memory area, as a bumper, so that the MAP_FIXED
123 allocation later will not overwrite parts of the memory areas
124 allocated to ld.so or libc.so. */
125 void *bumper_region =
126 mmap (NULL, 0x1000000, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
127 /* A file descriptor pointing to a regular file. */
128 int fd = open ("test-free", O_RDONLY);
130 if (firstpage_backup != NULL && lastpage_backup != NULL
131 && bumper_region != (void *)(-1)
132 && fd >= 0)
134 /* Do a large memory allocation. */
135 size_t big_size = 0x1000000;
136 void *ptr = malloc (big_size - 0x100);
137 char *ptr_aligned = (char *) ((uintptr_t) ptr & ~(pagesize - 1));
138 /* This large memory allocation allocated a memory area
139 from ptr_aligned to ptr_aligned + big_size.
140 Enlarge this memory area by adding a page before and a page
141 after it. */
142 memcpy (firstpage_backup, ptr_aligned, pagesize);
143 memcpy (lastpage_backup, ptr_aligned + big_size - pagesize, pagesize);
144 if (mmap (ptr_aligned - pagesize, pagesize + big_size + pagesize,
145 PROT_READ | PROT_WRITE,
146 MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)
147 != (void *)(-1))
149 memcpy (ptr_aligned, firstpage_backup, pagesize);
150 memcpy (ptr_aligned + big_size - pagesize, lastpage_backup, pagesize);
152 /* Now add as many mappings as we can.
153 Stop at 65536, in order not to crash the machine (in case the
154 limit has been increased by the system administrator). */
155 size_t i;
156 for (i = 0; i < 65536; i++)
157 if (mmap (NULL, pagesize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)
158 == (void *)(-1))
159 break;
160 /* Now the number of VMAs of this process has hopefully attained
161 its limit. */
163 errno = 1789;
164 /* This call to free() is supposed to call
165 munmap (ptr_aligned, big_size);
166 which increases the number of VMAs by 1, which is supposed
167 to fail. */
168 free (ptr);
169 ASSERT_NO_STDIO (errno == 1789);
173 #endif
174 #endif
176 return test_exit_status;