radeon GART table_size / num_gpu_pages 32-bit wrap (DF-2012's radeon twin): missing table-size invariant check in radeon_gart_table_{vram,ram}_alloc β power-of-two validator currently forces the wrapped size to 0 (fail-closed)
Summary
The u32 num_pages/table_size math wraps like DF-2012 but radeon's power-of-two validator forces the wrap to exactly 0 (fail-closed: init failure, not undersized table). Latent OOB sinks at the restore/set_page loops noted. Arithmetic-proven; invariant-check fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4968 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.9 KB | β raw | |
| VERDICT.md | β | 3.0 KB | β raw | |
| gart_math.c | β | 5.3 KB | view raw | |
| build.sh | β | 92 B | view raw | |
| run.sh | β | 92 B | view raw | |
| build.log | β | 52 B | view raw | |
| run.log | β | 1.1 KB | view raw | |
| env.txt | β | 249 B | view raw | |
| fix.diff | β | 1.4 KB | view raw | |
| verdict.json | β | 2.9 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw |
DF-4968 β radeon GART table_size/num_gpu_pages 32-bit wrap (DF-2012's radeon twin): power-of-two validation forces the wrapped size to 0, so init fails closed β missing consistency check in radeon_gart_table_{vram,ram}_alloc
File: sys/dev/drm/radeon/radeon_gart.c:127-140,68-87,345-350
Class: integer wrap β undersized GART table (hardening; fail-closed in the current code)
Severity: Info (defense-in-depth gap; no demonstrated runtime impact)
Status: untested on this guest (no radeon hardware); arithmetic validated by gart_math.c.
Build / Run / Expected
cc -O2 -Wall -o gart_math gart_math.c && ./gart_math
ALL CHECKS PASSED (0 failures) β shows gart_size=2097152 MB passing radeon_check_arguments,
num_gpu_pages = 2^29, and table_size = num_gpu_pages * 8 wrapping to 0 (and the Γ4
asic variant wrapping at 4 TiB, plus num_gpu_pages itself truncating to 0 at β₯ 16 TiB).
Why Info and not Medium (unlike DF-2012)
DF-2012 (amdgpu) had a usable small wrapped table (2048 bytes) because amdgpu.gart_size
is not constrained to powers of two. The radeon validator
(sys/dev/drm/radeon/radeon_device.c:1126-1134) requires a power of two, so the wrapped
table_size is always exactly 0 for every accepted value β₯ 2 TiB (Γ8 asics) / β₯ 4 TiB
(Γ4 asics). A 0-size table yields a failed/zero-length BO (radeon_gart_table_vram_alloc
β radeon_bo_create(0)) or a NULL pci_alloc_consistent(0) β gart init fails and the
device fails to initialize (fail-closed). The residual hazard: nothing rejects the
configuration before allocation, and radeon_gart_table_vram_pin's restore loop
(radeon_gart.c:178-179) would write num_gpu_pages PTEs through whatever (possibly
0-length) mapping radeon_bo_kmap returned, if that ever succeeded. The consistency check
that upstream added for amdgpu-style bugs is simply absent here.
VERDICT β DF-4968
Status: untested (no radeon hardware on this guest). Arithmetic certain and validated
by gart_math.c (11/11 PASS, run.log). Filed as Info/hardening: the current code
fails closed, but only by accident of the power-of-two validator, and the missing
table-size consistency check is the exact gap DF-2012 exposed in amdgpu.
Chain
sys/dev/drm/radeon/radeon_device.c:1117-1134β power-of-two + β₯32 MB validation, no upper bound; line 1137gtt_size = (uint64_t)radeon_gart_size << 20.sys/dev/drm/radeon/radeon_gart.c:345-346βnum_cpu_pages/num_gpu_pagesareunsigned(u32) fields assigned from the u64 with no overflow check (radeon.h:659-660); they truncate for gtt β₯ 16 TiB.- Per-asic table sizing (32-bit unsigned multiply, no overflow check):
- Γ8:
r600.c:1115,rs600.c:546β wraps to 0 for gtt β₯ 2 TiB - Γ4:r100.c:647,r300.c:136(rv370),rs400.c:101β wraps to 0 for gtt β₯ 4 TiB (rs400 additionally is bounded 32..2048 MB by its own switch atrs400.c:86-93; r100/rv370/rs600/r600 are not). sys/dev/drm/radeon/radeon_gart.c:68-87(radeon_gart_table_ram_alloc) and:127-140(radeon_gart_table_vram_alloc) usetable_sizedirectly with no check thattable_size >= num_gpu_pages * sizeof(entry).- Write sinks if a wrapped/short table ever mapped successfully:
radeon_gart_bindβradeon_gart_set_page(radeon_gart.c:305-313) and theradeon_gart_table_vram_pinrestore loop (radeon_gart.c:178-179) iterate tonum_gpu_pages-1.
Why fail-closed today
The power-of-two constraint makes every accepted radeon_gart_size β₯ 2 TiB produce
table_size == 0 exactly (2^k Γ 2^11 β‘ 0 mod 2^32 for k β₯ 21). A 0-size table cannot be
usefully mapped: radeon_bo_create(..., 0, ...) / pci_alloc_consistent(..., 0, ...) give
a zero-page BO or NULL, radeon_bo_kmap of 0 pages returns an error or NULL mapping
(ttm_bo_util.c:599-627, ttm_bo_ioremap returns -ENOMEM when ioremap yields NULL), and
gart enable fails β device init fails. No kernel OOB write is constructible from the
tunable alone in this tree; hence Info.
Fix
fix.diff adds the missing invariant check at both allocation sites in
sys/dev/drm/radeon/radeon_gart.c (64-bit multiply): reject when the computed
table_size cannot hold num_gpu_pages entries. This converts any future relaxation of
the validator (e.g. someone "fixing" the power-of-two requirement the way amdgpu's
validator works) from a silent OOB into an -EINVAL at init.
Fix validation
Not testable on this guest (no radeon hardware; the trigger is additionally a root boot tunable). Baseline and patched kernels cannot be exercised.
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_testableFix not testable: baseline cannot be reproduced on this guest (no radeon GPU); fix.diff adds the missing table_size/num_gpu_pages consistency check at both allocation sites.
fix.diff; hardware absence documented in env.txt
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (gart_math.c on guest: table_size wrap to 0 at gart=2TiB/4TiB, num_gpu_pages u32 truncation at 16TiB); VERDICT.md (chain + fail-closed reasoning); env.txt (hardware absence)
PoC changes
arithmetic validator instead of kernel PoC (hardware absent)
Verified recommended fix
Add a radeon_gart_table_size_ok() invariant check (64-bit multiply/divide) at the top of radeon_gart_table_vram_alloc and radeon_gart_table_ram_alloc, rejecting undersized/wrapped tables.
Verdict
Kernel behavior untestable on this guest (no radeon PCI device, radeon not in the guest kernel config). Arithmetic certain, validated on the guest by gart_math.c (11/11 PASS): with drm.radeon.gart_size a power-of-two >= 2 TiB, num_gpu_pages8 (or 4 at >= 4 TiB) wraps table_size to exactly 0 because the validator only accepts powers of two - unlike amdgpu (DF-2012), where non-power-of-two values produced a small-but-usable 2048-byte table. A 0-size table cannot be usefully mapped (radeon_bo_create(0)/pci_alloc_consistent(0) -> zero-page BO/NULL; kmap of 0 pages errors or returns NULL), so gart enable fails closed. Filed Info/hardening: radeon_gart_table_vram_alloc (radeon_gart.c:127) and radeon_gart_table_ram_alloc (:68) never validate table_size against num_gpu_pages, and radeon_gart_init (:345-346) stores gtt_size/PAGE_SIZE into u32 fields without an overflow check - the same missing invariants DF-2012 fixed in amdgpu. Any future relaxation of the power-of-two validator turns this into the DF-2012 OOB write (sinks: radeon_gart.c:305-313 set_page and the :178-179 vram_pin restore loop).
No comments yet.