dce_virtual_pageflip dereferences crtcs[crtc_id] before the crtc_id bounds check (latent OOB read)
Summary
The array read precedes its own bounds check; today's sole caller always passes a bounded id so the defect is latent hardening (upstream carries the same inverted order) - one refactor away from a live OOB read of a kernel pointer array. Reorder-check-above-read fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4642 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.2 KB | β raw | |
| VERDICT.md | β | 2.2 KB | β raw | |
| verdict.json | β | 2.0 KB | view raw | |
| manifest.json | β | 474 B | view raw |
DF-4642 β dce_virtual_pageflip reads crtcs[crtc_id] before bounds-checking crtc_id
- File:
sys/dev/drm/amd/amdgpu/dce_virtual.c:662-667 - Severity: Info (hardening) β latent OOB read; no demonstrated impact on this tree
- Status: untested β requires an AMD GPU with the amdgpu kld module loaded and
amdgpu.virtual_display=1; the audit QEMU guest has no AMD GPU and amdgpu is not in GENERIC (module-only:sys/dev/drm/amd/amd/Makefile), so the code path cannot be exercised here.
Why it is still latent-only today
dce_virtual_pageflip() is static and has exactly one caller:
dce_virtual_vblank_timer_handle() (dce_virtual.c:708) which passes
amdgpu_crtc->crtc_id β always index from dce_virtual_crtc_init() in
[0, adev->mode_info.num_crtc) and num_crtc is clamped to 1..6
(amdgpu_device.c:1271-1275) against crtcs[AMDGPU_MAX_CRTCS=6]
(amdgpu_mode.h:65,327). So the pre-check dereference at line 662 never indexes OOB
from the current call graph; it becomes a live OOB read the moment any future
caller passes an unchecked id (upstream Linux carries the same inverted order).
Fix
Move the crtc_id >= num_crtc test above the array access.
DF-4642 β VERDICT
Not verified on the guest (untested). The code-order defect is proven statically:
static int dce_virtual_pageflip(struct amdgpu_device *adev, unsigned crtc_id)
{
...
amdgpu_crtc = adev->mode_info.crtcs[crtc_id]; /* 662: OOB read for bad id */
if (crtc_id >= adev->mode_info.num_crtc) { /* 664: check AFTER use */
DRM_ERROR("invalid pageflip crtc %d\n", crtc_id);
return -EINVAL;
}
Chain traced:
- Only caller: dce_virtual_vblank_timer_handle (dce_virtual.c:708) passes
amdgpu_crtc->crtc_id, initialized to index < num_crtc at dce_virtual.c:246.
- num_crtc clamped to [1,6] in amdgpu_device_enable_virtual_display
(amdgpu_device.c:1271-1275); array is crtcs[AMDGPU_MAX_CRTCS], AMDGPU_MAX_CRTCS=6
(amdgpu_mode.h:65,327).
- Therefore no OOB is reachable from the current call graph β Info/hardening, not a
demonstrable memory-safety bug on this tree.
Why untested rather than not_reproduced: the driver cannot even load on the audit
guest (no AMD PCI device; amdgpu exists only as a kld module,
sys/dev/drm/amd/amd/Makefile β SUBDIR amdgpu; GENERIC sys/config/X86_64_GENERIC
does not include it). Running the path would require AMD hardware.
Recommended fix (dce_virtual.c)
--- a/sys/dev/drm/amd/amdgpu/dce_virtual.c
+++ b/sys/dev/drm/amd/amdgpu/dce_virtual.c
@@ -659,12 +659,12 @@ static int dce_virtual_pageflip(struct amdgpu_device *adev,
struct amdgpu_crtc *amdgpu_crtc;
struct amdgpu_flip_work *works;
- amdgpu_crtc = adev->mode_info.crtcs[crtc_id];
-
if (crtc_id >= adev->mode_info.num_crtc) {
DRM_ERROR("invalid pageflip crtc %d\n", crtc_id);
return -EINVAL;
}
+ amdgpu_crtc = adev->mode_info.crtcs[crtc_id];
+
/* IRQ could occur when in initial stage */
if (amdgpu_crtc == NULL)
return 0;
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testablefix.diff not authored: nothing was verified at runtime; the one-line reorder in VERDICT.md is the recommended fix.
VERDICT.md contains the git-apply-able reorder diff.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (code excerpt + call-graph proof + clamp citations); README.md (why untested)
PoC changes
none β no PoC seed; finding is hardening-only, driver not loadable on guest
Verified recommended fix
Move the crtc_id >= num_crtc check above the crtcs[crtc_id] array read in dce_virtual_pageflip.
Verdict
Static proof only: dce_virtual.c:662 dereferences adev->mode_info.crtcs[crtc_id] two lines before the crtc_id bounds check at :664. Latent OOB read (CWE-125): the sole caller (dce_virtual.c:708) always passes a bounded crtc_id (num_crtc clamped 1..6 by amdgpu_device.c:1271-1275 against crtcs[AMDGPU_MAX_CRTCS=6]), so no OOB is reachable in the current call graph. Cannot be exercised on the audit guest: amdgpu is a kldload-only module and the guest has no AMD GPU. Reported as Info hardening (matches upstream Linux's inverted order).
No comments yet.