β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2961

kobj_class_free() reclaims the compiled ops table with no grace period while the lock-free dispatch hot path reads it (force multiplier turning any consumer refcount slip into a freed function-pointer table call)

Field Value
ID DF-2961
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE CWE-362 / CWE-416
File sys/kern/subr_kobj.c
Lines 179-208 (dispatch :184-188; kfree :206; publish :260)
Area kern/kobj
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket memcorrupt
Reported pending
Known CVE none
CVE match novel

Summary

Every KOBJOPLOOKUP dispatch executes kobj_lookup_method_cache() completely unsynchronized: it reads OPS->cls, cep[desc->id & 255], ce->desc and finally indirect-calls ce->func, and the slow path even WRITES the cache slot. Meanwhile kobj_class_uninstantiate() drops the last reference and kobj_class_free() immediately kfree()s the 2056-byte ops table holding only kobj_token β€” a lock the dispatch path never takes. kobj_init() likewise publishes obj->ops = cls->ops with no release/acquire discipline relative to a concurrent free. kobj therefore provides zero read-side lifetime protection: any consumer that drops the class reference even transiently while another CPU is mid-dispatch (exactly the unprivileged sysctl-reader-vs-device-teardown race already proven as DF-2679 against subr_bus) turns a would-be benign stale read into a UAF walk of freed slab memory culminating in an indirect call through reclaimed data β€” RIP control if the attacker grooms the freed 2056-byte block (/dev/dsp open/close cycles legitimately compile/free channel-class ops at attacker-influenced timing). No in-tree caller misbalances refs by itself β€” newbus, libiconv, and PCM sound are balanced β€” which is why this is filed Low/defense-in-depth: the kobj layer is the multiplier, the trigger is a consumer lifetime bug. No standalone PoC exists or should exist at the kobj layer (manufacturing the primitive requires a buggy demonstration driver; verification deferred to the consumer-side DF-2679 which owns the reachable trigger). Fix: grace period or never-free static classes (row sketch).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of subr_kobj.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2961 Β· 5 files
FileTypeDescriptionSize
README.md β€” 606 B ↓ raw
VERDICT.md β€” 1.9 KB ↓ raw
env.txt β€” 401 B view raw
manifest.json β€” 489 B view raw
verdict.json β€” 1.7 KB view raw

DF-2961 PoC evidence pack

What

kobj dispatch is unsynchronized against kobj_class_free (force multiplier for consumer refcount bugs).

Build

(none)

Run

(none)

Expected

static confirmation (unsynchronized dispatch vs immediate kfree; no standalone trigger)

Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 All logs are full untrimmed captures unless noted. fix.diff is the git-apply-able verified fix (authored after verification; never applied to the audit tree).

VERDICT.md
↓ download raw

DF-2961 - verification verdict

Status: not_reproduced | reproduced: 0 | impact: corruption | confidence: certain

Confirmed as a force multiplier, not a standalone bug - matching the findings own framing. kobj_lookup_method_cache dispatch (subr_kobj.c:181-188 via KOBJOPLOOKUP) is fully unsynchronized (reads cls->ops, cache slot, ce->desc, indirect-calls ce->func; even WRITES the cache slot on miss at :169), while kobj_class_free kfrees the 2056-byte ops table holding only kobj_token (:206) - a lock the dispatch path never takes - and kobj_init publishes obj->ops with no ordering (:260). All in-tree consumers keep balanced references (subr_bus, libiconv, PCM sound), so no standalone trigger exists; the reachable trigger is consumer-side (DF-2679 family). Fix is architectural (grace period or never-free static classes); no patch compiled - documented per verification contract.

Evidence pointers

subr_kobj.c:181-188 unsynchronized dispatch vs :206 immediate kfree under kobj_token only

Kernel references

PoC changes vs the filing sketch

verification deferred to consumer-side DF-2679 per finding text

Fix

recommendation: grace period or never-free static classes

fix status: not_testable

architectural; no patch (documented)

Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable

architectural; no patch (documented)

per-fix-DF-2961

Confirmed kernel references

Detail

Evidence (decisive lines)

subr_kobj.c:181-188 unsynchronized dispatch vs :206 immediate kfree under kobj_token only

PoC changes

verification deferred to consumer-side DF-2679 per finding text

Verified recommended fix

grace period or never-free static classes

Verdict

Confirmed as a force multiplier, not a standalone bug - matching the findings own framing. kobj_lookup_method_cache dispatch (subr_kobj.c:181-188 via KOBJOPLOOKUP) is fully unsynchronized (reads cls->ops, cache slot, ce->desc, indirect-calls ce->func; even WRITES the cache slot on miss at :169), while kobj_class_free kfrees the 2056-byte ops table holding only kobj_token (:206) - a lock the dispatch path never takes - and kobj_init publishes obj->ops with no ordering (:260). All in-tree consumers keep balanced references (subr_bus, libiconv, PCM sound), so no standalone trigger exists; the reachable trigger is consumer-side (DF-2679 family). Fix is architectural (grace period or never-free static classes); no patch compiled - documented per verification contract.