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