DF-3211 / verdict.json
{ "finding_id": "DF-3211", "status": "untested", "reproduced": 0, "impact": "dos", "confidence": "certain", "verdict": "md_lookup_swap reads only word [6] (low 32 bits) of the 64-bit NFSv3 fattr.size into a signed int (nfs_mountrpc.c:286; layout nfsproto.h:325: size spans words [5..6]) and divides it into the int swap_nblks (:296): a 2-4 GiB swap file yields a negative swap_nblks, >=4 GiB wraps mod 2^32 (8 GiB -> swap silently disabled); the v2 twin at :292 lands a >=2 GiB u32 size in the same signed int. nfs_mountroot then treats the negative count as truthy, doubles it, sets a negative n_size on the swap vnode, and passes it to swaponvps u_quad_t parameter where it sign-extends to ~2^64 blocks (nfs_vfsops.c:666-701, vm_swap.c) \u2014 corrupt swap geometry/boot failure. Arithmetic is certain; not runtime-testable because the code runs only during an NFS-root boot and the audit guest boots hammer2 from disk.", "exploit_chain": "", "evidence": "findings/poc/DF-3211/VERDICT.md steps 1-5; decisive lines: nfs_mountrpc.c:286 (v3[6] only, into signed int), :292 (v2 signed landing), :295-296 (swap_nblks = size/1024), nfsdiskless.h:70 (int swap_nblks), nfs_vfsops.c:691-692 (negative n_size), :701 + vm_swap.c swaponvp(u_quad_t) (sign extension), xdr_subs.h:83 (fxdr_hyper is the intended decoder)", "kernel_refs": [ "sys/vfs/nfs/nfs_mountrpc.c:286", "sys/vfs/nfs/nfs_mountrpc.c:292", "sys/vfs/nfs/nfs_mountrpc.c:295", "sys/vfs/nfs/nfsproto.h:325", "sys/vfs/nfs/nfs_vfsops.c:691", "sys/vfs/nfs/nfs_vfsops.c:701", "sys/vfs/nfs/xdr_subs.h:83" ], "poc_changes": "none \u2014 seed not runnable (boot path); verdict is a wire-layout + signedness derivation cross-checked against nfsproto.h fattr layout and the fxdr_hyper macro", "attempts": 0, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (X86_64_GENERIC; hammer2 disk root \u2014 boot path never executes)", "runtime_sec": 0, "guest_dirty": 0, "build_cmd": "", "run_cmd": "", "code_hash": "", "notes": "Trigger: legitimate server exporting a >2 GiB swap file, or DF-3205 forged LOOKUP reply (deterministic xid, unfiltered source). Downstream corruption is described from source; exact panic-vs-weird-failure at boot was not observable on the disk-root guest. Ceiling identical to the krpc pass-2 findings.", "recommended_fix": "decode fattr.size with fxdr_hyper(&fattribs.v3[5]) into u_quad_t and only accept sizes <= INT_MAX before storing into swap_nblks (fix.diff applies clean)" } |