1 | /* Test that free preserves errno. |
2 | Copyright (C) 2020-2022 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 | #include <errno.h> |
20 | #include <stdlib.h> |
21 | #include <string.h> |
22 | #include <unistd.h> |
23 | #include <fcntl.h> |
24 | #include <stdint.h> |
25 | #include <string.h> |
26 | #include <sys/mman.h> |
27 | #include <support/check.h> |
28 | #include <support/support.h> |
29 | #include <support/temp_file.h> |
30 | #include <support/xunistd.h> |
31 | |
32 | /* The __attribute__ ((weak)) prevents a GCC optimization. Without |
33 | it, GCC would "know" that errno is unchanged by calling free (ptr), |
34 | when ptr was the result of a malloc call in the same function. */ |
35 | int __attribute__ ((weak)) |
36 | get_errno (void) |
37 | { |
38 | return errno; |
39 | } |
40 | |
41 | static int |
42 | do_test (void) |
43 | { |
44 | /* Check that free() preserves errno. */ |
45 | { |
46 | errno = 1789; /* Liberté, égalité, fraternité. */ |
47 | free (NULL); |
48 | TEST_VERIFY (get_errno () == 1789); |
49 | } |
50 | { /* Large memory allocations, to force mmap. */ |
51 | enum { N = 2 }; |
52 | void * volatile ptrs[N]; |
53 | size_t i; |
54 | for (i = 0; i < N; i++) |
55 | ptrs[i] = xmalloc (n: 5318153); |
56 | for (i = 0; i < N; i++) |
57 | { |
58 | errno = 1789; |
59 | free (ptr: ptrs[i]); |
60 | TEST_VERIFY (get_errno () == 1789); |
61 | } |
62 | } |
63 | |
64 | /* Test a less common code path. |
65 | When malloc() is based on mmap(), free() can sometimes call munmap(). |
66 | munmap() usually succeeds, but fails in a particular situation: when |
67 | - it has to unmap the middle part of a VMA, and |
68 | - the number of VMAs of a process is limited and the limit is |
69 | already reached. |
70 | The latter condition is fulfilled on Linux, when the file |
71 | /proc/sys/vm/max_map_count exists. For all known Linux versions |
72 | the default limit is at most 65536. |
73 | */ |
74 | #if defined __linux__ |
75 | if (xopen (path: "/proc/sys/vm/max_map_count" , O_RDONLY, 0) >= 0) |
76 | { |
77 | /* Preparations. */ |
78 | size_t pagesize = getpagesize (); |
79 | void *firstpage_backup = xmalloc (n: pagesize); |
80 | void *lastpage_backup = xmalloc (n: pagesize); |
81 | /* Allocate a large memory area, as a bumper, so that the MAP_FIXED |
82 | allocation later will not overwrite parts of the memory areas |
83 | allocated to ld.so or libc.so. */ |
84 | xmmap (NULL, length: 0x1000000, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, fd: -1); |
85 | /* A file descriptor pointing to a regular file. */ |
86 | int fd = create_temp_file (base: "tst-free-errno" , NULL); |
87 | if (fd < 0) |
88 | FAIL_EXIT1 ("cannot create temporary file" ); |
89 | |
90 | /* Do a large memory allocation. */ |
91 | size_t big_size = 0x1000000; |
92 | void * volatile ptr = xmalloc (n: big_size - 0x100); |
93 | char *ptr_aligned = (char *) ((uintptr_t) ptr & ~(pagesize - 1)); |
94 | /* This large memory allocation allocated a memory area |
95 | from ptr_aligned to ptr_aligned + big_size. |
96 | Enlarge this memory area by adding a page before and a page |
97 | after it. */ |
98 | memcpy (firstpage_backup, ptr_aligned, pagesize); |
99 | memcpy (lastpage_backup, ptr_aligned + big_size - pagesize, |
100 | pagesize); |
101 | xmmap (addr: ptr_aligned - pagesize, length: pagesize + big_size + pagesize, |
102 | PROT_READ | PROT_WRITE, |
103 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, fd: -1); |
104 | memcpy (ptr_aligned, firstpage_backup, pagesize); |
105 | memcpy (ptr_aligned + big_size - pagesize, lastpage_backup, |
106 | pagesize); |
107 | |
108 | /* Now add as many mappings as we can. |
109 | Stop at 65536, in order not to crash the machine (in case the |
110 | limit has been increased by the system administrator). */ |
111 | for (int i = 0; i < 65536; i++) |
112 | if (mmap (NULL, len: pagesize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd: fd, offset: 0) |
113 | == MAP_FAILED) |
114 | break; |
115 | /* Now the number of VMAs of this process has hopefully attained |
116 | its limit. */ |
117 | |
118 | errno = 1789; |
119 | /* This call to free() is supposed to call |
120 | munmap (ptr_aligned, big_size); |
121 | which increases the number of VMAs by 1, which is supposed |
122 | to fail. */ |
123 | free (ptr: ptr); |
124 | TEST_VERIFY (get_errno () == 1789); |
125 | } |
126 | #endif |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | #include <support/test-driver.c> |
132 | |