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 | 142 B | view raw |
| run.sh | run-script | ./harness | 41 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.4 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 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.