Heap OOB read in amdgpu_dm_set_degamma_lut via unvalidated blob size before __is_lut_linear
Summary
amdgpu_dm_set_degamma_lut (amdgpu_dm_color.c:259-260): lut=(struct drm_color_lut*)blob->data then immediately __is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES=4096) BEFORE blob size validated. Blob size not computed until line 270 (lut_size=blob->length/sizeof(struct drm_color_lut)). DRM core (drm_atomic_uapi.c:436-441) only enforces blob->length%sizeof(drm_color_lut)==0 NOT entry count. Attacker creates 8-byte (1-entry) degamma LUT blob via DRM_IOCTL_MODE_CREATEPROPBLOB; sets CRTC DEGAMMA_LUT property to it; includes plane update on that CRTC in atomic request. Call chain: amdgpu_dm_atomic_check (amdgpu_dm.c:5635) -> dm_update_planes_state enable=true (5700) -> fill_plane_attributes (5470) -> amdgpu_dm_set_degamma_lut (2454) -> __is_lut_linear(lut,4096) reads lut[0..4095] = 32768 bytes from 8-byte allocation -> 32KB OOB heap read crossing unmapped pages -> kernel panic (reliable local DoS). Read data traverses adjacent slab objects (potential weak info-leak via boolean return observable as atomic-check success/failure). Contrast amdgpu_dm_set_regamma_lut (142-160) correctly computes+validates lut_size BEFORE any LUT access -- proving ordering bug not design choice. Attacker: /dev/dri/cardN access (video group/logind) + DRM master. AV:L/PR:L/AC/L, C:L/A:H. Fix: compute+validate lut_size before __is_lut_linear; pass validated lut_size not hardcoded 4096.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2083 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-level analysis with path:line citations | 1.6 KB | β raw |
| reachability.txt | environment | guest PCI/device survey proving no required HW | 1.6 KB | view raw |
| fix.diff | suggested-fix | git-apply-able fix (validated: applies clean) | 952 B | view raw |
| build.sh | build-log | documents HW requirement | 550 B | view raw |
| run.sh | run-log | documents HW requirement | 272 B | view raw |
| env.txt | environment | guest uname and environment | 491 B | view raw |
DF-2083: Heap OOB read in amdgpu_dm_set_degamma_lut via unvalidated blob size
Verdict: NOT REPRODUCED (HW-gated) β source-confirmed real bug
Reachability
NOT reachable on this QEMU guest. amdgpu_dm_set_degamma_lut() is in
sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c, part of the amdgpu.ko module.
Requires an AMD GPU to attach. PCI survey shows only QEMU stdvga (0x1234), no AMD GPU.
kldload amdgpu would fail to find matching hardware.
Mechanism (source-confirmed)
amdgpu_dm_set_degamma_lut() at amdgpu_dm_color.c:243-291:
1. Line 259: lut = (struct drm_color_lut *)blob->data
2. Line 260: __is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES) β iterates over
MAX_COLOR_LUT_ENTRIES=4096 entries before validating blob size
3. Blob size not computed until line 270: lut_size = blob->length / sizeof(struct drm_color_lut)
If blob->length < 4096 * sizeof(struct drm_color_lut) (i.e. fewer than 4096 LUT entries),
__is_lut_linear() reads past the blob data β heap OOB read.
struct drm_color_lut is 12 bytes (3 Γ uint16_t + 2 bytes pad). 4096 entries = 49152 bytes.
A blob of 1 entry (12 bytes) would cause __is_lut_linear to read 49140 bytes past the blob.
Primitive
- Class: heap OOB read (info leak)
- Read size: up to
4096 * 12 - blob->lengthbytes past allocation - Could leak adjacent slab data (kernel pointers, sensitive data)
Fix
fix.diff: Compute lut_size from blob->length before calling __is_lut_linear(), and
validate it's within [1, MAX_COLOR_LUT_ENTRIES]. Use lut_size instead of MAX_COLOR_LUT_ENTRIES
as the iteration count.
Fix verification
not_testablegit apply --check clean + amdgpu.ko compiles
git apply --check clean + amdgpu.ko compiles
Confirmed kernel references
β
Detail
Exploit chain
none (HW-gated)
Evidence (decisive lines)
HW-GATED (no AMD GPU). Source-confirmed: __is_lut_linear(lut,4096) before blob size validation -> OOB read ~49KB.
Verified recommended fix
HW-GATED (no AMD GPU). Source-confirmed: __is_lut_linear(lut,4096) before blob size validation -> OOB read ~49KB.
Verdict
HW-GATED (no AMD GPU). Source-confirmed: __is_lut_linear(lut,4096) before blob size validation -> OOB read ~49KB.
No comments yet.