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

Short-buffer read() on /dev/udev silently drops the event and reports ENOMEM

Field Value
ID DF-2807
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:N
CWE CWE-404 Improper Resource Shutdown (event loss)
File sys/kern/kern_udev.c
Lines 842-860
Area kern
Confidence certain
Discovered 2026-08-31
Pass 2 (GLM 5.3 second pass)
Bucket base:kern
Reported pending
Known CVE none
CVE match novel

Summary

When uio_resid < strlen(xml)+1 the handler sets error = ENOMEM but still advances the reader marker past the event and reaps it: the event is consumed and destroyed without being delivered β€” silent event loss to a reader issuing a too-small read(), plus a misleading errno. Root-gated; consumers using small buffers would silently miss hotplug events. Fix: on the short-buffer path break WITHOUT moving the marker and return EMSGSIZE so the reader can retry with a larger buffer.

Timeline

  • 2026-08-31 Discovered during pass-2 audit of kern_udev.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2807 Β· 10 files
FileTypeDescriptionSize
README.md β€” 714 B ↓ raw
VERDICT.md β€” 1.6 KB ↓ raw
build.sh β€” 69 B view raw
env.txt β€” 509 B view raw
fix.diff β€” 1.3 KB view raw
manifest.json β€” 558 B view raw
run.log β€” 544 B view raw
run.sh β€” 95 B view raw
udev_short.c β€” 2.5 KB view raw
verdict.json β€” 2.7 KB view raw

DF-2807 β€” udev short read drops event, reports ENOMEM (Info-tier hardening verification)

What

udev short read drops event, reports ENOMEM

Build

cc -Wall -O2 -o udev_short udev_short.c

Run

./udev_short (as root, timeout 45)

Expected

baseline (stock kernel): see verdict below fix kernel (all-41-fixes build): the bad behavior is gone

Files

README.md VERDICT.md build.sh env.txt fix.diff manifest.json run.log run.sh udev_short.c 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.

VERDICT.md
↓ download raw

DF-2807 β€” VERDICT

status: reproduced | reproduced: 1 | impact: denial_of_service | confidence: certain

Mechanically reproduced as root on the stock guest with a single /dev/udev reader (O_NONBLOCK): after the mandatory initiation read arms the marker (kern_udev.c:826-829), two distinctive events were generated (vnconfig attach family, h41 make_dev). A 1-byte read returned -1/ENOMEM AND consumed the first (564-byte) event: the follow-up full read delivered only the 824-byte second event - the first event was silently destroyed without delivery (kern_udev.c:846-860 advances the marker and reaps even on the short-buffer error). On the fix kernel the 1-byte read returns -1/EMSGSIZE (errno 40) and the first full read delivers the SAME 564-byte event first (verified against a no-short-read control sequence). Fix iteration: v1 (skip marker advance only) panicked the guest with prop_object_release ocnt!=0 because udev_event_externalize consumes the event dict reference (kern_udev.c:571) - v3 makes externalize non-consuming and moves the reference drop to the delivery point; validated stable across the full A/B suite.

Fix-validation panic (v1)

Fix v1 (skip marker advance only) panicked the guest during A/B: panic: assertion "ocnt != 0" failed in prop_object_release udev_dev_read() ... udev_event_externalize consumes the event dict reference (kern_udev.c:571), so a retried read double-releases. v3 makes externalize non-consuming and drops the reference at the delivery point; validated stable.

Baseline vs fix kernel

baseline: see verdict above / run log fix: behavior changed as designed (see verdict)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

A/B: baseline ENOMEM + silent event loss; fix kernel EMSGSIZE + event preserved. v1 fix double-release (prop_object_release panic) caught by fix validation and reworked to a non-consuming externalize.

↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Mon Oct 26 01:57:23 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (h41 fix kernel, all 41 hardening fixes applied)

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log baseline: short read errno=12 + 564-byte event never delivered; fix kernel: errno=40 + 564-byte event delivered first (control: ab0 sequence shows 564B event is the queue head)

PoC changes

reader must be initiated first (first read arms the marker; udev_initiated_count gates event queueing); O_NONBLOCK to avoid blocking drain reads; exact-name XML extraction

Verified recommended fix

Short buffer: kfree xml, EMSGSIZE, no marker advance; move the event dict release to the delivery point

Verdict

Mechanically reproduced as root on the stock guest with a single /dev/udev reader (O_NONBLOCK): after the mandatory initiation read arms the marker (kern_udev.c:826-829), two distinctive events were generated (vnconfig attach family, h41 make_dev). A 1-byte read returned -1/ENOMEM AND consumed the first (564-byte) event: the follow-up full read delivered only the 824-byte second event - the first event was silently destroyed without delivery (kern_udev.c:846-860 advances the marker and reaps even on the short-buffer error). On the fix kernel the 1-byte read returns -1/EMSGSIZE (errno 40) and the first full read delivers the SAME 564-byte event first (verified against a no-short-read control sequence). Fix iteration: v1 (skip marker advance only) panicked the guest with prop_object_release ocnt!=0 because udev_event_externalize consumes the event dict reference (kern_udev.c:571) - v3 makes externalize non-consuming and moves the reference drop to the delivery point; validated stable across the full A/B suite.