VIA/JMicron metadata writeback persists low 32 bits of kernel device_t pointers into on-disk metadata
Summary
ata_raid_jmicron_write_meta stores (u_int32_t)(uintptr_t)rdp->disks[disk].dev into meta->disks[] when a member has no serial (true for every ioctl-created array, since ata_raid_create M_ZEROes the softc and never sets serials), and ata_raid_via_write_meta does it unconditionally for all members. The value is inside the checksummed block written to each member disk and re-parsed later - a durable, reboot-surviving disclosure of live kernel pointer bits. Vendor-gated (JMicron/VIA PCI IDs) so not runnable on this guest: inconclusive, source certain (straight-line dataflow).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3694 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.6 KB | β raw | |
| VERDICT.md | β | 2.0 KB | β raw | |
| analysis.txt | β | 1.8 KB | view raw | |
| fix.diff | β | 3.6 KB | view raw | |
| verdict.json | β | 1.7 KB | view raw | |
| manifest.json | β | 2.1 KB | view raw |
DF-3694 β VIA / JMicron metadata writeback persists kernel pointer bits on disk
Root cause (source)
Both vendor write paths store truncated kernel pointers as on-disk disk IDs:
2736 (jmicron): meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev;
3986 (via): meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev;
- JMicron (
:2732-2737): used when a member disk has no stored serial β i.e. for every ioctl-created array (ata_raid_create never populates serials). - VIA (
:3985-3986): used unconditionally for every member.
The metadata block (checksummed) is then written to each member disk
(:2753-2756, :4005-4008). Anyone who can later read raw sectors of the
member disk (operator group on the raw /dev/adX node, physical access,
cloud image inspection) recovers the low 32 bits of live kernel device_t
pointers β defeating heap/KASLR randomization to that extent
(CWE-200/524). Writeback happens on array state changes (see DF-3693
triggers), including the boot-time 0βREADY transition and
atacontrol-driven events.
Guest testability
VIA and JMicron formats are only parsed on matching PCI vendors
(ata_raid_read_metadata:1433-1435, :1460-1462); this guest has Intel IDE
only β inconclusive (cannot instantiate the format on this hardware).
Source analysis is certain: the pointerβu32βdisk dataflow has no
conditional to argue about.
Fix
fix.diff replaces both with karc4random() (no kernel-derived value is
persisted; the on-disk ID is only matched against itself on re-parse).
Files
analysis.txt, fix.diff, verdict.json
DF-3694 VERDICT
Status: inconclusive (runtime) / certain (source). impact: leak (partial pointer disclosure; not demonstrated at runtime).
Why inconclusive at runtime
VIA and JMicron formats are vendor-gated (ata-raid.c:1433-1435, :1460-1462).
The guest has no VIA/JMicron controller, so neither write path can execute
here. The JMicron/VIA write functions are only dispatched from
ata_raid_write_metadata for arrays of those formats β no other caller.
Why the source claim is certain
:2736meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev;(JMicron, executed when serial[0]==0 β true for every ioctl-created array sinceata_raid_createkmallocs with M_ZERO and never sets serials) and:3986(VIA, unconditional for all members).- The value lands inside the checksummed metadata block that
ata_raid_rw(..., ATA_R_WRITE|ATA_R_DIRECT)persists to each member disk (:2753-2756,:4005-4008). - On-disk it is directly recoverable: read the metadata sector, subtract
the known template fields β low 32 bits of a live kernel
device_t. - No conditional logic is involved; the dataflow is straight-line.
Impact ceiling
32 bits of a kernel heap pointer per array writeback β meaningful KASLR / heap-randomization erosion on systems where the pointer's high bits are otherwise known, and a durable (survives reboot) leak into disk images. Read access to member disk raw sectors is required (root / devfs-permissive operator / physical / image inspection). Low severity overall.
Fix
Replace both assignments with karc4random() (in fix.diff; not runtime
validated on this guest for the hardware reason above β fix_status =
not_testable).
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.
Fix verification
not_testableconstant substitution included in fix.diff; not runtime-validatable on this guest
fix.diff hunks '@@ -2733,7 +2760,7 @@' and '@@ -3983,7 +4010,7 @@'
Confirmed kernel references
Detail
Evidence (decisive lines)
analysis.txt (full source trace incl. round-trip: leaked value is checksummed and re-parsed)
PoC changes
n/a
Verified recommended fix
store karc4random() instead of a truncated kernel pointer (fix.diff hunks 8+9)
Verdict
ata_raid_jmicron_write_meta (:2736, fires whenever a member has no serial - true for every ioctl-created array) and ata_raid_via_write_meta (:3986, unconditional) store (u_int32_t)(uintptr_t)rdp->disks[disk].dev - low 32 bits of live kernel device_t pointers - into checksummed on-disk metadata that is persisted on every config writeback. Straight-line dataflow, no conditionals; source-certain. Runtime test impossible here: both formats are PCI-vendor-gated (JMicron/VIA) and the guest has Intel IDE only.
No comments yet.