== DF-0768 panic signature (from dfbsd-qemu/boot.log, unpatched #0 kernel) == Trigger: unprivileged `ls -f /mnt` (getdents) as maxx (uid 1001) against a malicious NFSv3 server mounted with `-o rdirplus` at /mnt. The malicious READDIRPLUS reply contains one entry whose `name_attributes` has attributes_follow = 0, immediately followed by the u32 0x7FFFFFFD, which the client's buggy "else" branch in nfs_readdirplusrpc_uio() (sys/vfs/nfs/nfs_vnops.c:2924-2928) reads as the (mythical) file-handle length `i`: NULLOUT(tl = nfsm_dissect(&info, NFSX_UNSIGNED)); i = fxdr_unsigned(int, *tl); /* i = 0x7FFFFFFD */ ERROROUT(nfsm_adv(&info, nfsm_rndup(i))); /* nfsm_rndup overflows to INT_MIN */ nfsm_rndup(a) = ((a)+3) & ~3 -> for a=0x7FFFFFFD: (0x80000000) & ~3 = 0x80000000 = INT_MIN (-2147483648). nfsm_adv(info, INT_MIN) does: n = mtod(md)+md->m_len - dpos; /* small positive (bytes left in cluster) */ if (n >= len) /* n >= INT_MIN -> TRUE (positive >= negative) */ dpos += len; /* dpos += INT_MIN -> 2 GiB BELOW real data */ ...returning success. The next nfsm_dissect(4) computes n = (cluster_end - corrupted_dpos) which truncates to a NEGATIVE int, so it routes to nfsm_disct(). Because the reply was padded to span >=2 mbuf clusters, mp->m_next != NULL and nfsm_disct takes the mbuf pull-up branch: MGET(mp2, ...); mp->m_len -= left; /* left<0 -> m_len grows huge */ bcopy(*dposp, p, left); /* (size_t)left ~= 2^64 -> copies forever from the wild dpos -> page fault */ Fatal trap 12: page fault while in kernel mode cpuid = 3; lapic id = 3 fault virtual address = 0xfffff80016c7fff8 fault code = supervisor read data, page not present instruction pointer = 0x8:0xffffffff80bcab4f stack pointer = 0x10:0xfffff80117a8b2b0 frame pointer = 0x10:0xfffff80117a8b308 code segment = base 0x0, limit 0xffff, type 0x1b = DPL 0, pres 1, long 0, def32 0, gran 1 processor eflags = interrupt enabled, resume, IOPL = 0 current process = 993 current thread = pri 10 kernel: type 12 trap, code=0 CPU3 stopping CPUs: 0x00000037 stopped Stopped at memmove+0x24f: repe movsq (%rsi),%es:(%rdi) db> == interpretation == memmove+0x24f == the `repe movsq (%rsi),%es:(%rdi)` inside bcopy/memmove, which is the `bcopy(*dposp, p, left)` in nfsm_disct()'s mbuf pull-up branch (sys/vfs/nfs/nfsm_subs.c ~1352). %rsi = the corrupted dpos (0xfffff80016c7fff8 -- note the 0xfffff800 high bits, ~2 GiB below the real mbuf data at 0xfffff801..). The page fault is a supervisor READ at that wild address -- a wild-pointer read, not a write. No write primitive -> no privilege-escalation path; impact is local DoS / kernel panic. The fault address 0xfffff80016c7fff8 ends in ...ff8, i.e. 8 bytes below a page boundary (0x...000) -- consistent with bcopy reading 8-byte (movsq) words and faulting at the start of an unmapped page just below the last mapped one.