hsw_activate_psr2() overflows two 4-bit EDP_PSR2_CTL fields when sink reports max resync latency, corrupting IDLE_FRAME/FRAME_BEFORE_SU/TP2_TIME in one register write
Summary
get_sink_sync_latency accepts any sink-reported 0..15; activate_psr2 computes idle_frames = max(idle, latency+1) = 16 and FRAME_BEFORE_SU(latency+1)=16<<4, but both EDP_PSR2_CTL fields are 4-bit. With latency=15 the single register write yields 0xC0000310: IDLE_FRAME reads back 0 (below the HW minimum the code enforces), FRAME_BEFORE_SU=1 (wanted 16), and the spill ORs into TP2_TIME making it 50us - three timing fields corrupted. Proven by a guest-built demo mirroring the exact code (run.log). Crafted-sink (malicious dongle/monitor firmware) display DoS on PSR2-capable GLK/gen9/gen10; no kernel memory-safety impact. Reject-PSR2-at-15 fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4450 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| psr2_reg_demo.c | β | 3.2 KB | view raw | |
| build.sh | β | 104 B | view raw | |
| run.sh | β | 99 B | view raw | |
| build.log | β | 138 B | view raw | |
| run.log | β | 1.0 KB | view raw | |
| env.txt | β | 541 B | view raw | |
| fix.diff | β | 809 B | view raw | |
| manifest.json | β | 984 B | view raw | |
| verdict.json | β | 2.8 KB | view raw |
DF-4450 β PSR2 EDP_PSR2_CTL 4-bit field overflow via sink-reported resync latency
What this pack contains
A deterministic, hardware-free reproduction of the register-encoding defect
in hsw_activate_psr2() (sys/dev/drm/i915/intel_psr.c:410-443): the demo
mirrors the kernel arithmetic exactly and shows that a sink reporting
DP_SYNCHRONIZATION_LATENCY_IN_SINK = 0x0F (the maximum value allowed by
DP_MAX_RESYNC_FRAME_COUNT_MASK, include/drm/drm_dp_helper.h:800) makes the
driver write an EDP_PSR2_CTL value in which three independent 4-bit timing
fields are corrupted by adjacent-field spill.
Build / run
./build.sh # cc -O2 -o /tmp/psr2_reg_demo /tmp/psr2_reg_demo.c ./run.sh # /tmp/psr2_reg_demo
(On the audit host the source is psr2_reg_demo.c; it was transferred to the
DF guest, compiled with the guest's cc 8.3, and executed there β see
build.log / run.log / env.txt.)
Expected output (decisive lines)
latency=14 (max legal) EDP_PSR2_CTL=0xc00002ff -> IDLE_FRAME=15 FRAME_BEFORE_SU=15 TP2_TIME=2500us latency=15 (SPILLS!) EDP_PSR2_CTL=0xc0000310 -> IDLE_FRAME=0 FRAME_BEFORE_SU=1 TP2_TIME=50us
At latency=15 the kernel computes idle_frames = max(6, 15+1) = 16 and
EDP_PSR2_FRAME_BEFORE_SU(16):
16 << 0(intel_psr.c:421) spills bit 4 into FRAME_BEFORE_SU β the IDLE_FRAME field reads back 0, silently dropping below the HW minimum of 6 frames the code explicitly tries to enforce (intel_psr.c:415-418).16 << 4 = 0x100(intel_psr.c:430) spills bit 8 into TP2_TIME; OR'ed with the requested 2500us encoding (2<<8), the 2-bit TP2_TIME field becomes3= 50us β the opposite of the requested value.
Why no kernel-side run
The kernel manifestation requires an Intel display engine (HSW+) driving a
PSR2-capable eDP sink that reports resync latency 0xF. The audit guest has
virtio-gpu only (pciconf -l: vgapci0 ... chip=0x11111234), so i915
cannot bind; the write to EDP_PSR2_CTL (a device MMIO at 0x6f900) cannot be
exercised there. The arithmetic is reproduced exactly; the MMIO effect is
hardware-bound.
VERDICT β DF-4450
Status: inconclusive (kernel-path hardware-blocked; arithmetic defect proven)
Finding-level severity: Low Β· confidence: certain (math), hardware manifestation not exercisable on this guest
Root cause (path:line)
sys/dev/drm/i915/intel_psr.c:205-215βintel_dp_get_sink_sync_latency()reads one sink-controlled DPCD byte and masks it withDP_MAX_RESYNC_FRAME_COUNT_MASK=0xf(include/drm/drm_dp_helper.h:800), i.e. accepts 0..15 with no upper bound check for the PSR2 use.sys/dev/drm/i915/intel_psr.c:418-421βhsw_activate_psr2()computesidle_frames = max(6, vbt)thenmax(idle_frames, sink_sync_latency + 1)and writesval = idle_frames << EDP_PSR2_IDLE_FRAME_SHIFT.sys/dev/drm/i915/intel_psr.c:430βval |= EDP_PSR2_FRAME_BEFORE_SU(sink_sync_latency + 1).sys/dev/drm/i915/i915_reg.h:4217-4222β bothEDP_PSR2_IDLE_FRAME_MASK(0xf, bits 3:0) andEDP_PSR2_FRAME_BEFORE_SU_MASK(0xf << 4, bits 7:4) are 4-bit fields;EDP_PSR2_TP2_TIME_MASKoccupies bits 9:8.
With sink_sync_latency == 15 (a fully spec-legal DPCD value, attacker-chosen
by a crafted sink β dongle/dock/panel firmware): 15 + 1 = 16 does not fit a
4-bit field, in two places, in the same register write.
Proof
psr2_reg_demo.c reproduces the kernel's value construction verbatim
(build+run logs on the DF 6.5-DEVELOPMENT guest, cc 8.3):
latency=14 (max legal) EDP_PSR2_CTL=0xc00002ff -> IDLE_FRAME=15 FRAME_BEFORE_SU=15 TP2_TIME=2500us latency=15 (SPILLS!) EDP_PSR2_CTL=0xc0000310 -> IDLE_FRAME=0 FRAME_BEFORE_SU=1 TP2_TIME=50us
Three independent PSR2 timing fields end up wrong in a single MMIO write: IDLE_FRAME silently 0 (below the enforced minimum of 6), FRAME_BEFORE_SU 1 instead of 16, TP2_TIME 50us instead of the requested 2500us.
Impact assessment (honest)
- No kernel memory-safety effect:
EDP_PSR2_CTLis a device register; the spill corrupts display timing configuration only. - Practical ceiling: a crafted sink can destabilize PSR2 entry timing on
GLK/CNL+/gen9 PSR2 platforms (link errors, blanking, flicker) β a local,
hardware-assisted denial-of-service against display usability. PSR2 also
requires
sink_psr2_support(Y-coordinate + ALPM capable sink), further narrowing the trigger. - The PSR1 path is safe:
EDP_PSR_CTL's idle-frame field is 8 bits wide (i915_reg.h:4151 area), and 16 fits.
Why the kernel path itself was not run
Guest hardware is virtio-gpu only (env.txt), so i915 never binds and
EDP_PSR2_CTL (0x6f900) is not reachable. Building the demo on the guest and
running it there (done) is the maximum achievable verification on this
platform; the defect itself is arithmetic and reproducible by inspection.
Fix
fix.diff β refuse PSR2 in intel_psr2_config_valid() when
sink_sync_latency + 1 overflows the 4-bit fields (i.e. > EDP_PSR2_IDLE_FRAME_MASK).
One hunk, no behavior change for latency β€ 14.
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_testableCannot build the fixed kernel path on this guest: no Intel display hardware; the MMIO write under test never executes. fix.diff is a one-hunk guard in intel_psr2_config_valid() reviewed against the exact field widths (i915_reg.h:4217-4222).
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'latency=15 (SPILLS!) EDP_PSR2_CTL=0xc0000310 -> IDLE_FRAME=0 FRAME_BEFORE_SU=1 TP2_TIME=50us' vs legal max 'latency=14 -> 0xc00002ff IDLE_FRAME=15 FRAME_BEFORE_SU=15 TP2_TIME=2500us'; env.txt proves virtio-gpu-only guest; VERDICT.md has the full path:line trace
PoC changes
No kernel PoC seed existed; authored a standalone deterministic demo mirroring the exact kernel arithmetic and field definitions
Verified recommended fix
In intel_psr2_config_valid() reject PSR2 when dev_priv->psr.sink_sync_latency + 1 > EDP_PSR2_IDLE_FRAME_MASK (4-bit fields cannot hold 16)
Verdict
Arithmetic defect proven (demo compiled and run on the DF 6.5 guest): a sink reporting DP_SYNCHRONIZATION_LATENCY_IN_SINK=0xF makes hsw_activate_psr2() compute 15+1=16, overflowing the 4-bit EDP_PSR2_IDLE_FRAME and EDP_PSR2_FRAME_BEFORE_SU fields of EDP_PSR2_CTL and corrupting TP2_TIME as well (0xc0000310 -> IDLE_FRAME=0, FRAME_BEFORE_SU=1, TP2_TIME=50us). Kernel-side MMIO manifestation is hardware-bound and cannot be exercised: the guest has virtio-gpu only, i915 cannot bind. Impact ceiling is display-timing corruption on PSR2-capable platforms (local DoS via crafted sink); no kernel memory-safety effect. Mapped to 'inconclusive' (device absent) per orchestrator enum.
No comments yet.