10#include <sys/syscall.h>
12static int anonymous_resize(
void) {
14 const size_t p = vm_page;
15 unsigned char *base = MAP_FAILED, *moved = MAP_FAILED, *tail = MAP_FAILED, *reuse = MAP_FAILED;
16 unsigned char resident[8];
17 CHECK((base = mmap(NULL, 8 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) !=
19 for (
int n = 0; n < 8; ++n)
20 memset(base + n * p, 0x40 + n, p);
21 CHECK(mremap(base, 8 * p, 8 * p, 0) == base);
22 CHECK(mremap(base, 8 * p, 4 * p, 0) == base);
23 CHECK(mincore(base + 4 * p, p, resident) == -1 && errno == ENOMEM);
24 CHECK((tail = mmap(base + 4 * p, 4 * p, PROT_READ | PROT_WRITE,
25 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0)) == base + 4 * p);
26 memset(tail, 0xee, 4 * p);
27 CHECK(mremap(base, 4 * p, 6 * p, 0) == MAP_FAILED && errno == ENOMEM);
28 CHECK(vm_uniform(base, p, 0x40) && vm_uniform(base + 3 * p, p, 0x43) &&
29 vm_uniform(tail, 4 * p, 0xee));
30 CHECK((moved = mremap(base, 4 * p, 6 * p, MREMAP_MAYMOVE)) != MAP_FAILED && moved != base);
31 CHECK(mincore(moved, 6 * p, resident) == 0 && resident[4] == 0 && resident[5] == 0);
32 for (
int n = 0; n < 4; ++n)
33 CHECK(vm_uniform(moved + n * p, p, 0x40 + n));
34 CHECK(vm_uniform(moved + 4 * p, 2 * p, 0));
35 CHECK(mincore(base, p, resident) == -1 && errno == ENOMEM);
36 CHECK((reuse = mmap(base, 4 * p, PROT_READ | PROT_WRITE,
37 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0)) == base);
38 CHECK(vm_uniform(reuse, p, 0) && vm_uniform(tail, 4 * p, 0xee));
40 if (moved != MAP_FAILED)
42 if (base != MAP_FAILED)
47static int growth_and_rejection(
void) {
49 const size_t p = vm_page;
51 mmap(NULL, 4 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
52 unsigned char resident[4];
53 volatile int stack_value = 17;
54 void* stack_page = (
void*)((uintptr_t)&stack_value & ~(uintptr_t)(p - 1));
55 CHECK(base != MAP_FAILED);
56 memset(base, 0x51, 2 * p);
57 CHECK(munmap(base + 2 * p, 2 * p) == 0);
58 CHECK(mremap(base, 2 * p, 4 * p, 0) == base);
59 CHECK(mincore(base, 4 * p, resident) == 0 && resident[0] == 1 && resident[1] == 1 &&
60 resident[2] == 0 && resident[3] == 0);
61 CHECK(vm_uniform(base, 2 * p, 0x51) && vm_uniform(base + 2 * p, 2 * p, 0));
62 CHECK(mremap(base + 1, p, p, 0) == MAP_FAILED && errno == EINVAL);
63 CHECK(mremap(base, 4 * p, 0, 0) == MAP_FAILED && errno == EINVAL);
64 CHECK(mremap(base, 4 * p, 4 * p, 8) == MAP_FAILED && errno == EINVAL);
65 CHECK(mremap(base, 0, p, 0) == MAP_FAILED && errno == EINVAL);
66 CHECK(mremap(base, 0, p, MREMAP_MAYMOVE) == MAP_FAILED && errno == EOPNOTSUPP);
67 CHECK(mremap(base, 4 * p, 4 * p, MREMAP_MAYMOVE | MREMAP_DONTUNMAP) == MAP_FAILED &&
69 CHECK(mremap(base, 4 * p, 4 * p, MREMAP_FIXED, base + 8 * p) == MAP_FAILED && errno == EINVAL);
70 CHECK(mremap(base, 4 * p, 2 * p, MREMAP_FIXED | MREMAP_MAYMOVE, base + p) == MAP_FAILED &&
72 CHECK(mremap(base, 4 * p, 2 * p, MREMAP_FIXED | MREMAP_MAYMOVE, base + 1) == MAP_FAILED &&
74 CHECK(mremap(base, 4 * p, 65537 * p, MREMAP_MAYMOVE) == MAP_FAILED && errno == ENOMEM);
75 CHECK((
void*)syscall(SYS_mremap, base, 4 * p, SIZE_MAX, 0, NULL) == MAP_FAILED &&
77 CHECK(mremap(stack_page, p, p, 0) == MAP_FAILED && errno == EOPNOTSUPP && stack_value == 17);
78 CHECK(vm_uniform(base, 2 * p, 0x51) && vm_uniform(base + 2 * p, 2 * p, 0));
80 if (base != MAP_FAILED)
85static int fixed_victims(
void) {
87 const size_t p = vm_page;
88 unsigned char* source =
89 mmap(NULL, 3 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
90 unsigned char* target =
91 mmap(NULL, 5 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
92 unsigned char resident;
93 CHECK(source != MAP_FAILED && target != MAP_FAILED);
94 for (
int n = 0; n < 3; ++n)
95 memset(source + n * p, 0x61 + n, p);
96 for (
int n = 0; n < 5; ++n)
97 memset(target + n * p, 0x71 + n, p);
98 CHECK(mprotect(target + 4 * p, p, PROT_READ) == 0);
99 CHECK(mremap(source, 3 * p, 2 * p, MREMAP_FIXED | MREMAP_MAYMOVE, target + p) == target + p);
100 CHECK(vm_uniform(target, p, 0x71) && vm_uniform(target + p, p, 0x61) &&
101 vm_uniform(target + 2 * p, p, 0x62) && vm_uniform(target + 3 * p, p, 0x74) &&
102 vm_uniform(target + 4 * p, p, 0x75));
103 CHECK(vm_fault(target + 4 * p, 1, SIGSEGV) == 0);
104 CHECK(mincore(source, p, &resident) == -1 && errno == ENOMEM);
105 CHECK(mremap(source, p, p, 0) == MAP_FAILED && errno == EFAULT);
106 CHECK(mmap(source, 3 * p, PROT_READ | PROT_WRITE,
107 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0) == source);
108 CHECK(vm_uniform(source, 3 * p, 0));
110 if (source != MAP_FAILED)
111 munmap(source, 3 * p);
112 if (target != MAP_FAILED)
113 munmap(target, 5 * p);
117static int fork_cow(
void) {
118 int failed = 0, gate[2] = {-1, -1};
119 const size_t p = vm_page;
121 unsigned char* areas[3] = {MAP_FAILED, MAP_FAILED, MAP_FAILED};
122 unsigned char* current;
123 for (
int n = 0; n < 3; ++n) {
124 areas[n] = mmap(NULL, 2 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
125 CHECK(areas[n] != MAP_FAILED);
128 memset(current, 0x31, 2 * p);
129 CHECK(pipe(gate) == 0 && (child = fork()) >= 0);
133 if (read(gate[0], &
byte, 1) != 1)
135 for (
int n = 0; n < 12; ++n) {
136 memset(areas[0], 0xc0 + n, 2 * p);
138 if (!vm_uniform(areas[0], 2 * p, 0xc0 + n))
143 CHECK(write(gate[1],
"g", 1) == 1);
144 for (
int n = 0; n < 12; ++n) {
145 memset(current, 0x40 + n, 2 * p);
146 unsigned char* next = areas[1 + (n & 1)];
147 CHECK(mremap(current, 2 * p, 2 * p, MREMAP_FIXED | MREMAP_MAYMOVE, next) == next);
150 CHECK(vm_uniform(current, 2 * p, 0x40 + n));
152 int status = vm_reap(child, 3000);
154 CHECK(status == 0 && vm_uniform(current, 2 * p, 0x4b));
157 kill(child, SIGKILL);
158 vm_reap(child, 1000);
160 for (
int n = 0; n < 2; ++n)
163 for (
int n = 0; n < 3; ++n)
164 if (areas[n] != MAP_FAILED)
165 munmap(areas[n], 2 * p);
169static __thread
unsigned char tls_bytes[8192];
170static void* tls_probe(
void* argument) {
171 unsigned char value = (uintptr_t)argument;
172 memset(tls_bytes, value,
sizeof(tls_bytes));
174 return vm_uniform(tls_bytes,
sizeof(tls_bytes), value) ? NULL : (
void*)1;
176static void* thread_churn(
void* ignored) {
178 for (uintptr_t n = 1; n <= 12; ++n) {
181 if (pthread_create(&thread, NULL, tls_probe, (
void*)n) || pthread_join(thread, &result) ||
187static int allocation_and_tls(
void) {
188 int failed = 0, active = 0;
189 const size_t p = vm_page;
192 unsigned char* heap = malloc(192 * 1024);
193 unsigned char *current = MAP_FAILED, *next = MAP_FAILED;
195 memset(heap, 0x83, 192 * 1024);
196 unsigned char* replacement = realloc(heap, 384 * 1024);
197 CHECK(replacement != NULL);
199 CHECK(vm_uniform(heap, 192 * 1024, 0x83));
200 memset(heap + 192 * 1024, 0x84, 192 * 1024);
201 replacement = realloc(heap, 160 * 1024);
202 CHECK(replacement != NULL);
204 CHECK(vm_uniform(heap, 160 * 1024, 0x83));
205 CHECK((current = mmap(NULL, 2 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) !=
207 memset(current, 0x93, 2 * p);
208 CHECK(pthread_create(&
worker, NULL, thread_churn, NULL) == 0);
210 for (
int n = 0; n < 24; ++n) {
212 CHECK((next = mmap(NULL, 2 * p, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED);
213 CHECK(mremap(current, 2 * p, 2 * p, MREMAP_FIXED | MREMAP_MAYMOVE, next) == next);
217 CHECK(vm_uniform(current, 2 * p, 0x93));
219 CHECK(pthread_join(
worker, &result) == 0);
221 CHECK(result == NULL && vm_uniform(heap, 160 * 1024, 0x83));
224 pthread_join(
worker, NULL);
225 if (current != MAP_FAILED)
226 munmap(current, 2 * p);
227 if (next != MAP_FAILED)
233static int fragmented_lookup(
void) {
234 enum { Mappings = 64 };
235 const size_t p = vm_page, length = Mappings * 3 * p;
236 int failed = 0, fd = -1;
237 unsigned char* arena = MAP_FAILED;
238 CHECK((fd = vm_file(1, NULL)) >= 0);
239 CHECK((arena = mmap(NULL, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED);
242 for (
size_t n = 0; n < Mappings; ++n) {
243 const size_t i = (n * 37) % Mappings;
244 unsigned char* base = arena + i * 3 * p;
245 CHECK(mmap(base, 2 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
248 for (
size_t i = 0; i < Mappings; ++i) {
249 unsigned char* base = arena + i * 3 * p;
251 base[0] = (
unsigned char)(i + 1);
252 CHECK(pread(fd, base + p - 1, 2, 0) == 2);
253 CHECK(base[p - 1] == 0x20 && base[p] == 0x20);
254 base[2 * p - 1] = (
unsigned char)(i + 65);
256 CHECK(mprotect(base + p, p, PROT_READ) == 0);
258 CHECK(vm_fault(arena + 2 * p, 0, SIGSEGV) == 0);
259 CHECK(vm_fault(arena + 32 * 3 * p + p, 1, SIGSEGV) == 0);
260 CHECK(munmap(arena + 17 * 3 * p + 2 * p, p) == 0);
261 CHECK(vm_fault(arena + 17 * 3 * p + 2 * p, 0, SIGSEGV) == 0);
263 pid_t child = fork();
266 for (
size_t i = 0; i < Mappings; ++i) {
267 unsigned char* base = arena + i * 3 * p;
268 if (base[0] != i + 1 || base[2 * p - 1] != i + 65)
276 CHECK(vm_reap(child, 4000) == 0);
277 for (
size_t i = 0; i < Mappings; ++i) {
278 unsigned char* base = arena + i * 3 * p;
279 CHECK(base[0] == i + 1 && base[2 * p - 1] == i + 65);
282 unsigned char* source = arena + 32 * 3 * p;
283 unsigned char* destination = arena + 33 * 3 * p + 2 * p;
284 CHECK(mremap(source, p, p, MREMAP_FIXED | MREMAP_MAYMOVE, destination) == destination);
285 CHECK(destination[0] == 33 && destination[p - 1] == 0x20);
286 CHECK(source[p] == 0x20 && source[2 * p - 1] == 97);
287 CHECK(vm_fault(source, 0, SIGSEGV) == 0);
288 CHECK(mmap(source, p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) ==
290 CHECK(source[0] == 0 && source[p - 1] == 0);
291 CHECK(pread(fd, source, 1, 0) == 1 && source[0] == 0x20);
292 CHECK(destination[0] == 33);
294 if (arena != MAP_FAILED)
295 munmap(arena, length);
301int vm_test_remap(
void) {
302 return anonymous_resize() || growth_and_rejection() || fixed_victims() || vm_test_remap_file() ||
303 vm_test_remap_shm() || fork_cow() || allocation_and_tls() || fragmented_lookup();