wa_init_mcr uses 1-based fls() to index the 0-based subslice_mask[] and (via intel_calculate_mcr_s_ss_select) programs invalid MCR slice steering on gen10/11
Summary
slice = fls(sseu->slice_mask) (1-based) then subslice_mask[slice] (0-based u8[8]): every single-slice gen10/11 part (all ICL) reads slice 1's mask instead of enabled slice 0's, and the same 1-based values are programmed into GEN8_MCR_SELECTOR - steering MCR reads to a disabled slice/subslice (the code's own comment requires an enabled pair). 0-based field semantics proven in-tree by read_subslice_reg callers. No user input reaches it (boot fuse data); OOB needs fls==8, impossible (GEN_MAX_SLICES=6). Impact is indeterminate hangcheck INSTDONE reads. ffs()-1 fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4416 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | β | 4.0 KB | β raw | |
| verdict.json | β | 2.4 KB | view raw | |
| fix.diff | β | 859 B | view raw | |
| manifest.json | β | 662 B | view raw | |
| README.md | β | 900 B | β raw |
DF-4416 β i915: fls() off-by-one in MCR slice selection (wa_init_mcr + intel_calculate_mcr_s_ss_select)
- File: sys/dev/drm/i915/intel_workarounds.c (companion: sys/dev/drm/i915/intel_engine_cs.c)
- Severity: Low Β· CWE: CWE-193 Β· Confidence: certain (defect) / likely (HW impact)
- Status: untested β no Intel gen10/11 GPU in the audit QEMU guest.
Build / Run / Expected
Not runnable in this environment. Requires Ice Lake / Cannon Lake graphics
hardware. On such hardware: apply fix.diff, rebuild kernel, and after init dump
GEN8_MCR_SELECTOR (0xFD0): baseline steers slice=1 on single-slice parts
(slice_mask==0x1); patched steers slice=0. See VERDICT.md for the full
line-cited analysis (0-based field semantics proven via
for_each_instdone_slice_subslice, intel_ringbuffer.h:103-109).
Artifacts
VERDICT.md Β· verdict.json Β· fix.diff Β· manifest.json
DF-4416 β i915 MCR slice selection uses 1-based fls() to index/steer 0-based slice fields
Verdict (short)
Status: untested β no Intel gen10/gen11 (CNL/ICL) GPU exists in the audit
QEMU guest, so the trigger path (cnl_gt_workarounds_init / icl_gt_workarounds_init)
cannot execute there. The defect itself is proven by source analysis with
in-tree proof of the 0-based field semantics. Severity Low (register-steering
correctness bug; no memory-safety impact on reachable hardware).
Root cause (path:line)
sys/dev/drm/i915/intel_workarounds.c:787u32 slice = fls(sseu->slice_mask);thenintel_workarounds.c:789u8 ss_mask = sseu->subslice_mask[slice];
fls() returns the 1-based position of the MSB (fls(0x1)==1).
sseu->subslice_mask[] (sys/dev/drm/i915/intel_device_info.h:121,
u8 subslice_mask[GEN_MAX_SUBSLICES /* 8 */]) is a 0-based per-slice
array. With slice_mask == 0x1 (single-slice part β every ICL, and the only
case the is_power_of_2() guard admits on real hardware) the code reads
subslice_mask[1], i.e. slice 1's mask, not the enabled slice 0's.
Index range analysis: fls(u8) β [1..8]; array bound is 8, so an actual OOB
read requires slice_mask >= 0x80 (8 slices) β impossible: GEN_MAX_SLICES=6
(intel_device_info.h:116) and no real gen10/11 part fuses more than 3.
The bug is therefore wrong-slice, not out-of-bounds, on real hardware.
- The same 1-based values are then programmed into the MCR steering
selector:
intel_engine_cs.c:769-770(intel_calculate_mcr_s_ss_select), whose return valueintel_workarounds.c:819-822writes intoGEN8_MCR_SELECTORas a GT workaround on gen10/11.GEN8_MCR_SLICE(slice)/GEN11_MCR_SLICE(slice)(i915_reg.h:2442,2446) take a 0-based slice id β proven byread_subslice_reg(intel_engine_cs.c:785,798-804) whose callers pass 0-based counters fromfor_each_instdone_slice_subslice(intel_ringbuffer.h:103-109).
Net effect on single-slice ICL (slice_mask==0x1): MCR selector steered to
slice 1 (does not exist) instead of slice 0, violating the requirement stated
in the function's own comment (intel_workarounds.c:808-817: "needs to be
programmed to point to any enabled s/ss pair"). Mis-steered MCR reads return
indeterminate data β used by INSTDONE reads (hang detection) and the
WaProgramMgsrForL3BankSpecificMmioReads WARN check (intel_workarounds.c:799).
Why it is only Low
- No attacker-controlled input reaches the computation:
sseu->slice_maskis derived at boot from hardware fuse registers (intel_device_info.c). - No kernel-memory disclosure: the wrong value goes to MMIO, not to userspace.
- Worst realistic impact: invalid MCR steering on gen10/11 β wrong per-slice
MMIO read values β misbehaving hangcheck / spurious or missed WARN. This is
a correctness bug (upstream later switched this code to
ffs(mask) - 1).
Fix
findings/poc/DF-4416/fix.diff β use 0-based indices in both
wa_init_mcr (this file) and intel_calculate_mcr_s_ss_select
(intel_engine_cs.c, companion instance feeding the write at
intel_workarounds.c:822).
Reproduce (on hardware with an ICL/CNL GPU)
- Build a kernel with
drmdebug (CONFIG_DRM_I915_DEBUG_GEM equivalent) and boot on Intel Ice Lake graphics. dmesg | grep -i mcr/ observewa_init_mcrWARN behavior, and dumpGEN8_MCR_SELECTOR(offset 0xFD0) after init β bits 30:27 will show slice=1 although only slice 0 is enabled (slice_mask=0x1 indev_priv->info.sseu).- With the patch, the selector shows slice 0.
Not runnable in this audit environment (QEMU guest has no Intel GPU);
status stays untested.
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 validation not performed: no triggering hardware (Intel gen10/11 GPU / 82541 NIC) exists in the audit QEMU guest; fix.diff authored from line-accurate source analysis only.
Fix validation not performed: no triggering hardware (Intel gen10/11 GPU / 82541 NIC) exists in the audit QEMU guest; fix.diff authored from line-accurate source analysis only.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (full trace); intel_workarounds.c:781-800,819-822; intel_engine_cs.c:765-782,785; intel_device_info.h:116-121; i915_reg.h:2442-2449
PoC changes
No runnable PoC authored (hardware-dependent path); source-trace verification only.
Verified recommended fix
Use ffs(sseu->slice_mask)-1 for the array index and 0-based values for the MCR selector (see fix.diff).
Verdict
wa_init_mcr (intel_workarounds.c:787) indexes the 0-based sseu->subslice_mask[] with the 1-based fls(slice_mask) result; the same 1-based values from intel_calculate_mcr_s_ss_select (intel_engine_cs.c:769-770) are programmed into GEN8/11_MCR_SELECTOR via intel_workarounds.c:819-822. On single-slice gen10/11 parts (slice_mask==0x1, all ICL) this reads slice 1's subslice mask and steers MCR packets to the nonexistent slice 1, contradicting the enabled-pair requirement in the function's own comment. Out-of-bounds requires 8 fused slices (fls==8 vs array[8]) which no hardware has (GEN_MAX_SLICES=6), so impact is wrong MCR steering / bogus WARN at :799, not memory corruption. Untested: guest has no Intel GPU; 0-based semantics proven in-tree by read_subslice_reg callers (intel_ringbuffer.h:103-109).
No comments yet.