19ed77019SAugustin Cavalier /*
29ed77019SAugustin Cavalier * Copyright 2023, Haiku, Inc. All rights reserved.
39ed77019SAugustin Cavalier * Distributed under the terms of the MIT License.
49ed77019SAugustin Cavalier */
59ed77019SAugustin Cavalier
69ed77019SAugustin Cavalier #include <stdio.h>
779572316STrung Nguyen #include <stdlib.h>
89ed77019SAugustin Cavalier #include <string.h>
99ed77019SAugustin Cavalier #include <fcntl.h>
109ed77019SAugustin Cavalier #include <sys/mman.h>
1179572316STrung Nguyen #include <unistd.h>
129ed77019SAugustin Cavalier #include <OS.h>
139ed77019SAugustin Cavalier
149ed77019SAugustin Cavalier
15bdcc293fSTrung Nguyen int gTestFd = -1;
169ed77019SAugustin Cavalier
17bdcc293fSTrung Nguyen
18bdcc293fSTrung Nguyen int
map_negative_offset_test()19bdcc293fSTrung Nguyen map_negative_offset_test()
20bdcc293fSTrung Nguyen {
219ed77019SAugustin Cavalier // should fail (negative offset)
22bdcc293fSTrung Nguyen void* ptr = mmap(NULL, B_PAGE_SIZE, PROT_READ, MAP_PRIVATE, gTestFd, -4096);
23bdcc293fSTrung Nguyen if (ptr != MAP_FAILED) {
249ed77019SAugustin Cavalier printf("map-negative-offset unexpectedly succeeded!\n");
259ed77019SAugustin Cavalier return -1;
269ed77019SAugustin Cavalier }
27bdcc293fSTrung Nguyen return 0;
28bdcc293fSTrung Nguyen }
299ed77019SAugustin Cavalier
30bdcc293fSTrung Nguyen
31bdcc293fSTrung Nguyen int
map_cut_compare_test()32bdcc293fSTrung Nguyen map_cut_compare_test()
33bdcc293fSTrung Nguyen {
34bdcc293fSTrung Nguyen uint8* ptr1 = (uint8*)mmap(NULL, 16 * B_PAGE_SIZE, PROT_READ, MAP_PRIVATE, gTestFd, 0);
359ed77019SAugustin Cavalier uint8 chunk[128];
369ed77019SAugustin Cavalier memcpy(chunk, &ptr1[3 * B_PAGE_SIZE], sizeof(chunk));
379ed77019SAugustin Cavalier
389ed77019SAugustin Cavalier // now cut the area
399ed77019SAugustin Cavalier uint8* ptr2 = (uint8*)mmap(&ptr1[B_PAGE_SIZE], B_PAGE_SIZE,
409ed77019SAugustin Cavalier PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
419ed77019SAugustin Cavalier
429ed77019SAugustin Cavalier // validate that the area after the cut still has the expected data
439ed77019SAugustin Cavalier int status = memcmp(&ptr1[3 * B_PAGE_SIZE], chunk, sizeof(chunk));
449ed77019SAugustin Cavalier if (status != 0) {
459ed77019SAugustin Cavalier printf("map-cut-compare test failed!\n");
469ed77019SAugustin Cavalier return status;
479ed77019SAugustin Cavalier }
48bdcc293fSTrung Nguyen return 0;
49bdcc293fSTrung Nguyen }
50bdcc293fSTrung Nguyen
51bdcc293fSTrung Nguyen
52bdcc293fSTrung Nguyen int
map_protect_cut_test1()53*ce2b9f5eSAugustin Cavalier map_protect_cut_test1()
54bdcc293fSTrung Nguyen {
55bdcc293fSTrung Nguyen uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE,
56bdcc293fSTrung Nguyen MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
57bdcc293fSTrung Nguyen
58bdcc293fSTrung Nguyen // make the tail accessible
59bdcc293fSTrung Nguyen mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
60bdcc293fSTrung Nguyen
61bdcc293fSTrung Nguyen // store any value
62bdcc293fSTrung Nguyen ptr[B_PAGE_SIZE * 3] = 'a';
63bdcc293fSTrung Nguyen
64bdcc293fSTrung Nguyen // cut the area in the middle, before the accessible tail
65*ce2b9f5eSAugustin Cavalier if (mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_READ | PROT_WRITE,
66*ce2b9f5eSAugustin Cavalier MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == NULL)
67*ce2b9f5eSAugustin Cavalier return -1;
68bdcc293fSTrung Nguyen
69bdcc293fSTrung Nguyen // validate that this does not crash
70bdcc293fSTrung Nguyen if (ptr[B_PAGE_SIZE * 3] != 'a') {
71bdcc293fSTrung Nguyen printf("map-protect-cut test failed!\n");
72bdcc293fSTrung Nguyen return -1;
73bdcc293fSTrung Nguyen }
74bdcc293fSTrung Nguyen return 0;
75bdcc293fSTrung Nguyen }
76bdcc293fSTrung Nguyen
77bdcc293fSTrung Nguyen
78bdcc293fSTrung Nguyen int
map_protect_cut_test2()79*ce2b9f5eSAugustin Cavalier map_protect_cut_test2()
80*ce2b9f5eSAugustin Cavalier {
81*ce2b9f5eSAugustin Cavalier uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_READ | PROT_WRITE,
82*ce2b9f5eSAugustin Cavalier MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
83*ce2b9f5eSAugustin Cavalier
84*ce2b9f5eSAugustin Cavalier // store any value
85*ce2b9f5eSAugustin Cavalier ptr[B_PAGE_SIZE * 3] = 'a';
86*ce2b9f5eSAugustin Cavalier
87*ce2b9f5eSAugustin Cavalier // make the tail un-accessible
88*ce2b9f5eSAugustin Cavalier if (mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_NONE) != 0)
89*ce2b9f5eSAugustin Cavalier return -1;
90*ce2b9f5eSAugustin Cavalier
91*ce2b9f5eSAugustin Cavalier // cut the area in the middle, before the un-accessible tail
92*ce2b9f5eSAugustin Cavalier if (mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
93*ce2b9f5eSAugustin Cavalier MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == NULL)
94*ce2b9f5eSAugustin Cavalier return -1;
95*ce2b9f5eSAugustin Cavalier
96*ce2b9f5eSAugustin Cavalier // make the tail accessible again (changes commitment size)
97*ce2b9f5eSAugustin Cavalier if (mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE) != 0)
98*ce2b9f5eSAugustin Cavalier return -1;
99*ce2b9f5eSAugustin Cavalier
100*ce2b9f5eSAugustin Cavalier // validate that this does not crash
101*ce2b9f5eSAugustin Cavalier if (ptr[B_PAGE_SIZE * 3] != 'a') {
102*ce2b9f5eSAugustin Cavalier printf("map-protect-cut test failed!\n");
103*ce2b9f5eSAugustin Cavalier return -1;
104*ce2b9f5eSAugustin Cavalier }
105*ce2b9f5eSAugustin Cavalier
106*ce2b9f5eSAugustin Cavalier // store another value
107*ce2b9f5eSAugustin Cavalier ptr[B_PAGE_SIZE * 3] = 'b';
108*ce2b9f5eSAugustin Cavalier
109*ce2b9f5eSAugustin Cavalier // make the tail un-accessible again
110*ce2b9f5eSAugustin Cavalier if (mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_NONE) != 0)
111*ce2b9f5eSAugustin Cavalier return -1;
112*ce2b9f5eSAugustin Cavalier
113*ce2b9f5eSAugustin Cavalier // clear page protections and reset to area protections
114*ce2b9f5eSAugustin Cavalier set_area_protection(area_for(ptr + B_PAGE_SIZE * 3), B_READ_AREA | B_WRITE_AREA);
115*ce2b9f5eSAugustin Cavalier
116*ce2b9f5eSAugustin Cavalier // validate that this does not crash
117*ce2b9f5eSAugustin Cavalier if (ptr[B_PAGE_SIZE * 3] != 'b') {
118*ce2b9f5eSAugustin Cavalier printf("map-protect-cut test failed!\n");
119*ce2b9f5eSAugustin Cavalier return -1;
120*ce2b9f5eSAugustin Cavalier }
121*ce2b9f5eSAugustin Cavalier return 0;
122*ce2b9f5eSAugustin Cavalier }
123*ce2b9f5eSAugustin Cavalier
124*ce2b9f5eSAugustin Cavalier
125*ce2b9f5eSAugustin Cavalier int
map_cut_protect_test()126*ce2b9f5eSAugustin Cavalier map_cut_protect_test()
127*ce2b9f5eSAugustin Cavalier {
128*ce2b9f5eSAugustin Cavalier uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE,
129*ce2b9f5eSAugustin Cavalier MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
130*ce2b9f5eSAugustin Cavalier
131*ce2b9f5eSAugustin Cavalier // cut the area in the middle
132*ce2b9f5eSAugustin Cavalier if (mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_READ | PROT_WRITE,
133*ce2b9f5eSAugustin Cavalier MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == NULL)
134*ce2b9f5eSAugustin Cavalier return -1;
135*ce2b9f5eSAugustin Cavalier
136*ce2b9f5eSAugustin Cavalier // now make the tail accessible
137*ce2b9f5eSAugustin Cavalier mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
138*ce2b9f5eSAugustin Cavalier
139*ce2b9f5eSAugustin Cavalier // store any value
140*ce2b9f5eSAugustin Cavalier ptr[B_PAGE_SIZE * 3] = 'a';
141*ce2b9f5eSAugustin Cavalier return 0;
142*ce2b9f5eSAugustin Cavalier }
143*ce2b9f5eSAugustin Cavalier
144*ce2b9f5eSAugustin Cavalier
145*ce2b9f5eSAugustin Cavalier int
map_cut_fork_test()14679572316STrung Nguyen map_cut_fork_test()
14779572316STrung Nguyen {
14879572316STrung Nguyen char name[24];
14979572316STrung Nguyen sprintf(name, "/shm-mmap-cut-fork-test-%d", getpid());
15079572316STrung Nguyen name[sizeof(name) - 1] = '\0';
15179572316STrung Nguyen shm_unlink(name);
15279572316STrung Nguyen int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
15379572316STrung Nguyen shm_unlink(name);
15479572316STrung Nguyen
15579572316STrung Nguyen if (fd < 0) {
15679572316STrung Nguyen printf("failed to create temporary file!\n");
15779572316STrung Nguyen return fd;
15879572316STrung Nguyen }
15979572316STrung Nguyen
16079572316STrung Nguyen ftruncate(fd, B_PAGE_SIZE * 4);
16179572316STrung Nguyen
16279572316STrung Nguyen uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE, MAP_PRIVATE,
16379572316STrung Nguyen fd, 0);
16479572316STrung Nguyen
165c0a12a6bSAugustin Cavalier // we can close the FD as mmap acquires another reference to the vnode
166c0a12a6bSAugustin Cavalier close(fd);
167c0a12a6bSAugustin Cavalier
16879572316STrung Nguyen // make the head accessible and also force the kernel to allocate the
16979572316STrung Nguyen // page_protections array
17079572316STrung Nguyen mprotect(ptr, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
17179572316STrung Nguyen
17279572316STrung Nguyen // store any value
17379572316STrung Nguyen ptr[0] = 'a';
17479572316STrung Nguyen
17579572316STrung Nguyen // cut the area in the middle
17679572316STrung Nguyen mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
17779572316STrung Nguyen MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
17879572316STrung Nguyen
17979572316STrung Nguyen // validate that the fork does not crash the kernel
18079572316STrung Nguyen int pid = fork();
18179572316STrung Nguyen
182*ce2b9f5eSAugustin Cavalier if (pid == 0) {
18379572316STrung Nguyen exit(0);
184*ce2b9f5eSAugustin Cavalier } else if (pid < 0) {
18579572316STrung Nguyen printf("failed to fork the test process!\n");
18679572316STrung Nguyen return pid;
18779572316STrung Nguyen }
18879572316STrung Nguyen
18979572316STrung Nguyen int status;
19079572316STrung Nguyen waitpid(pid, &status, 0);
19179572316STrung Nguyen
19279572316STrung Nguyen // validate that this does not crash
19379572316STrung Nguyen if (ptr[0] != 'a') {
19479572316STrung Nguyen printf("map-cut-fork test failed!\n");
19579572316STrung Nguyen return -1;
19679572316STrung Nguyen }
19779572316STrung Nguyen return 0;
19879572316STrung Nguyen }
19979572316STrung Nguyen
20079572316STrung Nguyen
20179572316STrung Nguyen int
main()202bdcc293fSTrung Nguyen main()
203bdcc293fSTrung Nguyen {
204bdcc293fSTrung Nguyen gTestFd = open("/boot/system/lib/libroot.so", O_CLOEXEC | O_RDONLY);
205bdcc293fSTrung Nguyen if (gTestFd < 0)
206bdcc293fSTrung Nguyen return -1;
207bdcc293fSTrung Nguyen
208bdcc293fSTrung Nguyen int status;
209bdcc293fSTrung Nguyen
210bdcc293fSTrung Nguyen if ((status = map_negative_offset_test()) != 0)
211bdcc293fSTrung Nguyen return status;
212bdcc293fSTrung Nguyen
213bdcc293fSTrung Nguyen if ((status = map_cut_compare_test()) != 0)
214bdcc293fSTrung Nguyen return status;
215bdcc293fSTrung Nguyen
216*ce2b9f5eSAugustin Cavalier if ((status = map_protect_cut_test1()) != 0)
217*ce2b9f5eSAugustin Cavalier return status;
218*ce2b9f5eSAugustin Cavalier
219*ce2b9f5eSAugustin Cavalier if ((status = map_protect_cut_test2()) != 0)
220*ce2b9f5eSAugustin Cavalier return status;
221*ce2b9f5eSAugustin Cavalier
222*ce2b9f5eSAugustin Cavalier if ((status = map_cut_protect_test()) != 0)
223bdcc293fSTrung Nguyen return status;
2249ed77019SAugustin Cavalier
22579572316STrung Nguyen if ((status = map_cut_fork_test()) != 0)
22679572316STrung Nguyen return status;
22779572316STrung Nguyen
2289ed77019SAugustin Cavalier return 0;
2299ed77019SAugustin Cavalier }
230