xref: /haiku/src/tests/system/kernel/mmap_cut_tests.cpp (revision ce2b9f5ec2c2523f7a8cbb19fd639784f0e6850d)
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