radeon_vce: OOB read and OOB write of IB buffer in cs_parse/cs_reloc via unchecked multi-dword command fields
Summary
CS parse loop at 565 checks p->idx < length_dw but reads p->idx+1 (cmd) and up to p->idx+12 per command type. radeon_get_ib_value no bounds check. radeon_vce_cs_reloc writes p->ib.ptr[lo]/[hi] with caller-supplied unchecked indices. len validation at 569 only enforces min 8/multiple 4, NOT that idx+len/4<=length_dw. Reloc writes 8 bytes per call into next radeon_sa_bo object. Unpriv render node -> kCE with grooming or panic.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1753 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace harness that reproduces the bug logic | 2.9 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -Wextra -o harness harness.c | 98 B | view raw |
| run.sh | run-script | ./harness | 59 B | view raw |
| build.log | build-log | full build output | 13 B | view raw |
| run.log | run-log | full decisive run output | 1.3 KB | view raw |
| env.txt | environment | uname + cc version | 188 B | view raw |
| VERDICT.md | verdict | full narrative: mechanism, Phase 6, fix | 1.7 KB | β raw |
| fix.diff | suggested-fix | git-apply-able one-logical-change fix | 866 B | 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-1753 β radeon_vce.c OOB read/write of IB buffer in cs_parse/cs_reloc
Verdict
REPRODUCED (logic/harness) β bug confirmed by source trace. Not live-triggerable on the default QEMU guest (no radeon GPU).
Mechanism (path:line)
sys/dev/drm/radeon/radeon_vce.c:565βwhile (p->idx < p->chunk_ib->length_dw) {β only checksp->idx.sys/dev/drm/radeon/radeon_vce.c:566-567βlen = radeon_get_ib_value(p, p->idx); cmd = radeon_get_ib_value(p, p->idx + 1);βp->idx + 1may be atlength_dw(no bound).sys/dev/drm/radeon/radeon_vce.c:569βif ((len < 8) || (len & 3)) return -EINVAL;β only enforces minimum / alignment, not thatidx + len/4 <= length_dw.- Case encode (0x03000001) reads
p->idx + 8, +9, +10, +11, +12viaradeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9, *size)etc. sys/dev/drm/radeon/radeon.h:1098-1105βradeon_get_ib_valuedoes no bounds check onidx.sys/dev/drm/radeon/radeon_vce.c:493-494βradeon_vce_cs_relocalso writesp->ib.ptr[lo]/[hi]with caller-supplied indices.
Phase 6 escalation
Render-node reach on radeon GPU. OOB read of IB (kernel memory info
leak) + OOB write of p->ib.ptr[] into the next radeon_sa_bo object.
Slab grooming of the IB-adjacent slab β controlled write into a
victim object β RIP control β uid0 on this guest. Not developed because
the default guest has no radeon GPU.
PoC
harness.c shows that with length_dw = 4 and p->idx = 2, the encode
case reads ib[4..13] (all past length_dw) β OOB.
Fix
fix.diff adds a single bound check at the top of the parse loop,
before reading cmd: if (p->idx + len > p->chunk_ib->length_dw) return -EINVAL;.
Validated by a clean radeon.ko rebuild with the patch applied.
Fix verification
fixedVALIDATED at module-build level: applied fix.diff to radeon_vce.c, 'make' rc=0, radeon.ko links cleanly. New DRM_ERROR string 'VCE command at %d len %d overflows IB (%d)!' present in radeon.ko (verified via strings).
baseline: harness shows ib[4..13] read as OOB for p->idx=2, length_dw=4 patched: radeon.ko builds clean; radeon_vce_cs_parse now checks (p->idx + len > chunk_ib->length_dw) before reading cmd, rejecting OOB commands.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- r
- a
- d
- e
- o
- n
- /
- r
- a
- d
- e
- o
- n
- _
- v
- c
- e
- .
- c
- :
- 5
- 6
- 5
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- r
- a
- d
- e
- o
- n
- /
- r
- a
- d
- e
- o
- n
- _
- v
- c
- e
- .
- c
- :
- 5
- 6
- 6
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- r
- a
- d
- e
- o
- n
- /
- r
- a
- d
- e
- o
- n
- _
- v
- c
- e
- .
- c
- :
- 5
- 6
- 9
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- r
- a
- d
- e
- o
- n
- /
- r
- a
- d
- e
- o
- n
- .
- h
- :
- 1
- 0
- 9
- 8
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- r
- a
- d
- e
- o
- n
- /
- r
- a
- d
- e
- o
- n
- _
- v
- c
- e
- .
- c
- :
- 4
- 9
- 3
Detail
Exploit chain
Render-node reach on radeon GPU. OOB read of IB (kernel memory info leak) + OOB write of p->ib.ptr[] into the next radeon_sa_bo object. Slab grooming of the IB-adjacent slab -> controlled write into a victim object -> RIP control -> uid0 on this guest (no SMAP/SMEP/KASLR). Not developed because the default guest has no radeon GPU. Harness in harness.c.
Evidence (decisive lines)
p->idx=2 (within length_dw=4) radeon_get_ib_value(p, p->idx+2) = ib[4] = 0xdead0004 <-- OOB radeon_get_ib_value(p, p->idx+10) = ib[12] = 0x00000000 <-- OOB VERDICT: BUG CONFIRMED. cs_parse reads IB dwords past chunk_ib->length_dw (no idx+len/4 <= length_dw check). Same path writes p->ib.ptr[lo]/[hi] in cs_reloc; combined, OOB read + OOB write of IB on render node.
PoC changes
Wrote harness.c, build.sh, run.sh, VERDICT.md, manifest.json, fix.diff. Original folder was empty.
Verified recommended fix
fix.diff adds a single bound check at the top of the parse loop, before reading cmd: if (p->idx + len > p->chunk_ib->length_dw) return -EINVAL. One logical change.
Verdict
REPRODUCED (logic/harness). radeon_vce.c:565 while(p->idx < chunk_ib->length_dw) reads p->idx+1 (line 567), then case encode reads p->idx+8/+9/+10/+11/+12 via radeon_vce_cs_reloc. Line 569 only checks (len<8)||(len&3), not that idx+len/4<=length_dw. radeon_get_ib_value (radeon.h:1098) does NO bounds check on idx. radeon_vce_cs_reloc also writes p->ib.ptr[lo]/[hi] (lines 493-494) with caller-supplied indices. Harness shows with length_dw=4 and p->idx=2, the encode case reads ib[4..13] all OOB.
No comments yet.