bus_dmamem_coherent reports success when the underlying bus_dmamap_load fails (error swallowed in callback), yielding dmem_busaddr==0
| Field | Value |
|---|---|
| ID | DF-2981 |
| 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-252 / CWE-755 |
| File | sys/kern/subr_busdma.c |
| Lines | 46-47, 79-94 (contract: busdma_machdep.c:804-806) |
| Area | kern/busdma |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | memcorrupt |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
bus_dmamem_coherent() relies solely on bus_dmamap_load()'s return value to detect load failure. Per the busdma contract, bus_dmamap_load() returns 0 whenever the callback was invoked synchronously β the load error is delivered ONLY through the callback argument. The glue's callback, _bus_dmamem_coherent_cb(), discards that error. Therefore any synchronous load failure (EFBIG/EINVAL) would leave bus_dmamap_load()==0, and bus_dmamem_coherent() would return 0 (success) with dmem->dmem_busaddr==0 from the initial bzero β the caller then programs its device to DMA at physical address 0 while the CPU accesses the real allocation, and the map/tag are handed out in a never-loaded state. Latent only: with the current x86_64 MD allocator the failing-load branch is provably unreachable (tag created with nsegments=1 and maxsegsz==maxsize so the buffer can never split; the contigmalloc path fails cleanly with ENOMEM for boundary-constrained allocations). If the MD loader semantics ever change, or a future caller passes a load-failing configuration, a driver would silently DMA at bus address 0 β device-controlled writes to physical page 0. Filed as hardening so the fragile contract is fixed before it becomes memory corruption. Fix: capture the load error through the callback argument (row diff).
Timeline
- 2026-09-02 Discovered during pass-2 audit of subr_busdma.c (GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2981 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 245 B | β raw | |
| fix.diff | β | 1.3 KB | view raw | |
| manifest.json | β | 428 B | view raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-2981 evidence pack
What
bus_dmamem_coherent swallows synchronous bus_dmamap_load callback errors
Verdict
status=not_reproduced impact=none confidence=certain
See VERDICT.md for the full narrative and fix.diff for the verified fix.
Fix verification
not_testablefix.diff applied in the F40FIX verification kernel build (RC=0, zero -Werror errors); no runtime A/B for this item (line-certain hardening)
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md with path:line citations; run.log where a live demo exists; fix.diff
PoC changes
n/a - no PoC (unreachable branch); verification by source trace + compile validation
Verified recommended fix
propagate the synchronous callback error through a carg struct; bus_dmamem_coherent fails and frees on carg.error
Verdict
Line-accurate confirmation, fix compile-validated; runtime demonstration deliberately not attempted - the failing-load branch is provably unreachable with the current x86_64 MD allocator (tag is created with nsegments=1 and maxsegsz==maxsize so a legal buffer cannot split; subr_busdma.c:64-67). The defect is certain from source: the glue callback _bus_dmamem_coherent_cb returns early on error without recording it (subr_busdma.c:46-47), while the MD bus_dmamap_load always returns 0 for synchronous loads, delivering the error only through the callback (busdma_machdep.c: callback(...error); return 0). bus_dmamem_coherent therefore returns 0 (success) with dmem_busaddr==0 and a never-loaded map if a load ever fails - the caller programs its device to DMA at physical address 0. Fix (carg.error propagation) compiled in the combined F40FIX kernel build.
No comments yet.