xref: /haiku/src/tests/system/kernel/mmap_cut_tests.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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
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
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
53 map_protect_cut_test()
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 	mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_READ | PROT_WRITE,
66 		MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
67 
68 	// validate that this does not crash
69 	if (ptr[B_PAGE_SIZE * 3] != 'a') {
70 		printf("map-protect-cut test failed!\n");
71 		return -1;
72 	}
73 	return 0;
74 }
75 
76 
77 int
78 map_cut_fork_test()
79 {
80 	char name[24];
81 	sprintf(name, "/shm-mmap-cut-fork-test-%d", getpid());
82 	name[sizeof(name) - 1] = '\0';
83 	shm_unlink(name);
84 	int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
85 	shm_unlink(name);
86 
87 	if (fd < 0) {
88 		printf("failed to create temporary file!\n");
89 		return fd;
90 	}
91 
92 	ftruncate(fd, B_PAGE_SIZE * 4);
93 
94 	uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE, MAP_PRIVATE,
95 		fd, 0);
96 
97 	// make the head accessible and also force the kernel to allocate the
98 	// page_protections array
99 	mprotect(ptr, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
100 
101 	// store any value
102 	ptr[0] = 'a';
103 
104 	// cut the area in the middle
105 	mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
106 		MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
107 
108 	// validate that the fork does not crash the kernel
109 	int pid = fork();
110 
111 	if (pid == 0)
112 	{
113 		exit(0);
114 	}
115 	else if (pid < 0)
116 	{
117 		printf("failed to fork the test process!\n");
118 		return pid;
119 	}
120 
121 	int status;
122 	waitpid(pid, &status, 0);
123 
124 	// validate that this does not crash
125 	if (ptr[0] != 'a') {
126 		printf("map-cut-fork test failed!\n");
127 		return -1;
128 	}
129 	return 0;
130 }
131 
132 
133 int
134 main()
135 {
136 	gTestFd = open("/boot/system/lib/libroot.so", O_CLOEXEC | O_RDONLY);
137 	if (gTestFd < 0)
138 		return -1;
139 
140 	int status;
141 
142 	if ((status = map_negative_offset_test()) != 0)
143 		return status;
144 
145 	if ((status = map_cut_compare_test()) != 0)
146 		return status;
147 
148 	if ((status = map_protect_cut_test()) != 0)
149 		return status;
150 
151 	if ((status = map_cut_fork_test()) != 0)
152 		return status;
153 
154 	return 0;
155 }
156