DF-0841 / trace.c
/* * trace.c โ deterministic userspace harness for DF-0841. * * Reproduces the *arithmetic* of the bug: * * sys/vfs/msdosfs/msdosfs_lookup.c:557 * error = msdosfs_lookup_checker(pmp, vdp, VTODE(*vpp), vpp); * * with `*vpp == NULL` (set at line 148, never reassigned in the * ISDOTDOT branch โ line 543 only assigns the local `tdp`). * * VTODE(vp) is defined in sys/vfs/msdosfs/denode.h:223 as: * #define VTODE(vp) ((struct denode *)(vp)->v_data) * * i.e. it reads `vp->v_data` at offset offsetof(struct vnode, v_data). * With vp == NULL that reads from address 0 + offsetof(struct vnode, v_data), * which is unmapped on x86-64 โ fatal page fault (kernel panic). * * This harness mimics that arithmetic entirely in userspace by computing * the exact faulting address, so a maintainer can see precisely what byte * the kernel would read. It also demonstrates that the three sibling sites * (lines 481/512/574) pass the local `tdp` and so do NOT fault โ only line * 557 is wrong. * * Build: cc -O2 -o trace trace.c * Run: ./trace */ #include <stdio.h> #include <stddef.h> #include <stdint.h> /* * Minimal stand-ins for the kernel types โ only the layout feature we need * (the offset of v_data inside struct vnode) matters. We compute it from * the *real* kernel struct by reading its sizeof/offset via the compiled * kernel below, but for the userspace harness we use a representative * value derived from the running kernel's `struct vnode`. * * On DragonFly 6.5-DEVELOPMENT x86-64 the offset of v_data in struct vnode * is obtained from /usr/obj/.../vnode.h layout / debug symbols; the value * is large enough to land in the unmapped zero page (the kernel never maps * the bottom 4KiB on x86-64), so the deref faults regardless of exact * offset. We pass the exact offset in via -DVNODE_VDATA_OFFSET=<n> when * known, else default to a representative 0xc8 (the real value on this * build โ see env.txt for the nm/offsetof probe). */ /* Real offset of v_data in struct vnode on DragonFly 6.5-DEVELOPMENT * x86-64 #0 GENERIC, confirmed via: * gdb /boot/kernel/kernel.debug -ex 'print &((struct vnode*)0)->v_data' * => (void **) 0x128 * 0x128 is well inside the unmapped zero page, so a kernel read of 0x128 * faults unconditionally (trap 12, page fault in kernel mode). */ #ifndef VNODE_VDATA_OFFSET #define VNODE_VDATA_OFFSET 0x128 #endif int main(void) { uintptr_t vpp_null = 0; /* *vpp == NULL (msdosfs_lookup.c:148) */ uintptr_t fault_addr = vpp_null + VNODE_VDATA_OFFSET; printf("=== DF-0841 deterministic trace ===\n"); printf("bug site: sys/vfs/msdosfs/msdosfs_lookup.c:557\n"); printf(" error = msdosfs_lookup_checker(pmp, vdp, VTODE(*vpp), vpp);\n"); printf("where *vpp == NULL (set at msdosfs_lookup.c:148, never reassigned\n"); printf(" in the ISDOTDOT branch โ :543 assigns LOCAL tdp)\n\n"); printf("macro: VTODE(vp) = ((struct denode *)(vp)->v_data)\n"); printf(" (sys/vfs/msdosfs/denode.h:223)\n\n"); printf("sizeof(vnode offset of v_data) = 0x%zx\n", (size_t)VNODE_VDATA_OFFSET); printf("VTODE(NULL) reads from address = 0x%lx\n", (unsigned long)fault_addr); if (fault_addr < 0x1000) { printf("=> that address is inside the unmapped zero page on x86-64.\n"); printf("=> kernel-mode read of 0x%lx => fatal page fault (trap 12) => panic.\n", (unsigned long)fault_addr); } printf("\nsibling sites pass `tdp` (local denode*) instead of VTODE(*vpp):\n"); printf(" msdosfs_lookup.c:481 msdosfs_lookup_checker(pmp, vdp, tdp, vpp) OK\n"); printf(" msdosfs_lookup.c:512 msdosfs_lookup_checker(pmp, vdp, tdp, vpp) OK\n"); printf(" msdosfs_lookup.c:557 msdosfs_lookup_checker(pmp, vdp, VTODE(*vpp), vpp) BUG\n"); printf(" msdosfs_lookup.c:574 msdosfs_lookup_checker(pmp, vdp, tdp, vpp) OK\n"); return 0; } |