1 /*
2 * Copyright 2023, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <fcntl.h>
10 #include <sys/mman.h>
11 #include <unistd.h>
12 #include <OS.h>
13
14
15 int gTestFd = -1;
16
17
18 int
map_negative_offset_test()19 map_negative_offset_test()
20 {
21 // should fail (negative offset)
22 void* ptr = mmap(NULL, B_PAGE_SIZE, PROT_READ, MAP_PRIVATE, gTestFd, -4096);
23 if (ptr != MAP_FAILED) {
24 printf("map-negative-offset unexpectedly succeeded!\n");
25 return -1;
26 }
27 return 0;
28 }
29
30
31 int
map_cut_compare_test()32 map_cut_compare_test()
33 {
34 uint8* ptr1 = (uint8*)mmap(NULL, 16 * B_PAGE_SIZE, PROT_READ, MAP_PRIVATE, gTestFd, 0);
35 uint8 chunk[128];
36 memcpy(chunk, &ptr1[3 * B_PAGE_SIZE], sizeof(chunk));
37
38 // now cut the area
39 uint8* ptr2 = (uint8*)mmap(&ptr1[B_PAGE_SIZE], B_PAGE_SIZE,
40 PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
41
42 // validate that the area after the cut still has the expected data
43 int status = memcmp(&ptr1[3 * B_PAGE_SIZE], chunk, sizeof(chunk));
44 if (status != 0) {
45 printf("map-cut-compare test failed!\n");
46 return status;
47 }
48 return 0;
49 }
50
51
52 int
map_protect_cut_test1()53 map_protect_cut_test1()
54 {
55 uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE,
56 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
57
58 // make the tail accessible
59 mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
60
61 // store any value
62 ptr[B_PAGE_SIZE * 3] = 'a';
63
64 // cut the area in the middle, before the accessible tail
65 if (mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_READ | PROT_WRITE,
66 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == NULL)
67 return -1;
68
69 // validate that this does not crash
70 if (ptr[B_PAGE_SIZE * 3] != 'a') {
71 printf("map-protect-cut test failed!\n");
72 return -1;
73 }
74 return 0;
75 }
76
77
78 int
map_protect_cut_test2()79 map_protect_cut_test2()
80 {
81 uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_READ | PROT_WRITE,
82 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
83
84 // store any value
85 ptr[B_PAGE_SIZE * 3] = 'a';
86
87 // make the tail un-accessible
88 if (mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_NONE) != 0)
89 return -1;
90
91 // cut the area in the middle, before the un-accessible tail
92 if (mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
93 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == NULL)
94 return -1;
95
96 // make the tail accessible again (changes commitment size)
97 if (mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE) != 0)
98 return -1;
99
100 // validate that this does not crash
101 if (ptr[B_PAGE_SIZE * 3] != 'a') {
102 printf("map-protect-cut test failed!\n");
103 return -1;
104 }
105
106 // store another value
107 ptr[B_PAGE_SIZE * 3] = 'b';
108
109 // make the tail un-accessible again
110 if (mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_NONE) != 0)
111 return -1;
112
113 // clear page protections and reset to area protections
114 set_area_protection(area_for(ptr + B_PAGE_SIZE * 3), B_READ_AREA | B_WRITE_AREA);
115
116 // validate that this does not crash
117 if (ptr[B_PAGE_SIZE * 3] != 'b') {
118 printf("map-protect-cut test failed!\n");
119 return -1;
120 }
121 return 0;
122 }
123
124
125 int
map_cut_protect_test()126 map_cut_protect_test()
127 {
128 uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE,
129 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
130
131 // cut the area in the middle
132 if (mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_READ | PROT_WRITE,
133 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == NULL)
134 return -1;
135
136 // now make the tail accessible
137 mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
138
139 // store any value
140 ptr[B_PAGE_SIZE * 3] = 'a';
141 return 0;
142 }
143
144
145 int
map_cut_fork_test()146 map_cut_fork_test()
147 {
148 char name[24];
149 sprintf(name, "/shm-mmap-cut-fork-test-%d", getpid());
150 name[sizeof(name) - 1] = '\0';
151 shm_unlink(name);
152 int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
153 shm_unlink(name);
154
155 if (fd < 0) {
156 printf("failed to create temporary file!\n");
157 return fd;
158 }
159
160 ftruncate(fd, B_PAGE_SIZE * 4);
161
162 uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE, MAP_PRIVATE,
163 fd, 0);
164
165 // we can close the FD as mmap acquires another reference to the vnode
166 close(fd);
167
168 // make the head accessible and also force the kernel to allocate the
169 // page_protections array
170 mprotect(ptr, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
171
172 // store any value
173 ptr[0] = 'a';
174
175 // cut the area in the middle
176 mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
177 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
178
179 // validate that the fork does not crash the kernel
180 int pid = fork();
181
182 if (pid == 0) {
183 exit(0);
184 } else if (pid < 0) {
185 printf("failed to fork the test process!\n");
186 return pid;
187 }
188
189 int status;
190 waitpid(pid, &status, 0);
191
192 // validate that this does not crash
193 if (ptr[0] != 'a') {
194 printf("map-cut-fork test failed!\n");
195 return -1;
196 }
197 return 0;
198 }
199
200
201 int
main()202 main()
203 {
204 gTestFd = open("/boot/system/lib/libroot.so", O_CLOEXEC | O_RDONLY);
205 if (gTestFd < 0)
206 return -1;
207
208 int status;
209
210 if ((status = map_negative_offset_test()) != 0)
211 return status;
212
213 if ((status = map_cut_compare_test()) != 0)
214 return status;
215
216 if ((status = map_protect_cut_test1()) != 0)
217 return status;
218
219 if ((status = map_protect_cut_test2()) != 0)
220 return status;
221
222 if ((status = map_cut_protect_test()) != 0)
223 return status;
224
225 if ((status = map_cut_fork_test()) != 0)
226 return status;
227
228 return 0;
229 }
230