DF-2835 / vmsp_abi.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 | /* * DF-2835 PoC -- sys_vmspace_create violates its own ABI contract. * * Contract (sys/vm/vm_vmspace.c:134-139): * "An id of NULL cannot be used. The type and data fields must * currently be 0." * * Buggy kernel: the implementation (sys/vm/vm_vmspace.c:146-201) never * checks uap->id / uap->type / uap->data. This program, running as an * unprivileged user, shows: * [1] create(id=NULL, 0, NULL) is accepted (rc=0) -- want -1/EINVAL * [2] a second NULL create returns EEXIST, proving the NULL id became a * live RB-tree key (sys/vm/vm_vmspace.c:190) * [3] create(id, type=123, data=0xdead0000) is accepted, type/data are * silently ignored -- want -1/EINVAL * [4] a normal documented create still works (want rc=0) * * Exit status: 0 when the contract violation reproduces, 1 when the * contract is enforced (fixed kernel). * * Requires vm.vkernel_enable=1 (root sysctl), which is the documented * operating configuration on any vkernel host. */ #include <errno.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/syscall.h> static int last_errno; static long vmspace_create(void *id, int type, void *data) { long r; errno = 0; r = syscall(SYS_vmspace_create, id, type, data); last_errno = errno; return (r); } static long vmspace_destroy(void *id) { long r; errno = 0; r = syscall(SYS_vmspace_destroy, id); last_errno = errno; return (r); } int main(void) { int bug_null = 0, bug_typed = 0; long r; printf("uid=%d euid=%d SYS_vmspace_create=%d\n", getuid(), geteuid(), SYS_vmspace_create); r = vmspace_create(NULL, 0, NULL); printf("[1] create(id=NULL, type=0, data=NULL) -> rc=%ld errno=%d (%s) want -1/EINVAL\n", r, last_errno, last_errno ? strerror(last_errno) : "ACCEPTED"); if (r == 0) bug_null = 1; r = vmspace_create(NULL, 0, NULL); printf("[2] create(id=NULL, ...) again -> rc=%ld errno=%d (%s) EEXIST proves NULL is a live RB key\n", r, last_errno, last_errno ? strerror(last_errno) : "ACCEPTED"); if (bug_null && r == -1 && last_errno == EEXIST) printf(" => NULL id really resides in the per-proc vmspace RB tree\n"); r = vmspace_create((void *)0x40000000UL, 123, (void *)0xdead0000UL); printf("[3] create(id=%p, type=123, data=%p) -> rc=%ld errno=%d (%s) want -1/EINVAL\n", (void *)0x40000000UL, (void *)0xdead0000UL, r, last_errno, last_errno ? strerror(last_errno) : "ACCEPTED"); if (r == 0) bug_typed = 1; r = vmspace_create((void *)0x40000001UL, 0, NULL); printf("[4] create(id=%p, type=0, data=NULL) -> rc=%ld errno=%d (%s) want 0 (legit use unchanged)\n", (void *)0x40000001UL, r, last_errno, last_errno ? strerror(last_errno) : "OK"); /* cleanup: destroy whatever we created so later tests start clean */ r = vmspace_destroy(NULL); printf("[c] destroy(id=NULL) -> rc=%ld errno=%d\n", r, last_errno); r = vmspace_destroy((void *)0x40000000UL); printf("[c] destroy(id=%p) -> rc=%ld errno=%d\n", (void *)0x40000000UL, r, last_errno); r = vmspace_destroy((void *)0x40000001UL); printf("[c] destroy(id=%p) -> rc=%ld errno=%d\n", (void *)0x40000001UL, r, last_errno); printf("RESULT: null_id_accepted=%d typed_args_ignored=%d\n", bug_null, bug_typed); if (bug_null || bug_typed) { printf("VERDICT: BUG REPRODUCED -- documented ABI contract not enforced\n"); return (0); /* bug present */ } printf("VERDICT: contract enforced (EINVAL) -- fixed kernel\n"); return (1); /* bug absent */ } |