DragonFlyBSD Kernel Audit
DF-0585 / fix_run.log
← back to finding ↓ download raw
=== DF-0585 FIX-VALIDATION — patched #1 kernel + patched if_tap.ko ===

BUILD
-----
Source patch applied to /usr/src/sys/net/tap/if_tap.c (patch -p1, Hunk #1 succeeded at 741, rc=0):
  742: case TAPSIFINFO:
  743:     tapp = (struct tapinfo *)data;
  744:     if (ifp->if_type != tapp->type) {
  745:         ifnet_deserialize_all(ifp);     <-- added: release serializer
  746:         return (EPROTOTYPE);
  747:     }

Build: cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC  (warm obj)
  => === NK_DONE rc=0 ===   (Thu Jul  2 16:50:15 UTC 2026)
  Full nativekernel output: see fix_build.log (~35k lines, no errors)

INSTALLED ARTIFACTS
-------------------
/boot/kernel/kernel          = kernel.stripped  (15,705,800 B, Jul 2 16:50)  #1
/boot/kernel/kernel.debug    = kernel.debug     (119,340,592 B, Jul 2 16:50)
/boot/kernel/if_tap.ko       = PATCHED module   (263,008 B, Jul 2 16:50)
  sha256: bfe90971b5139929f255114c712a85b5621b41550dc55782fa14c00a46d92e02
/boot/kernel/if_tap.ko.orig  = BASELINE module  (262,320 B, Jun 29 14:24)
  sha256: 8e1a6da38502efe3cc7db2e6bb07555d555312bb02c4d5d44436b68791c65393

IMPORTANT NUANCE: in X86_64_GENERIC, tap(4) is a KLD MODULE, not a static
kernel device. The bug (and the fix) live in if_tap.ko, NOT in the kernel
binary. The first fix-validation attempt rebuilt the kernel but left the old
Jun-29 if_tap.ko on disk; kldload if_tap then loaded the UNPATCHED module and
the wedge reproduced even on the #1 kernel. The correct validation installs
BOTH the rebuilt kernel AND the rebuilt if_tap.ko. The fix is a one-line change
in the module; the kernel rebuild is incidental (version bump only).

PATCHED KERNEL
--------------
kern.version: DragonFly 6.5-DEVELOPMENT #1: Thu Jul  2 16:46:56 UTC 2026
loaded if_tap.ko sha256: bfe90971b5139929f255114c712a85b5621b41550dc55782fa14c00a46d92e02  (== patched)

RUN 1 (decisive)
----------------
Trigger ./leak_tap_lock /dev/tap on #1 + patched module:
[*] opened /dev/tap (fd=3)
[*] cloned interface: tap0
[*] TAPSIFINFO returned -1: errno=41 (Protocol wrong type for socket)  [EPROTOTYPE=41]
[*] tapioctl() early-returned at if_tap.c:745 (now WITH ifnet_deserialize_all at 745)
[*] parent dropped its fd; child 851 will perform the final close()
[+] child pid 851 EXITED after close() (status=0x0)
[+] -> tapclose() completed; serializer released
[+] -> DF-0585 NOT reproduced: no wedge (FIXED kernel)
TRIGGER_RC=1

Kernel-side state after trigger (vs baseline D1/slize):
  956  S1  nanslp  leak_tap_lock     <- parent sleeping in its 3s probe
  958  Z   -       leak_tap_lock     <- child EXITED (zombie), NOT wedged

Independent `ifconfig tap0` (timed):
  ifconfig rc=1  elapsed=0s          <- returned immediately ("interface tap0
                                        does not exist" because the completing
                                        close() properly tore down the clone)

RUN 2 (determinism)
-------------------
ifconfig tap0 rc=1 (returned immediately); no D-state leak_tap_lock procs.

RUN 3 (determinism)
-------------------
ifconfig tap0 rc=1 (returned immediately); no D-state leak_tap_lock procs.

BEFORE / AFTER CONTRAST
-----------------------
                       BASELINE (#0 + orig if_tap.ko)     PATCHED (#1 + new if_tap.ko)
child state after 3s   D1  wchan=slize  (WEDGED)          Z   (EXITED)
ifconfig tap0          hangs >120s in D-sleep (slize)     returns 0s, rc=1
interface              permanently orphaned serializer    cleanly torn down

==> FIX VALIDATED: the orphaned serializer is released on the TAPSIFINFO
    type-mismatch error path; tapclose() completes; the interface is no
    longer wedged. Behavior on every other path is unchanged.