DF-5800 / forkcow.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | /* * DF pass-3 vm_map.c stress rig 3 — fork COW churn. * * Exercises vmspace_fork / vmspace_fork_normal_entry / vm_map_copy_entry / * vm_map_backing_replicated / ONEMAPPING truncation in vm_map_delete: * - fork with mixed wired (mlock'd) + shared + COW + NONE-inherit entries * - children immediately mprotect RW and write (shadow path) * - parent munmaps partial ranges (ONEMAPPING tail truncation, * object->size shrink at vm_map.c:3362-3365) while children hold * backing refs * - re-fork from mutated map (backing_ba chain depth vs limit 5) * * build: cc -O -o forkcow forkcow.c -lpthread */ #include <sys/mman.h> #include <sys/wait.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #ifndef VM_INHERIT_NONE #define VM_INHERIT_SHARE 0 #define VM_INHERIT_COPY 1 #define VM_INHERIT_NONE 2 #endif #define NREG 256 #define REGSZ (32 * 1024) static void *regs[NREG]; int main(void) { int i, round, bad; pid_t pid; int status; unsigned char *p; for (i = 0; i < NREG; i++) { regs[i] = mmap(NULL, REGSZ, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (regs[i] == MAP_FAILED) { perror("mmap"); return 1; } memset(regs[i], i & 0xff, REGSZ); switch (i & 3) { case 0: minherit(regs[i], REGSZ, VM_INHERIT_SHARE); break; case 1: minherit(regs[i], REGSZ, VM_INHERIT_NONE); break; case 2: /* leave COPY */ mlock(regs[i], REGSZ); /* wired COW mix */ break; } } for (round = 0; round < 60; round++) { pid = fork(); if (pid == 0) { /* child: mutate a slice then fork a grandchild */ for (i = 0; i < NREG; i += 7) { if ((i & 3) == 1) continue; /* INHERIT_NONE: absent */ if (i & 1) { mprotect(regs[i], REGSZ, PROT_READ | PROT_WRITE | PROT_EXEC); memset(regs[i], 0xa3, REGSZ); } else { madvise(regs[i], REGSZ, MADV_DONTNEED); } } if (round & 1) { pid_t g = fork(); if (g == 0) { for (i = 0; i < NREG; i += 13) { if ((i & 3) == 1) continue; memset(regs[i], 0x3c, REGSZ); } _exit(0); } waitpid(g, &status, 0); } _exit(0); } waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { printf("round %d: child status %d sig %d\n", round, WIFEXITED(status) ? WEXITSTATUS(status) : -1, WTERMSIG(status)); return 2; } /* parent: churn partial munmaps + remaps */ for (i = round % 8; i < NREG; i += 32) { p = regs[i]; munmap(p + REGSZ / 2, REGSZ / 4); regs[i] = mmap(p, REGSZ, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); if (regs[i] == MAP_FAILED) { perror("remap"); return 3; } } } /* verify shareable regions still coherent */ bad = 0; for (i = 0; i < NREG; i += 4) if (*(volatile unsigned char *)regs[i] != (i & 0xff) && *(volatile unsigned char *)regs[i] != 0xa3 && *(volatile unsigned char *)regs[i] != 0x3c && *(volatile unsigned char *)regs[i] != 0) bad++; printf("forkcow: complete, %d unexpected bytes\n", bad); return bad ? 4 : 0; } |