⬢ DragonFlyBSD Kernel Audit
DF-3204 / env.txt
← back to finding ↓ download raw
Guest (single-tenant QEMU/KVM rig, owned for the whole verification run)
  DragonFly dfbsd 6.5-DEVELOPMENT x86_64; baseline decisive boot
  "DragonFly 6.5-DEVELOPMENT #1: Tue Oct 20 17:57:48 UTC 2026" (harness
  kernel over the audited-verbatim krpc_subr.c); fixed decisive boot
  "DragonFly 6.5-DEVELOPMENT #2: Tue Oct 20 18:25:39 UTC 2026" (same
  harness over the fix_all.diff variant). Stock config X86_64_GENERIC
  WITH INVARIANTS (options NFS_ROOT at sys/config/X86_64_GENERIC:29;
  krpc_subr.c compiled in via "vfs/nfs/krpc_subr.c optional nfs").
  CPU: QEMU Virtual CPU (6 vCPUs), 4 GB RAM.
  Compiler: cc 8.3 [DragonFly] Release/2019-02-22; kern.osreldate=600519.

Rig (verbatim interposition, kernel-compiled; no socket seams)
  - /usr/src/sys/vfs/nfs/dfrig13_krpc.c  (pack copy = the exact file
    compiled into the guest kernel; sha256
    7e1e3ce06a6846a1b942d9d9f0098be5685fe36980db2620828f691ffb429520)
    #defines the three external symbols of the production source
    (krpc_portmap/krpc_call/xdr_string_encode -> dfx_*) and then
    #includes krpc_subr.c VERBATIM.  Everything under test -- the
    static_xid = ~0xFF counter, the reserved-port sweep, the unconnected
    socket + discarded reply source, the residual-length dead gate, the
    pre-pullup rp_status read, the PROGMISMATCH exit -- is the
    production code, byte for byte.  The rig drives the REAL kernel UDP
    stack over lo0 with REAL mbufs; the attacker is the userland
    process attacker.c (sha256
    d7337b8717b8d4edb990f51ff612ea099085970756a8314fbbe9ab328c0507f1)
    playing the malicious boot-network host.
  - /usr/src/sys/conf/files: one added line
    "vfs/nfs/dfrig13_krpc.c	optional nfs"
  - built with: cd /usr/src && make -j6 nativekernel
    KERNCONF=X86_64_GENERIC && make installkernel  (build.sh performs
    the sha256 verbatim gate FIRST and refuses to build otherwise).
  - verbatim gate hashes:
      baseline sys/vfs/nfs/krpc_subr.c:
        284e77ed3ba5c8f640e4831f7a43f6a78fbcde363e86ded82a7088b7796e5d63
      fixed (baseline + fix_all.diff, all three findings):
        af834f6e30225b1af68cc83cbcdaf25e29e0e39ca6deaec6f3c13663a661b886
  - trigger: sysctl -w kern.dfrig13_arg=<arg>; sysctl -w kern.dfrig13=
    <test>; markers via sysctl -n kern.dfrig13_status; verbatim console
    lines ("rpc denied, status=N", "RPC timeout for server ...") in
    dmesg.  CPU pinning with "usched :1 <cmd>" aligns the groom/attack
    allocations to one objcache magazine (DragonFly has no cpuset(1)).
  - 127.0.0.2 alias: "ifconfig lo0 127.0.0.2 netmask 255.0.0.0 alias"
    (run scripts do this themselves; DragonFly lo0 does not accept
    arbitrary 127/8 binds without the alias).

Reachability (honest scope)
  - krpc_call/krpc_portmap have exactly two call sites, both in
    sys/vfs/nfs/nfs_mountrpc.c (md_mount :140-163, md_lookup_swap
    :202-256), reached only from nfs_mountroot (nfs_vfsops.c:557,
    mountroot hook :943) and the BOOTP netboot path -- i.e. only while
    a root-on-NFS (diskless) client boots.  The stock guest boots from
    disk, so the production path never runs at runtime; the rig drives
    the same code directly.  Attacker model: a malicious or compromised
    host on the boot network (or the portmap/mountd/NFS server itself).
  - No runtime syscall path reaches krpc on a disk-root machine (rg
    over callers verified), so none of the three findings is a local
    unpriv->root escalation on this guest; privesc assessment is in
    each VERDICT.md.

Notable rig facts
  - The infinite resend loop (krpc_subr.c:315 for(;;) with no global
    deadline) was observed live twice when an attacker mode served
    fewer requests than the harness attempts: the sysctl thread spins
    forever printing "RPC timeout for server 127.0.0.1" ~every 5 s
    (documented in the DF-3205 finding summary; recover by reboot).
  - DF-3204 mbuf accounting via "netstat -m | head -1" deltas is exact:
    50 PROGMISMATCH replies -> +50 mbufs that stay; honest control 0.
  - DF-3203 stale-byte attribution: kernel-side groom (uniform byte
    fill of freshly allocated plain+pkthdr mbufs, then freed) and a
    purely userland "primer" (24-byte datagrams to a dead port carry
    the pattern at offset 20..23; their freed mbuf slots keep it).
    The primer is the stronger artifact: the attacker alone plants the
    exact 4 bytes the kernel then reads out of bounds and prints.