u32 wrap in SKL DDB data-rate sum over-subscribes the per-pipe DDB (un-carried Linux v5.2 u64 rework)
Summary
skl_get_total_relative_data_rate accumulates per-plane relative data rates in a u32. Each plane's rate reaches ~2.4e9 for a legal 8192x8192 ARGB8888 plane downscaled ~3x per axis; three overlapping planes sum to 4,829,822,976 which wraps to 534,855,680. The wrapped denominator inflates every plane's proportional share ~9x in skl_allocate_pipe_ddb, producing PLANE_BUF_CFG DDB entries up to 7789 blocks inside an 892-block per-pipe DDB. Nothing downstream bounds an entry against ddb_size: skl_compute_plane_wm's res_blocks >= ddb_allocation check is fed the same inflated share so it passes, and skl_ddb_allocation_overlaps only orders cross-PIPE updates. Programmed out-of-range DDB (10-bit field) = overlapping display buffer allocations = display FIFO underrun / pipe wedge requiring modeset or GPU reset. Linux v5.2 computes these rates in u64; this tree keeps the pre-v5.2 u32 arithmetic. VERIFIED source-level with an exact-integer PoC implementing the kernel's fixed16 helpers verbatim (wrap + DDB entries printed; run.log); runtime unverifiable (guest is virtio-gpu, no /dev/dri, i915 module-only). Local DRM master on SKL+ hardware; display DoS only - corrupted values flow only to display registers, never kernel memory. Same family as DF-1118 but distinct functions/sink/fix (v5.2 u64 rework backport, git apply --check clean). Also established: lines ~5130-9863 of this file are byte-identical to Linux v5.2; killed lockmgr-under-crit (systemic shim design), div-by-zero candidates, rps->work lifecycle, and all array bounds.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3572 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.3 KB | β raw | |
| VERDICT.md | β | 4.2 KB | β raw | |
| wm_datarate_wrap.py | β | 4.5 KB | view raw | |
| run.log | β | 1.5 KB | view raw | |
| upstream_v5.2_divergence.diff | β | 2.6 KB | view raw | |
| env.txt | β | 1.2 KB | view raw | |
| fix.diff | β | 3.2 KB | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 4.8 KB | view raw |
DF-3572 β u32 wrap in SKL DDB data-rate sum (skl_get_total_relative_data_rate)
File: sys/dev/drm/i915/intel_pm.c (pass 2, GLM 5.3 re-audit)
Lines: 4216β4262 (wrap at 4252/4259), sink at 4400β4466
Severity: Low β local DoS (display FIFO underrun / pipe wedge) by a DRM
master on Intel SKL+ hardware with an extreme (but fully legal) plane
configuration. Same impact family as pass-1 DF-1118 (skl_wm_method1/2
overflow), different code path and different sink (DDB allocation vs
watermark levels).
Build / Run / Expected
- Build: nothing to compile β this is an exact-integer-arithmetic reproduction of the kernel computation, runnable anywhere.
- Run:
python3 wm_datarate_wrap.py - Expected (see
run.log): per-plane rates 2,415,353,856 + 1,207,234,560 + 1,207,234,560 = 4,829,822,976 wraps in the u32 accumulator to 534,855,680;skl_allocate_pipe_ddbthen emits PLANE_BUF_CFG entries[0,3891] [3891,5840] [5840,7789]inside an 892-block pipe DDB.
Runtime verification on the audit guest is impossible: the guest has a
virtio GPU (see env.txt), i915 never binds, /dev/dri does not exist.
Source-level verification is the bar here (per orchestrator instruction).
Evidence
wm_datarate_wrap.py+run.logβ the arithmetic proof (above).upstream_v5.2_divergence.diffβ hunk ofdiff linux-v5.1 intel_pm.c dragonfly intel_pm.cshowing Linux v5.2 computesrateas u64 andtotal_data_rateas u64; this tree keeps the pre-v5.2 u32 form.env.txtβ guest: virtio vga0x11111234, no/dev/dri,i915.koshipped (module) but not in the stock X86_64_GENERIC kernel and not loaded;kldstatshows no i915.fix.diffβ backport of the u64 arithmetic (git-apply-able againstsys/; never applied to the read-only tree).
Reachability chain (all under attacker-chosen DRM atomic ioctls)
- DRM master sets mode 2731x2731@60 (~605 MHz dotclock β€ SKL max
dotclock = cdclk 675 MHz). At modeset time no scaler is in use, so
skl_check_pipe_max_pixel_rate(intel_pm.c:4119) passes trivially β that gate runs only fromintel_crtc_compute_config(intel_display.c:10926), i.e. at modeset. - Plane-only atomic updates (no modeset β gate not re-run) enable:
primary 8192x8192 ARGB8888 β 2731x2731 (2.9996x/axis), sprite0/1
4096x8192 β 1366x2731 (3.0x/axis). All within
skl_max_scale(3 β 2β»ΒΉβΆ per axis) and the sprite 4096-pixel src-width limit. skl_compute_ddbβskl_get_total_relative_data_ratesums the three u32 rates: 4,829,822,976 > 2Β³Β² β wraps to 534,855,680.skl_allocate_pipe_ddbdividesalloc_size * rateby the wrapped total β each plane's share is ~9x too large β entries run to 7789 in an 892-block DDB (hardware field is 10 bits β values alias to wrong ranges).skl_compute_plane_wm'sres_blocks >= ddb_allocationcheck is fed the same inflated share, so the watermarks validate.skl_ddb_entry_writeprograms the out-of-range PLANE_BUF_CFG values; the only overlap check (skl_ddb_allocation_overlaps, intel_display.c:12696) orders cross-pipe updates and never bounds an entry againstddb_size.- Effect: overlapping display buffer allocations between planes β display FIFO underruns / pipe wedge; recovery needs a modeset or GPU reset.
DF-3572 β VERDICT
Status: not_reproduced at runtime (hardware absent); source-level divergence and arithmetic proven with certainty; impact ceiling is a local display DoS (Low).
What was verified and how
-
Full-file pass-2 audit of
sys/dev/drm/i915/intel_pm.c(9,863 lines): read end-to-end; the second half (~line 5130βEOF) is byte-identical to Linux v5.2 (verified bydiff), the first half is a pre-v5.2 (βv5.1) vintage with selected v5.3/v5.4 backports (SAGV for KBL/CFL, ICL DDB mask,cpp==8dbuf_block_size fix, enabled DBUF_CTL_S2 read). Full divergence catalogue in the audit's negative notes. -
The finding:
skl_plane_relative_data_rate(intel_pm.c:4216-4220) computesdata_rate = width * height * cppin u32 andmul_round_up_u32_fixed16()(i915_drv.h:199) truncates its u64 intermediate to u32 (WARN only);skl_get_total_relative_data_rate(intel_pm.c:4252,4259) accumulates the per-plane rates in a u32total_data_rate. Linux v5.2 computes both in u64 (proof:upstream_v5.2_divergence.diff); this tree carries the pre-v5.2 form. -
Arithmetic proof (
wm_datarate_wrap.py,run.log): three overlapping planes (primary 8192x8192, sprites 4096x8192, all ARGB8888, all ~3x-per-axis downscaled onto a 2731x2731@60 pipe) yield per-plane rates 2,415,353,856 / 1,207,234,560 / 1,207,234,560 β each fits u32 β but the sum 4,829,822,976 wraps to 534,855,680. The wrapped denominator inflates each plane's proportional share ~9x;skl_allocate_pipe_ddb(intel_pm.c:4440-4465) emits DDB entries [0,3891] [3891,5840] [5840,7789] inside an 892-block per-pipe DDB. -
No downstream guard:
skl_compute_plane_wm'sres_blocks >= ddb_allocation(intel_pm.c:4773-4775) is fed the same inflated share, so it passes;skl_ddb_allocation_overlaps(intel_pm.c:5115, used at intel_display.c:12696) only orders cross-PIPE updates; nothing bounds an entry againstddb_size.skl_ddb_entry_write(intel_pm.c:5012) then programs out-of-range PLANE_BUF_CFG values (10-bit field, i915_reg.h:6667 β values alias to different wrong ranges). -
Reachability: every input (mode, plane src/dst rects, formats) is attacker-chosen via the DRM atomic/setcrtc ioctls by a local DRM master; the per-axis 3x scaler cap (
skl_max_scale, intel_display.c:13367,tmpclk1 = (1<<16)*3 - 1), the sprite 4096 src-width limit, and the 8192 max plane src size are all respected by the demonstrated config. The pixel-rate gate (skl_check_pipe_max_pixel_rate) is only invoked fromintel_crtc_compute_config(intel_display.c:10926) at modeset; plane-only atomic updates bypass it. Preconditions: Intel SKL+ iGPU (the default DFly graphics driver on such hardware), cdclk β₯ ~605 MHz.
Why not reproduced on the guest
The audit guest is QEMU/KVM with a virtio GPU
(vgapci0@pci0:0:2:0 chip=0x11111234), i915 never probes, /dev/dri does
not exist, and i915 is not compiled into the stock X86_64_GENERIC kernel
(i915.ko is shipped as a module β see env.txt). No Intel display
hardware β the code path cannot execute. This is an honest
hardware-unreachability record, per the orchestrator's instruction that
source-level certainty is the bar for this file.
Exploit chain
Not an exploitation primitive: the corrupted values flow only into display registers (PLANE_BUF_CFG / watermark levels), not into kernel memory. The realistic ceiling is a display-engine wedge (underrun storm / hung pipe requiring modeset or GPU reset) β local denial of service. No route to uid=0 exists through this path.
Fix
Backport the v5.2 u64 arithmetic (fix.diff): compute the per-plane rate
in u64 and accumulate total_data_rate in u64. Runtime fix validation is
not possible on this guest (no i915 hardware); the diff is a faithful
backport of the upstream-accepted computation.
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.diff is a faithful backport of the upstream v5.2 u64 computation and applies cleanly to sys/dev/drm/i915/intel_pm.c (verified with git apply --check in a scratch copy; sys/ was not modified). Behavioral fix validation requires Intel SKL+ display hardware which the audit guest lacks (virtio GPU, i915 never loads), so baseline-vs-patched runtime comparison is not testable here; the arithmetic model in wm_datarate_wrap.py shows the patched types make the wrap impossible (u64 sum 4,829,822,976 no longer truncates).
fix.diff (git-apply-able, verified); VERDICT.md 'Fix' section; run.log wrap demonstration
Confirmed kernel references
- sys/dev/drm/i915/intel_pm.c:4216
- sys/dev/drm/i915/intel_pm.c:4220
- sys/dev/drm/i915/intel_pm.c:4252
- sys/dev/drm/i915/intel_pm.c:4259
- sys/dev/drm/i915/intel_pm.c:4400
- sys/dev/drm/i915/intel_pm.c:4441
- sys/dev/drm/i915/intel_pm.c:4456
- sys/dev/drm/i915/intel_pm.c:4773
- sys/dev/drm/i915/intel_pm.c:5012
- sys/dev/drm/i915/intel_pm.c:5115
- sys/dev/drm/i915/i915_drv.h:199
- sys/dev/drm/i915/intel_display.c:10926
- sys/dev/drm/i915/intel_display.c:12696
- sys/dev/drm/i915/intel_display.c:13367
Detail
Evidence (decisive lines)
run.log (exact-integer reproduction: wrap 4829822976 -> 534855680, DDB entries [0,3891] [3891,5840] [5840,7789] vs ddb_size 892); upstream_v5.2_divergence.diff (Linux v5.2 u64 vs this tree u32); env.txt (guest virtio-gpu, no /dev/dri, i915 not in GENERIC, not loaded); fix.diff (git-apply-able u64 backport, verified to apply cleanly against sys/dev/drm/i915/intel_pm.c)
PoC changes
no seed PoC existed (new pass-2 finding); wrote wm_datarate_wrap.py implementing the kernel's fixed16 helpers verbatim from i915_drv.h and the exact reachable plane/mode configuration (per-axis 3x scaler cap, sprite 4096 src-width cap, modeset-only pixel-rate gate)
Verified recommended fix
Backport the Linux v5.2 u64 arithmetic for skl_plane_relative_data_rate/skl_get_total_relative_data_rate (see fix.diff)
Verdict
Not runtime-reproducible on the audit guest: it has a virtio GPU (chip 0x11111234), /dev/dri does not exist, and i915 is not in the stock X86_64_GENERIC kernel (i915.ko is shipped as a module that only loads on real Intel iGPUs β where it is the default graphics driver). Source-level verification is conclusive: intel_pm.c:4252/4259 accumulate per-plane relative data rates (each up to ~2.4e9, produced by mul_round_up_u32_fixed16 at i915_drv.h:199 which truncates its u64 intermediate to u32) in a u32 total_data_rate; three legal overlapping planes (primary 8192x8192 + two 4096x8192 sprites, all ARGB8888, all ~3x-per-axis downscaled onto a 2731x2731@60 pipe) sum to 4,829,822,976 which wraps to 534,855,680. The wrapped denominator inflates every plane's proportional share in skl_allocate_pipe_ddb (intel_pm.c:4441/4456), emitting PLANE_BUF_CFG DDB entries up to 7789 blocks inside an 892-block per-pipe DDB; nothing bounds entries against ddb_size (skl_ddb_allocation_overlaps at intel_display.c:12696 only orders cross-PIPE updates) and the watermark ddb_allocation check is fed the same inflated share so it passes. Programmed out-of-range DDB = overlapping display buffer allocations = display FIFO underrun / pipe wedge β a local DoS by any DRM master on SKL+. Impact ceiling is display DoS only; corrupted values flow to display registers, not kernel memory, so no privesc route exists. Linux v5.2 computes these rates in u64 (upstream_v5.2_divergence.diff); this DragonFly tree keeps the pre-v5.2 u32 arithmetic.
No comments yet.