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_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 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 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 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 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