METEORSETGEO + METEORSACTPIXFMT pixel-format mismatch yields OOB DMA write and OOB uiomove heap info-leak
Summary
METEORSETGEO at bktr_core.c:1657 allocates bigbuf = rows*cols*frames*2 (YUV_422 Bpp=2). METEORSACTPIXFMT at :2328 switches bktr->pixfmt to RGB 4Bpp WITHOUT resizing bigbuf. video_read at :1084: count=rows*cols*pixfmt_table[new].Bpp (=2x buffer) then uiomove(bigbuf,count,uio) -> OOB heap read. start_capture bzero at :3519 uses new Bpp -> OOB DMA write. RISC DMA builder uses pitch=cols*Bpp(new) -> OOB DMA write past bigbuf. Max over-read/write ~4MB past buffer end. Unprivileged (mode 0444). Fix: verify rows*cols*new_Bpp<=alloc_pages*PAGE_SIZE in METEORSACTPIXFMT, clamp count in video_read.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1216 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | self-contained reproduction of the cited OOB arithmetic | 4.7 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -o harness harness.c | 65 B | view raw |
| run.sh | run-script | ./harness | 89 B | view raw |
| build.log | build-log | final successful build (in-guest cc) | 13 B | view raw |
| run.log | run-log | decisive harness run printing OOB=4177936 | 286 B | view raw |
| env.txt | environment | uname + compiler + PCI inventory | 543 B | view raw |
| fix.diff | suggested-fix | add METEORSACTPIXFMT Bpp-vs-bigbuf bounds check | 962 B | view raw |
| fix_build.log | fix-build-log | patched nativekernel build, rc=0 | 5.6 MB | β download |
| VERDICT.md | verdict | full narrative | 4.8 KB | β raw |
| poc_bpp_mismatch.c | trigger-source-orig | orchestrator's draft device-PoC (no /dev/bktr0 on guest) | 1.6 KB | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1216 β METEORSETGEO + METEORSACTPIXFMT pixel-format mismatch
Verdict
REPRODUCED (source-level harness). The bug is real and the OOB read/write
arithmetic is confirmed; impact ceiling is OOB kernel-heap read (read()) +
OOB kernel-heap write (start_capture bzero/RISC DMA). The kernel code path
cannot be triggered on this audit guest because QEMU does not emulate the
Brooktree Bt848 PCI video-capture device β /dev/bktr0 does not exist and the
PCI vendor/device IDs (109e:0350/0351) are absent from pciconf -lv. The
fix.diff applies cleanly to the in-guest source tree and nativekernel
succeeds (rc=0); no run-time exercise of the path is possible on this guest
because the device is missing.
Mechanism (cited path:line, confirmed against sys/dev/video/bktr/bktr_core.c)
- METEORSETGEO at
bktr_core.c:1657computes the bigbuf allocation astemp = geo->rows * geo->columns * geo->frames * 2. The literal* 2hard-codes YUV_422 (Bpp=2). The buffer is mapped intoalloc_pagespages. - METEORSACTPIXFMT at
bktr_core.c:2328-2337accepts any index0..11from the user, assignsbktr->pixfmt = *(int *)arg, and returns. It does not verify that the new format'sBppis compatible with the already- allocated bigbuf, and does not reallocate. - video_read at
bktr_core.c:1084-1085computescount = rows * cols * pixfmt_table[bktr->pixfmt].public.Bpp. With pixfmt=5 (RGB 4Bpp frompixfmt_table[5]atbktr_core.c:230) and the YUV-sized buffer,countis 2Γ the buffer size. - uiomove at
bktr_core.c:1106then readscountbytes out ofbigbuf, reading up tocount - alloc_pages*PAGE_SIZEbytes past the end (OOB heap read β kernel-memory info leak). - start_capture at
bktr_core.c:3519-3524doesbzero(bigbuf, rows*cols*frames*pixfmt_table[bktr->pixfmt].Bpp)and the RISC DMA builder usespitch = cols*Bpp(new)β OOB heap write (up to ~4 MiB past buffer end).
The attacker needs only an open bktr fd (the device is cr--r--r--
world-readable per the finding, although the open may further be gated by
caps_priv_check_self); the two ioctls + read() sequence is fully unprivileged
on real hardware.
Harness proof (harness.c)
Extracts the exact pixfmt_table[] from bktr_core.c:220-238 and the exact
arithmetic from lines 1657, 2333, 1084, 3522, and prints the OOB length:
After METEORSETGEO(YUV_422): bigbuf=4186112 bytes alloc_pages=1022
video_read OOB=4177936 bytes start_capture OOB=4177936 bytes
CONFIRMED: read() reads 4177936 bytes past bigbuf end (heap leak);
start_capture bzero/DMA writes 4177936 bytes past bigbuf end (heap corruption)
Reproduced deterministically over 3 runs.
Exploit-chain note
This is a write-capable primitive on real hardware. Because the audit guest
lacks the Bt848 device, no kernel-side chain can be developed here. The OOB
write is large (~4 MiB), so a successful trigger on real HW would corrupt
adjacent kernel heap and yield a reliable DoS at minimum; exploitation
(strings-into-heap would require additional research but the offset is
attacker-tunable via rows/cols/pixfmt). Documented here as a primitive
characterization only; impact ceiling for this run is heap-corruption DoS /
info-leak.
PoC changes
- Replaced the original
poc_bpp_mismatch.c(which attempts to open/dev/bktr0that does not exist on this guest) withharness.c, a self-contained userland reproduction of the cited arithmetic. - Added
fix.diff,build.sh,run.sh,env.txt,VERDICT.md,manifest.json, fullbuild.log/run.log/fix_build.log.
Fix
fix.diff adds the missing bounds check inside METEORSACTPIXFMT:
bktr->rows * bktr->cols * bktr->frames * pixfmt_table[idx].Bpp must not
exceed alloc_pages * PAGE_SIZE when bigbuf != 0. This is the targeted
root-cause fix; it supersedes the finding markdown's "clamp count in video_read"
note by closing the bug at the actual configuration-change point so neither
video_read nor start_capture can ever see a too-large Bpp.
Fix-validation
The diff applies cleanly (patch -p1 --forward, hunk #1 succeeded at line
2330) and make -j6 nativekernel KERNCONF=X86_64_GENERIC completes with
rc=0 (fix_build.log). The kernel-side before/after run cannot be
demonstrated on this guest because the device is absent, so fix_status:
"not_testable" β the diff is verified to compile and the changed logic is
traced to close the cited code path.
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_testablenot_testable because the bt848 video-capture device is absent from the audit guest so the in-kernel PoC cannot be run; validated that fix.diff applies cleanly (patch -p1 --forward, hunk #1 at line 2330) and that the single-fix nativekernel compiles with rc=0 (fix_build.log). Traced the changed logic to close the cited overflow at the configuration-change point.
baseline (harness): video_read OOB=4177936 bytes / start_capture OOB=4177936 bytes patched kernel build: === NK_DONE rc=0 === (kernel compiles with new bounds check)
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- v
- i
- d
- e
- o
- /
- b
- k
- t
- r
- /
- b
- k
- t
- r
- _
- c
- o
- r
- e
- .
- c
- :
- 1
- 6
- 5
- 7
- s
- y
- s
- /
- d
- e
- v
- /
- v
- i
- d
- e
- o
- /
- b
- k
- t
- r
- /
- b
- k
- t
- r
- _
- c
- o
- r
- e
- .
- c
- :
- 2
- 3
- 0
- s
- y
- s
- /
- d
- e
- v
- /
- v
- i
- d
- e
- o
- /
- b
- k
- t
- r
- /
- b
- k
- t
- r
- _
- c
- o
- r
- e
- .
- c
- :
- 2
- 3
- 3
- 3
- s
- y
- s
- /
- d
- e
- v
- /
- v
- i
- d
- e
- o
- /
- b
- k
- t
- r
- /
- b
- k
- t
- r
- _
- c
- o
- r
- e
- .
- c
- :
- 1
- 0
- 8
- 4
- s
- y
- s
- /
- d
- e
- v
- /
- v
- i
- d
- e
- o
- /
- b
- k
- t
- r
- /
- b
- k
- t
- r
- _
- c
- o
- r
- e
- .
- c
- :
- 1
- 1
- 0
- 6
- s
- y
- s
- /
- d
- e
- v
- /
- v
- i
- d
- e
- o
- /
- b
- k
- t
- r
- /
- b
- k
- t
- r
- _
- c
- o
- r
- e
- .
- c
- :
- 3
- 5
- 2
- 1
Detail
Exploit chain
none (HW-gated): no bt848 device on the audit guest, so the kernel-side chain cannot be exercised. Primitive characterized via harness: a write-capable ~4 MiB OOB heap read (info leak) + OOB heap write (start_capture bzero/RISC DMA). Realistic ceiling on real HW: heap corruption DoS / kernel info leak; full escalation would require the bt848 device. Documented in harness.c and VERDICT.md.
Evidence (decisive lines)
After METEORSETGEO(YUV_422): bigbuf=4186112 bytes alloc_pages=1022 video_read OOB=4177936 bytes start_capture OOB=4177936 bytes CONFIRMED: read() reads 4177936 bytes past bigbuf end (heap leak); start_capture bzero/DMA writes 4177936 bytes past bigbuf end (heap corruption)
PoC changes
Replaced orchestrator's draft poc_bpp_mismatch.c (which tries to open /dev/bktr0 that does not exist on this guest) with harness.c β a self-contained userland reproduction of the exact cited arithmetic. Added fix.diff (METEORSACTPIXFMT Bpp-vs-bigbuf check), build.sh/run.sh, env.txt, full build/run/fix_build logs, VERDICT.md, manifest.json.
Verified recommended fix
fix.diff adds the missing bounds check inside METEORSACTPIXFMT (bktr_core.c:2333): compute need = rowscolsframespixfmt_table[idx].Bpp and reject with EINVAL when bigbuf!=0 && need > alloc_pagesPAGE_SIZE. This closes the bug at the configuration-change point so neither video_read nor start_capture can ever see a too-large Bpp. Supersedes finding proposal (which suggested clamping count in video_read).
Verdict
REPRODUCED at the source-logic level. bktr_core.c:1657 METEORSETGEO hardcodes bigbuf = rowscolsframes2 (YUV Bpp=2); bktr_core.c:2333 METEORSACTPIXFMT sets bktr->pixfmt=5 (RGB 4Bpp) with no realloc/bounds check; bktr_core.c:1084 video_read then computes count = rowscols*pixfmt_table[pixfmt].Bpp = 2x buffer; bktr_core.c:1106 uiomove reads 4177936 bytes past bigbuf end. start_capture bzero (bktr_core.c:3521) writes 4177936 bytes past bigbuf. Harness replicates the exact buggy arithmetic and prints OOB=4177936 bytes deterministically over 3 runs. The guest lacks a bt848 PCI video-capture device (no /dev/bktr0 in QEMU) so the in-kernel path cannot be triggered here; this is a harness proof.
No comments yet.