dev_is_good() tests a sentinel that is never installed: dead_dev_ops is assigned to no cdev, making every dead-device fence in the kernel dead code
| Field | Value |
|---|---|
| ID | DF-2914 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:N |
| CWE | CWE-477 / CWE-1075 |
| File | sys/kern/kern_conf.c |
| Lines | 127-132 (claims :336, :474) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
dev_is_good() returns 0 only when dev->si_ops == &dead_dev_ops. A tree-wide search shows dead_dev_ops (defined zero-filled at kern_device.c:83) is never assigned to any cdev's si_ops: the only si_ops writers are devfs_new_cdev (devfs_core.c:2459) and dev_ops_intercept/dev_ops_restore (kern_device.c:663/674 β console only). The comments at kern_conf.c:336 ('revectors its ops to &dead_dev_ops') and :474 document a revectoring that devfs_destroy_dev_worker never performs. Consequently the guards at vfs_subr.c:1215 (v_associate_rdev), vfs_subr.c:2531, and vfs_conf.c:452/522 can never reject a destroyed device. No direct exploit (sysref keeps the cdev struct alive, so dispatch hits still-mapped driver ops; the module-lifetime hazard is the already-filed DF-2896 family) β inert defense-in-depth plus a misleading API contract. Fix: test a dedicated SI_DESTROYED flag set by devfs_destroy_dev_worker (or actually implement the promised revectoring under devfs_lock).
Timeline
- 2026-09-02 Discovered during pass-2 audit of kern_conf.c (GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2914 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 762 B | β raw | |
| VERDICT.md | β | 1.0 KB | β raw | |
| build.sh | β | 50 B | view raw | |
| env.txt | β | 509 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| h41.c | β | 14.9 KB | view raw | |
| harness.log | β | 2.0 KB | view raw | |
| manifest.json | β | 576 B | view raw | |
| run.sh | β | 104 B | view raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-2914 β dev_is_good tests never-installed sentinel (Info-tier hardening verification)
What
dev_is_good tests never-installed sentinel
Build
cd /root/h41 && make
Run
kldload ./h41.ko; sysctl -w debug.h41.cmd=4
Expected
baseline (stock kernel): see verdict below fix kernel (all-41-fixes build): the bad behavior is gone
A/B one-liner: baseline: after-destroy=1 | fix: after-destroy=0
Files
README.md VERDICT.md build.sh env.txt fix.diff h41.c harness.log manifest.json run.sh verdict.json
Shared harness: h41.c (combined KLD, sysctl debug.h41.cmd=N driven).
Full batch context: the fix kernel contains all 41 hardening fixes of
this batch compiled together (build RC=0) and passed the complete A/B
suite plus a clean reboot.
DF-2914 β VERDICT
status: reproduced | reproduced: 1 | impact: none | confidence: certain
Mechanically reproduced via the harness on the stock guest: dev_is_good() returns 1 for a cdev AFTER destroy_dev + the devfs worker ran (with a held reference so the read is safe) - the function tests si_ops != &dead_dev_ops (kern_conf.c:127-132) but no code path ever installs dead_dev_ops (rg: definition + comments only; devfs_destroy_dev_worker never revectors), so every dead-device fence in the kernel (vfs_subr.c:1215/2531, vfs_conf.c:452/522, devfs_rules.c:298) is dead code and the kern_conf.c:336/:474 comments promise revectoring that never happens. No direct exploit: sysref keeps the cdev alive and dispatch reaches still-mapped driver ops (module-lifetime hazard is the DF-2896 family). On the fix kernel (SI_DESTROYED flag set in devfs_destroy_dev_worker, tested by dev_is_good) the same sequence returns 0.
Baseline vs fix kernel
baseline: baseline: after-destroy=1 | fix: after-destroy=0 fix: behavior changed as designed (see verdict)
Fix verification
fixedA/B: baseline after-destroy=1 (fence dead); fix kernel after-destroy=0 via SI_DESTROYED.
Confirmed kernel references
- sys/kern/kern_conf.c:127
- sys/kfs/../kern/kern_conf.c:336
- sys/vfs/devfs/devfs_core.c:1440
- sys/kern/vfs_subr.c:1215
Detail
Evidence (decisive lines)
harness.log T4: after-destroy=1; fix kernel T4: after-destroy=0
PoC changes
harness holds a cdev reference across destroy_dev and waits 2s for the async devfs worker
Verified recommended fix
Set an SI_DESTROYED flag in devfs_destroy_dev_worker and test it in dev_is_good
Verdict
Mechanically reproduced via the harness on the stock guest: dev_is_good() returns 1 for a cdev AFTER destroy_dev + the devfs worker ran (with a held reference so the read is safe) - the function tests si_ops != &dead_dev_ops (kern_conf.c:127-132) but no code path ever installs dead_dev_ops (rg: definition + comments only; devfs_destroy_dev_worker never revectors), so every dead-device fence in the kernel (vfs_subr.c:1215/2531, vfs_conf.c:452/522, devfs_rules.c:298) is dead code and the kern_conf.c:336/:474 comments promise revectoring that never happens. No direct exploit: sysref keeps the cdev alive and dispatch reaches still-mapped driver ops (module-lifetime hazard is the DF-2896 family). On the fix kernel (SI_DESTROYED flag set in devfs_destroy_dev_worker, tested by dev_is_good) the same sequence returns 0.
No comments yet.