xref: /haiku/src/tests/system/kernel/mmap_cut_tests.cpp (revision 2141d2fe3a5df2f55f3590f67660573b50d1d1d3)
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 	// we can close the FD as mmap acquires another reference to the vnode
98 	close(fd);
99 
100 	// make the head accessible and also force the kernel to allocate the
101 	// page_protections array
102 	mprotect(ptr, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
103 
104 	// store any value
105 	ptr[0] = 'a';
106 
107 	// cut the area in the middle
108 	mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
109 		MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
110 
111 	// validate that the fork does not crash the kernel
112 	int pid = fork();
113 
114 	if (pid == 0)
115 	{
116 		exit(0);
117 	}
118 	else if (pid < 0)
119 	{
120 		printf("failed to fork the test process!\n");
121 		return pid;
122 	}
123 
124 	int status;
125 	waitpid(pid, &status, 0);
126 
127 	// validate that this does not crash
128 	if (ptr[0] != 'a') {
129 		printf("map-cut-fork test failed!\n");
130 		return -1;
131 	}
132 	return 0;
133 }
134 
135 
136 int
137 main()
138 {
139 	gTestFd = open("/boot/system/lib/libroot.so", O_CLOEXEC | O_RDONLY);
140 	if (gTestFd < 0)
141 		return -1;
142 
143 	int status;
144 
145 	if ((status = map_negative_offset_test()) != 0)
146 		return status;
147 
148 	if ((status = map_cut_compare_test()) != 0)
149 		return status;
150 
151 	if ((status = map_protect_cut_test()) != 0)
152 		return status;
153 
154 	if ((status = map_cut_fork_test()) != 0)
155 		return status;
156 
157 	return 0;
158 }
159