umount -f frees autofs nodes/mount under in-flight triggers and mkdirs β use-after-free (read+write, dangling requests, armed callout in freed memory) and a guaranteed NULL-deref panic variant
Summary
autofs_trigger()'s retry loop parks the calling thread for 1-2s between attempts with sc_lock dropped and NO reference of any kind on the mount (autofs.c:451-453); the pre-trigger test_fs_root/nlookup_fs_root section (autofs_vnops.c:155-165) is a second gap. autofs_unmount() - under MNT_FORCE, where vflush(FORCECLOSE) force-vgones even referenced vnodes (vfs_mount.c:1165-1181) so in-flight vops keep running - drains sc_requests with a once-per-second scan then frees every autofs_node and the autofs_mount (autofs_vfsops.c:213-258). Its 'no new triggerings' assumption (:209-212) is false: a trigger parked in a retry gap has no request on the TAILQ, the scan sees empty, everything is freed, and the trigger's next autofs_trigger_one() executes on freed memory. Demonstrated sinks: post-free insertion of a request with ar_mount == freed amp (autofs.c:345/:362); strlcpy of amp->am_from/am_prefix/am_options from the freed/reused 4288-byte block into the daemon-visible request (:351-356) - heap bytes of whichever kernel object reuses the block leaked to automountd; anp->an_retries++ (:441-445), anp->an_cached/an_wildcards writes and callout_reset(&anp->an_callout, 600s) arming a callout inside the freed 256-byte node block (:409-413); and in autofs_nmkdir a guaranteed NULL-deref panic - after force-vgone dvp->v_mount becomes &dummymount whose mnt_data is NULL, so mtx_lock_ex_quick(&->am_lock) faults (:251/:263). VERIFIED deterministically on stock INVARIANTS guest: harness drives a real stat() trigger through VOP_NRESOLVE and calls the real dounmount(MNT_FORCE); after dounmount returns, a dangling request with ar_mount==freed amp and ar_from/ar_path containing markers planted in the reallocated amp block is observed on the live TAILQ, plus 6 write-after-free transitions on the freed node block ('UAF CONFIRMED'). Trigger side unpriv (stat/ls under the automount); free side root's umount -f (cron 'automount -u'/admin racing ordinary user churn - routine autofs operations). Result: silent kernel heap corruption (4-byte increments, daemon-influenced bool writes, callout-state corruption at type-stable freed offsets, post-free heap reads delivered to the daemon) or panic (NULL-deref variant). Not unpriv->root (racing unmount is privileged). Fix validated on rebuilt kernel (am_dying/am_triggers refcount + drain-before-free; a naive vfs_busy wrap deadlocks - documented): baseline UAF gone, no deadlock, normal autofs preserved; patched dounmount waits ~31s for drain.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3169 Β· 22 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df3169_race.c | β | 7.2 KB | view raw | |
| Makefile | β | 125 B | β download | |
| fake_daemon.c | β | 2.2 KB | view raw | |
| run.sh | β | 190 B | view raw | |
| variantB.sh | β | 1.2 KB | view raw | |
| sanity.sh | β | 302 B | view raw | |
| build.log | β | 839 B | view raw | |
| run.log | β | 1.9 KB | view raw | |
| run_kthread.log | β | 2.3 KB | view raw | |
| run_variantB.log | β | 252 B | view raw | |
| fix_run.log | β | 866 B | view raw | |
| fix_variantB.log | β | 130 B | view raw | |
| fix_build.log | β | 369 B | view raw | |
| fix.diff | β | 8.5 KB | view raw | |
| FIX_NOTES.md | β | 1.9 KB | β raw | |
| panic.txt | β | 1.9 KB | view raw | |
| panic_prelude.txt | β | 2.2 KB | view raw | |
| env.txt | β | 291 B | view raw | |
| README.md | β | 4.4 KB | β raw | |
| VERDICT.md | β | 8.9 KB | β raw | |
| manifest.json | β | 1.8 KB | view raw | |
| verdict.json | β | 6.5 KB | view raw |
DF-3169 β autofs umount -f vs in-flight trigger/mkdir: use-after-free
Bug: An in-flight autofs vop (autofs_nresolve trigger path /
autofs_readdir trigger path / autofs_nmkdir) holds only raw
anp/amp pointers across multi-second daemon-dispatch sleeps and the
1β2 s retry-gap sleeps of autofs_trigger()
(sys/vfs/autofs/autofs.c:427-455). autofs_unmount()
(sys/vfs/autofs/autofs_vfsops.c:195-261) β reachable concurrently via
umount -f (MNT_FORCE) β force-closes even referenced vnodes
(sys/kern/vfs_mount.c:1165-1181), drains sc_requests with a 1 s-cadence
scan, and then frees every autofs_node and the autofs_mount.
Its "no new triggerings" assumption
(autofs_vfsops.c:209-212) is false: a trigger thread parked in a retry
gap inserts new requests β and dereferences/writes the freed node β
after the free.
Sinks (all demonstrated or code-traced):
- read-after-free: anp->an_parent, anp->an_mount, amp->am_* copied
into the daemon-visible request (autofs.c:312-356) β leaked heap bytes
of whichever kernel object reuses the 4288-byte autofs_mount block;
- write-after-free: anp->an_retries++ (autofs.c:441-445),
anp->an_cached/anp->an_wildcards (autofs.c:410-411),
callout_reset(&anp->an_callout, β¦) arming a 600 s callout inside the
freed 256-byte node block (autofs.c:412-413);
- post-free request insertion with dangling ar_mount (autofs.c:345,362);
- autofs_nmkdir NULL-deref: after force-vgone, dvp->v_mount ==
&dummymount β amp == dummymount.mnt_data == NULL β
mtx_lock_ex_quick(&->am_lock) faults (autofs_vnops.c:251,263).
Trigger model: unprivileged churn (stat/ls under the automount β
the trigger side) racing root's umount -f (or automount -u) β i.e.
routine autofs admin operations on a box where users hit the automount.
Reproduce (deterministic KLD harness β primary proof)
Prerequisites (root, once)
kldload autofs
mkdir -p /df3169_mp
mount_autofs -f df3169 "" /df3169_mp # run.sh sets the sysctls
Build
./build.sh # builds df3169_race.ko in-guest (needs /usr/src)
Run (baseline, unpatched)
./baseline_run.sh # == run.sh content; see run.log for the result
Expected (see run.log, reproduced verbatim on stock INVARIANTS kernel):
DF3169: dounmount returned 0 (ticks=15842) -- anp=... amp=... ARE FREED NOW DF3169: reuse blocks: amp 0xfffff8008d4be800 ... SAME-BLOCK, node ... SAME-BLOCK DF3169: DANGLING REQUEST ar=... id=1 ar_mount=0xfffff8008d4be800 == FREED amp DF3169: ar_from="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"... <- read-after-free DF3169 POLL: freed anp=... an_retries: N -> N+1 ... <== WRITE-AFTER-FREE (Γ10) DF3169 W: trigger returned 60 (ticks=19042, dounmount_ticks=15842)
The harness re-allocates the freed blocks (marker 0x41 in the amp,
zeroed objcache node) right after dounmount() returns β same CPU, same
magazine β so the stale trigger's reads and writes become visible.
Run (production-shaped, probabilistic)
cc -O2 -pthread -o fake_daemon fake_daemon.c # + autofs_ioctl.h
./variantB.sh # 40 Γ (mount_autofs β trigger+mkdir storm β umount -f)
On the unpatched kernel this exercises the real userspace paths (58
daemon requests handled in the recorded run); the Β΅s-wide
autofs_nmkdir NULL-deref window did not hit within 40 cycles
(run_variantB.log) β the deterministic KLD run is the proof.
Run (patched, fix.diff applied, kernel rebuilt)
Same baseline_run.sh; expected DIFFERENCE:
DF3169 W: trigger returned ... (ticks=..., dounmount_ticks=<same or later>) # ordering flipped DF3169: SUMMARY ... dangling=0 # no post-free request # and NO "WRITE-AFTER-FREE" POLL lines
Fix
fix.diff (v2) β per-mount lifetime references: struct autofs_mount
gains am_dying/am_triggers; autofs_nresolve/autofs_readdir/
autofs_nmkdir acquire a trigger reference for their whole body (ENXIO
if dying / force-reclaimed); autofs_unmount() marks the mount dying
before the request-drain loop and waits (bounded, EBUSY abort) for
in-flight triggers before freeing any node. No mnt_lock is taken
inside the vops β a first fix revision that used vfs_busy() was
found to deadlock (ncp-lock β mnt_lock vs dounmount's mnt_lock β
cache_inval ncp-lock; see FIX_NOTES.md / VERDICT.md) and was
replaced.
DF-3169 VERDICT β autofs umount -f vs in-flight trigger/mkdir UAF
Status: REPRODUCED (deterministic KLD harness on stock INVARIANTS
kernel; production-shaped userspace variant exercised 40 cycles).
Impact: kernel use-after-free (read+write) + NULL-deref panic variant
(memcorrupt bucket). Not a standalone unprivβroot chain: the free side
requires umount -f (root), the trigger side is any unprivileged
stat/ls under the automount β the realistic incident is root/cron
(automount -u) racing user churn, producing silent heap corruption or
a panic.
Root cause (path:line)
autofs_trigger()retry loop parks the calling thread withsc_lockdropped forautofs_retry_delayseconds between attempts, holding no reference of any kind on the mount (sys/vfs/autofs/autofs.c:451-453). The daemon-dispatch cv wait is equally unprotected but does keep a request on the TAILQ (which the unmount drains); the retry gap keeps nothing. The pre-trigger section ofautofs_trigger_vn()(test_fs_root+nlookup_fs_root, sys/vfs/autofs/autofs_vnops.c:155-165) is a second, narrower gap.autofs_unmount()assumes "All vnodes are gone β¦ no new triggerings" (sys/vfs/autofs/autofs_vfsops.c:209-212). Under MNT_FORCE that is false:vflush(FORCECLOSE)force-vgones vnodes even with outstanding references (sys/kern/vfs_mount.c:1165-1181) and re-homes them todummymount, so the in-flight vop keeps running; the 1 s-cadence request scan (autofs_vfsops.c:213-237) then observes an empty TAILQ during a trigger's retry gap and proceeds to free everyautofs_node(incl.objcache_put) and theautofs_mount(kfree) (autofs_vfsops.c:239-258).- The parked thread wakes and its next
autofs_trigger_one()call runs wholly on freed memory: - readsanp->an_parent/an_mount,autofs_path()walks the freed mount (autofs.c:312-322); - copiesamp->am_from/am_prefix/am_optionsinto the new request (autofs.c:351-356) β reads from the freed/reused 4288-byte block; - inserts that request withar_mount =freed amp (autofs.c:345,362); - on each failureanp->an_retries++(autofs.c:441-445); - if the daemon answers success it also writesanp->an_cached/an_wildcardsand armscallout_reset(&anp->an_callout, autofs_cache*hz, β¦)β a 600 s callout installed in freed memory (autofs.c:409-413). autofs_nmkdir()variant: after force-vgonedvp->v_mount == &dummymount, soamp = VFSTOAUTOFS(dvp->v_mount)isdummymount.mnt_data == NULLandmtx_lock_ex_quick(&->am_lock)(autofs_vnops.c:251,263) faults β guaranteed NULL-offset panic once the interleaving hits (Β΅s window; not hit in 40 probabilistic cycles, code-trace certain).
Reproduction β baseline (run.log, decisive lines)
Stock X86_64_GENERIC (INVARIANTS) DragonFly 6.5-DEVELOPMENT #0.
Harness df3169_race.ko: kthread W in the REAL autofs_trigger() on
the mount's root node (sysctls: timeout=1, retry_attempts=12,
retry_delay=2, interruptible=0, debug=0), load thread runs the REAL
dounmount(mp, MNT_FORCE, 0) then immediately re-allocates the freed
blocks (same CPU/magazine β SAME-BLOCK for both, verified) β amp filled
with 0x41, node re-gotten from autofs_node_objcache (zeroed):
DF3169: dounmount returned 0 (ticks=15842) -- anp=0xfffff8008d3cd700 amp=0xfffff8008d4be800 ARE FREED NOW
DF3169: reuse blocks: amp 0xfffff8008d4be800 (freed amp was 0xfffff8008d4be800) SAME-BLOCK,
node 0xfffff8008d3cd700 (freed anp was 0xfffff8008d3cd700) SAME-BLOCK
DF3169: DANGLING REQUEST ar=0xfffff80089942e00 id=1 ar_mount=0xfffff8008d4be800 == FREED amp
DF3169: ar_from="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"... <- READ-AFTER-FREE (marker bytes)
DF3169: ar_path="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...
DF3169 POLL: freed anp=... an_retries: 1 -> 2 (ticks=16047) <== WRITE-AFTER-FREE
... ten such transitions through ticks=18744 (all > dounmount_ticks=15842)
DF3169 W: trigger returned 60 (ticks=19042, dounmount_ticks=15842)
DF3169: SUMMARY ... dangling=1 last_request_id 0 -> 12
Reads, writes, and the post-free request insertion are all directly
observed; ar_from containing the 0x41 marker proves the freed amp
block was read after free and after reallocation by unrelated
allocation β in production those bytes go to the automount daemon via
AUTOFSREQUEST (kernel-heap info leak of reused memory, root-gated
reader) and the writes land in whichever kernel object gets the 256 /
4288-byte blocks (silent corruption; an_wildcards is
daemon-controlled and callout_reset corrupts callout state).
A first harness revision (with vfs.autofs.debug=2) additionally
PANICKED the kernel (panic.txt: Fatal trap 12 β¦ fault va 0x5c β¦
Stopped at autofs_trigger+0x28c) β that specific fault is a harness
artifact (kthread curproc == NULL in the AUTOFS_WARN of
autofs.c:386-388; real triggers run in process context) but it
incidentally proves the predicted interleaving: the drain loop
ENXIO-completed W's request mid-dounmount, exactly as traced. The
final harness (debug=0, macro body skipped) proceeds past it.
Variant B (fake_daemon.c + variantB.sh): real userspace automountd
stand-in (58 requests handled, mkdir storms) Γ 40 mount/umount -f
cycles β no panic in 40 cycles (run_variantB.log); the Β΅s-wide
nmkdir window is probabilistic. The corruption variant is the one that
fires reliably (KLD run above).
Escalation assessment (primary objective)
The free side of the race requires MNT_FORCE unmount β privilege
gate PR:H. Within that gate the primitive is strong: repeated 4-byte
increment at a controlled-ish freed offset, two daemon-influenced bool
writes, callout-state corruption, and a request structure whose five
MAXPATHLEN string fields are filled from post-free heap β but the
attacker who can run umount -f is already root, so there is no
unprivβuid0 chain here. Honest ceiling: kernel heap corruption / panic
via a realistic root+user race (e.g. cron automount -u).
Fix validation (see fix_run.log / fix_build.log)
Two fix revisions were built and tested:
- v1 (REJECTED β deadlocks): wrapping the vop bodies in
vfs_busy()/vfs_unbusy(). Empirically the patched kernel HUNG insidedounmount()(harness never returned; guest wedged, serial console stopped after "calling dounmount(MNT_FORCE)"). Root cause (code analysis):VOP_NRESOLVEruns with the namecache entry locked (cache_resolve(), sys/kern/vfs_cache.c:4285), so the vop taking mnt_lock-shared creates the order ncp-lock β mnt_lock whiledounmount()does mnt_lock(EX) β cache_inval() ncp-locks (sys/kern/vfs_syscalls.c:842β:897) β an AB-BA cycle; a second cycle exists vianlookup()'s innervfs_busy()which tsleeps until dounmount finishes. SeeFIX_NOTES.md. - v2 (SHIPPED, fix.diff β VALIDATED):
struct autofs_mountgainsam_dying/am_triggers; the three racing vops hold a trigger reference for their whole body (ENXIO fast-fail when dying or force-reclaimed);autofs_unmount()marks the mount dying before the request-drain loop and waits β bounded, aborting with EBUSY β for in-flight triggers before freeing anything. Nomnt_lockis taken inside the vops, so neither cycle can form.
Identical harness on the v2-patched kernel (fix_run.log):
DF3169: churn trigger observed (ticks=9476); calling dounmount(MNT_FORCE) DF3169: dounmount returned 0 (ticks=12571, dounmount_ticks=12571) <- waited ~31 s for the drain DF3169: SUMMARY dounmount_ticks=12571 dangling=0 write_after_free_transitions=0 DF3169: CLEAN - no post-free access (fix active?)
vs baseline run.log (same harness, stock kernel): dounmount returned
in ~1.3 s with dangling=1, 6 write-after-free transitions,
UAF CONFIRMED. Production-shaped stress on the patched kernel
(fix_variantB.log): 6/6 mountβtrigger+mkdir-stormβumount -f cycles
clean (the fake daemon handled requests idβ₯152 through the fixed path),
no panic, no hang; functional sanity (sanity.sh): normal
trigger-timeout behavior and plain umount preserved.
Residual (documented, sub-microsecond): the ventry pair
(v_flag & VRECLAIMED check / v_mount->mnt_data read) can still race
a concurrent force-vgone of the same still-referenced vnode; fully
closing it needs a mount-pinning primitive legal with the ncp lock held
(vfs-core work for upstream). All seconds-wide windows demonstrated by
this finding are closed.
Negative results / notes
panic.txtdocuments the artifact panic; not counted as product impact.- Variant B: 40 cycles no panic β nmkdir window Β΅s-wide.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
struct autofs_mount refcount fix (DF-3169 fix v2)
fix v1 (vfs_busy/vfs_unbusy wrapped around the vop bodies) DEADLOCKS:
-
VOP_NRESOLVE is called by cache_resolve() with the namecache entry (ncp) locked exclusively (sys/kern/vfs_cache.c:4285), so taking mnt_lock shared inside the vop creates the order
trigger thread: ncp-lock -> mnt_lock(shared) dounmount: mnt_lock(EX) -> cache_inval() ncp-locks (sys/kern/vfs_syscalls.c:842 then :897)a classic AB-BA cycle. It also cycles a second way: the post-trigger nlookup_fs_root() crosses back into the autofs mount, and nlookup's inner vfs_busy() tsleeps until dounmount finishes while dounmount waits for the vop to drop its busy reference. Empirically confirmed: the patched kernel hung inside dounmount() (see VERDICT.md).
fix v2 keeps mnt_lock OUT of the vops entirely:
- struct autofs_mount gains
bool am_dyingandint am_triggers. - autofs_nresolve/autofs_readdir/autofs_nmkdir acquire a trigger reference at entry (fail ENXIO if the mount is dying or the vnode was already force-reclaimed) and release it at exit.
- autofs_unmount() marks the mount dying FIRST (new triggers refuse), keeps the existing request-drain loop, then waits for am_triggers to hit zero -- with a bounded wait that ABORTS the unmount with EBUSY (so a trigger thread sleeping in an inner vfs_busy() during dounmount can never deadlock the unmount; the admin retries).
- Only then are the nodes and the autofs_mount freed.
Residual (documented, sub-microsecond): the ventry pair (v_flag&VRECLAIMED check / v_mount->mnt_data read) races a concurrent force-vgone of the still-referenced vnode. Fully closing it needs a mount-pinning primitive that is legal with the ncp lock held (vfs core work); the seconds-wide UAF windows demonstrated by DF-3169 are gone.
Fix verification
fixedFIXED. Baseline on stock kernel #0: dangling=1, 6 write-after-free transitions, UAF CONFIRMED. First fix revision (vfs_busy wrap) DEADLOCKED the guest inside dounmount (ncp-lock->mnt_lock AB-BA vs dounmount's mnt_lock->cache_inval; nlookup inner vfs_busy second cycle) - rejected, documented in FIX_NOTES.md. Shipped fix.diff v2 (per-mount am_dying/am_triggers references; autofs_unmount marks dying, drains, bounded-waits for triggers with EBUSY abort before freeing): identical harness returns CLEAN with dangling=0 and zero post-free transitions while dounmount correctly waits ~31 s for the in-flight trigger; 6/6 production-shaped umount -f cycles clean; normal autofs trigger-timeout and plain umount behavior preserved (sanity.sh). Residual sub-us entry race (VRECLAIMED check vs v_mount->mnt_data read under concurrent force-vgone) documented for upstream.
fix_run.log (CLEAN summary), fix_variantB.log (6/6 cycles), fix_build.log (nativekernel RC=0 + installkernel IRC=0 + uname #1), FIX_NOTES.md (v1 deadlock), sanity.sh results in VERDICT.md
Confirmed kernel references
Detail
Exploit chain
unprivileged churn: stat /autofs_mp/key (VOP_NRESOLVE -> autofs_trigger, sleeps seconds) || root: umount -f /autofs_mp -> vflush(FORCECLOSE) force-vgones referenced vnodes -> request-drain scan sees empty TAILQ during trigger's 2 s retry gap -> kfree(autofs_mount) + objcache_put(all nodes) -> trigger wakes -> autofs_trigger_one reads freed amp (strlcpy into daemon-visible request: heap-info leak of whichever object reuses the block), writes anp->an_retries/an_cached/an_wildcards and arms &anp->an_callout in freed memory, inserts request with dangling ar_mount; nmkdir variant NULL-deref panics. Primitive: repeated 4-byte increment + two bool writes + callout-state corruption at type-stable freed offsets; escalation to uid0 not applicable (attacker side already root).
Evidence (decisive lines)
['run.log: \'DF3169: dounmount returned 0 (ticks=8296) ... ARE FREED NOW\' then \'DANGLING REQUEST ar=... ar_mount=0xfffff8008d214600 == FREED amp\', \'ar_from="AAAA..."\', 6x \'POLL ... <== WRITE-AFTER-FREE\', \'SUMMARY dangling=1 write_after_free_transitions=6 ... UAF CONFIRMED\'', 'run_kthread.log: same markers via direct kthread call into the real autofs_trigger (SAME-BLOCK reuse proven for both freed objects)', 'panic.txt: Fatal trap 12 in autofs_trigger+0x28c during dounmount (interleaving proof; NULL itself is a kthread-curproc harness artifact)', "fix_run.log: identical harness on fix-v2 kernel -> 'CLEAN - no post-free access', dangling=0, write_after_free_transitions=0", 'fix_variantB.log: 6/6 production-shaped umount -f cycles clean on patched kernel; run_variantB.log: 40 cycles unpatched (no panic, us window)', 'FIX_NOTES.md: fix v1 (vfs_busy) deadlock analysis (ncp-lock->mnt_lock vs dounmount mnt_lock->cache_inval)']
PoC changes
Harness evolved through 3 revisions: v1 kthread direct-call (panicked in AUTOFS_WARN curproc NULL - artifact, kept as panic.txt); v2 kthread + vfs.autofs.debug=0 + same-CPU freed-block reallocation probes (marker 0x41 amp / zeroed node) - deterministic silent-UAF proof; v3 (final) drives the trigger through the REAL VOP path (userspace stat churn) so the fix could be validated at the vop boundary. sysctls: timeout=1 retry_attempts=12 retry_delay=2 interruptible=0(kthread)/1(vop).
Verified recommended fix
Per-mount trigger references: am_dying/am_triggers in struct autofs_mount; acquire across autofs_nresolve/autofs_readdir/autofs_nmkdir bodies; autofs_unmount marks dying before the request-drain loop and bounded-waits (EBUSY abort) for am_triggers==0 before freeing nodes - no mnt_lock inside the vops (vfs_busy there deadlocks).
Verdict
REPRODUCED on stock INVARIANTS X86_64_GENERIC: a forced unmount (dounmount MNT_FORCE -> autofs_unmount frees every autofs_node and the autofs_mount after a 1 Hz request-drain scan, autofs_vfsops.c:195-261) races any in-flight autofs trigger (stat/getdents under the mountpoint). The trigger's retry loop parks 1-2 s with no reference on the mount (autofs.c:451-453); the unmount's scan hits that gap, frees everything, and the trigger's next autofs_trigger_one() run executes wholly on freed memory. Deterministic KLD-orchestrated VOP-path run (run.log): post-free request inserted into the live sc_requests TAILQ with ar_mount == freed amp; ar_from/ar_path filled with 0x41 markers planted in the reallocated freed-amp block (read-after-free of a reused 4288-byte kernel heap object, delivered to the automountd via AUTOFSREQUEST); 6 observed an_p->an_retries write-after-free transitions on the freed/reallocated 256-byte node block; plus autofs.c:409-413 can write an_cached/an_wildcards and arm a 600 s callout inside freed memory. autofs_nmkdir variant is a guaranteed NULL-deref panic (dummymount.mnt_data == NULL -> mtx_lock on NULL+offset) with a us window (code-trace certain; 40 probabilistic userspace cycles did not hit it). Not an unpriv->root chain: the free side requires root's umount -f; realistic incident is root/cron automount -u racing unprivileged stat churn -> kernel heap corruption or panic.
No comments yet.