dce120 vupdate entries carry no .src_id, so dce110_vblank_set (wired by dce120's own vblank_irq_info_funcs) re-derives DC_IRQ_SOURCE_INVALID and underflows pipe_offset to 178 β far-OOB pipe_ctx[178] read feeding a wild arm_vert_intr indirect call β the DF-4933/DF-5055 twin on the DCE120 table wiring
Summary
Same double-baked defect as dce80: vupdate entries init no src_id, and even a correct remap underflows (wrong base 78). Latent (no in-tree VUPDATE .set caller). Replica: SIGBUS x2 through poisoned tg; true-src_id + index-derived-pipe two-hunk fix validated via full amdgpu.ko -Werror rebuild.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5079 Β· 16 files| File | Type | Description | Size | |
|---|---|---|---|---|
| dce120_vupd.c | β | 27.6 KB | view raw | |
| build.sh | β | 79 B | view raw | |
| run.sh | β | 103 B | view raw | |
| build.log | β | 370 B | view raw | |
| run.log | β | 2.4 KB | view raw | |
| run.2.log | β | 2.4 KB | view raw | |
| dmesg.txt | β | 1.2 KB | view raw | |
| fix.diff | β | 2.6 KB | view raw | |
| amdgpu_module_build.log | β | 1.2 MB | β download | |
| fix_build.log | β | 1.2 MB | β download | |
| fix_run.log | β | 1.6 KB | view raw | |
| env.txt | β | 201 B | view raw | |
| VERDICT.md | β | 4.9 KB | β raw | |
| README.md | β | 2.2 KB | β raw | |
| verdict.json | β | 6.4 KB | view raw | |
| manifest.json | β | 1.5 KB | view raw |
DF-5079 β dce120 vupdate entries carry no .src_id β dce110_vblank_set pipe-offset underflow (pipe_ctx[178])
File: sys/dev/drm/amd/display/dc/irq/dce120/irq_service_dce120.c:138-144
(wired at :82-85 to dce110_vblank_set) β DF-4933 (dce110) / DF-5055 (dce80)
twin on the DCE120 table wiring.
Build (on the guest)
mkdir -p /root/df5079 && cp dce120_vupd.c build.sh run.sh /root/df5079/ sh /root/df5079/build.sh
Run (on the guest; run twice for determinism)
sh /root/df5079/run.sh # -> run.log, run.2.log
Expected
- baseline (
dce120_vupd_base): - table check: vupdate1
src_id=0 ext_id=0(macro initializes none, unlike vblank's 19..24); the "double-baked" note prints β even a correct VUPDATE remap underflows:(uint8_t)(VUPDATE1(72) β IRQ_TYPE_VBLANK(78)) = 250; - controls: VBLANK1..6 arm pipes 0..5 through the REAL conversion path (to_dal maps the consecutive VLINE0 src ids 19..24 β VBLANK1..6);
- trigger:
set(DC_IRQ_SOURCE_VUPDATE1)βto_dal_irq_source_dce110(0,0)βDC_IRQ_SOURCE_INVALIDβuint8_t pipe_offset = 0 β 78= 178 βpipe_ctx[178]far-OOB read on the MAX_PIPES=6 array (poisoned 0x5a mapping) β garbagetgβtg->funcs->arm_vert_intr(tg,2)wild indirect call β SIGBUS exit 138. Kernel equivalent: far-OOB read feeding a wild indirect call β panic; a groomed heap could in principle aimtg(not demonstrated β honest ceiling is a panic). - fixed (
-DFIX_DF5079, = fix.diff: pipe derived from the TABLE INDEX, provably 0..5 for both ranges + correct V_UPDATE src ids in the macro): all 12 VUPDATE/VBLANK sources arm pipes 0..5, exit 0.
Observed (guest, cc 8.3): baseline exit=138 x2, fixed exit=0 x2
(dmesg.txt corroborates: pid ... (dce120_vupd_base) ... exited on signal 10).
Fix validation: same combined amdgpu.ko rebuild as DF-5078 (rc=0, 0/0 under
-Werror; irq_service_dce110.c hunk = DF-4933/DF-5055's identical fix shape);
sources reverted md5-verified pristine; make clean.
Reachability is latent (traced in VERDICT.md) and the guest has no AMD GPU β family-standard logic-level replica methodology.
DF-5079 VERDICT β dce120 vupdate entries (no .src_id) β dce110_vblank_set pipe_ctx[178] OOB, replica-reproduced
Status: reproduced (logic level, family-standard replica), impact panic, confidence certain. DF-4933 (dce110) / DF-5055 (dce80) twin wired independently by the DCE120 table's own macros.
Root cause (code-certain)
vupdate_int_entry(irq_service_dce120.c:138-144) initializesIRQ_REG_ENTRY(...)+.funcs = &vblank_irq_info_funcsβ and no.src_id/.ext_id(struct irq_service.h:46-57 has both fields; the entries therefore carry{src_id=0, ext_id=0}).vblank_irq_info_funcs(irq_service_dce120.c:82-85) wires.set = dce110_vblank_set.dce110_vblank_set(irq_service_dce110.c:195-219) re-derives the source from the table entry:dc_interrupt_to_irq_source(dc, info->src_id=0, info->ext_id=0)βto_dal_irq_source_dce110(0,0)(dce110:315-393) hits the default case βDC_IRQ_SOURCE_INVALID (0); thenuint8_t pipe_offset = dal_irq_src - IRQ_TYPE_VBLANK(dce110:205) =(uint8_t)(0 - 78)= 178; thencore_dc->current_state->res_ctx.pipe_ctx[178].stream_res.tg(dce110:207-208) β a far-OOB read on the MAX_PIPES=6 array (core_types.h:239-241, hw_shared.h:37) β and the very next statement calls through the garbage pointer:tg->funcs->arm_vert_intr(tg, 2)(dce110:210-211).- Double-baked upstream: even a correctly remapped VUPDATE source underflows (VUPDATE1=72 β 78 β 250), because the subtraction base is IRQ_TYPE_VBLANK, not IRQ_TYPE_VUPDATE (printed by the replica).
- For contrast, the dce120 vblank entries DO set
.src_id = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 + reg_num= 19..24 (consecutive, verified against ivsrcid_vislands30.h:66-156), so the in-tree-reachable VBLANK path arms pipes 0..5 correctly β the defect is confined to the VUPDATE wiring (whose true src ids are the non-consecutive 7,9,11,13,15,17, ivsrcid_vislands30.h:30-61).
Reproduction (this run, on the audit guest)
Verbatim userland replica (dce120_vupd.c, adapted from DF-5055's harness): dce120 vupdate/vblank macros + the dce120-local vblank funcs table, dce110_vblank_set, to_dal_irq_source_dce110, the set dispatch (irq_service.c:77-124), and the dcβcurrent_stateβres_ctx.pipe_ctx[] walk against a 0x5a-poisoned 8MiB mapping with valid timing generators in pipes 0..5. Results (run.log, run.2.log β deterministic x2):
- table:
vupdate1 src_id=0 ext_id=0 | vblank1 src_id=19 - double-baked:
(uint8_t)(72-78) = 250 - controls: VBLANK1..6 arm pipes 0..5 through the REAL conversion path
- trigger:
set(DC_IRQ_SOURCE_VUPDATE1)β pipe_ctx[178] β garbage tg β wildarm_vert_intrβ SIGBUS exit 138 - fixed mode (fix.diff): index-derived pipe β all 12 VUPDATE/VBLANK sources arm pipes 0..5, exit 0
Kernel console corroborates (dmesg.txt: pid ... (dce120_vupd_base) ...
exited on signal 10 x2 per run).
Reachability (traced honestly β latent)
No in-tree caller ever .sets a VUPDATE source: suspend/resume touch
HPD/HPDRX only (amdgpu_dm_irq.c:353/380/406, 625-645); vblank enable is
IRQ_TYPE_VBLANK + otg_inst (amdgpu_dm.c:3014-3015); pflip is
IRQ_TYPE_PFLIP (amdgpu_dm_irq.c:535-556); the IH converter returns VUPDATE
only for src ids 7,9..17 which amdgpu_dm never registers. Service exists
only on DCE12 hardware (dce120_resource.c:1011); guest has no AMD GPU.
Ceiling
Far-OOB read feeding a wild indirect call β a groomed heap around the
dc_state allocation could in principle aim tg (not demonstrated; honest
ceiling is a kernel panic, family-consistent with DF-5055).
Fix validation
fix.diff (two hunks): (1) dce120 vupdate_int_entry gains
.src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + 2 * reg_num (7,9,..,17);
(2) dce110_vblank_set derives the pipe from the TABLE INDEX via the
vblank_pipe_offset() helper β provably 0..5 for both ranges, the exact
DF-4933/DF-5055 fix shape (the dce110 hunk is byte-identical to DF-5055's,
the shared-function owner). Applied to guest /usr/src together with
DF-5078's diff: hunks succeeded at :192/:226 (dce110) and :140 (dce120);
full make -j6 amdgpu.ko rebuild rc=0, 0/0 under -Werror
(amdgpu_module_build.log; irq_service_dce110.c compiled at :572);
replica fixed mode exit 0 with all 12 sources arming the correct pipes;
sources reverted md5-verified pristine (35607b1a.../51bd6ea8...),
make clean. Guest returned to clean snapshot afterwards.
Artifacts
dce120_vupd.c (code_hash 44d0edf9...), build.log, run.log, run.2.log, dmesg.txt, env.txt, fix.diff, amdgpu_module_build.log, fix_build.log, fix_run.log, manifest.json, verdict.json.
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
fixedfix.diff applied to guest /usr/src together with DF-5078's (hunks at dce110:192/:226, dce120:140); full make -j6 amdgpu.ko rebuild rc=0 with 0 errors/0 warnings under -Werror (amdgpu_module_build.log; irq_service_dce110.c compiled at :571-572); replica fixed mode (identical shape) arms the correct pipe for all 12 VUPDATE/VBLANK sources, exit 0 where baseline exits 138; sources reverted md5-verified pristine (35607b1a.../51bd6ea8...), make clean.
fix_build.log (=amdgpu_module_build.log, rc=0, zero diagnostics); fix_run.log (fixed mode: all 12 sources arm pipes 0..5, exit 0); run.log (baseline exit=138)
Confirmed kernel references
- sys/dev/drm/amd/display/dc/irq/dce120/irq_service_dce120.c:138
- sys/dev/drm/amd/display/dc/irq/dce120/irq_service_dce120.c:144
- sys/dev/drm/amd/display/dc/irq/dce120/irq_service_dce120.c:82
- sys/dev/drm/amd/display/dc/irq/dce110/irq_service_dce110.c:201
- sys/dev/drm/amd/display/dc/irq/dce110/irq_service_dce110.c:205
- sys/dev/drm/amd/display/dc/irq/dce110/irq_service_dce110.c:208
- sys/dev/drm/amd/display/dc/irq/dce110/irq_service_dce110.c:211
- sys/dev/drm/amd/display/dc/irq_types.h:153
- sys/dev/drm/amd/include/ivsrcid/ivsrcid_vislands30.h:30
- sys/dev/drm/amd/display/dc/inc/core_types.h:240
Detail
Evidence (decisive lines)
run.log + run.2.log (deterministic x2: vupdate1 src_id=0/ext_id=0 vs vblank1 19; double-baked 250; VBLANK1..6 controls arm pipes 0..5; set(VUPDATE1) -> pipe_ctx[178] -> SIGBUS exit=138; fixed exit=0); dmesg.txt (kernel-observed signal-10 exits); dce120_vupd.c header (path:line map); amdgpu_module_build.log (rc=0, 0/0 under -Werror, irq_service_dce110.o at :571-572)
PoC changes
Self-authored by adapting DF-5055's dce80 harness to the dce120 macros: swapped in the dce120 vupdate/vblank entry forms (SRI/IRQ_REG_ENTRY with per-instance stub mask defines), the dce120 funcs/table names, and added the double-baked underflow print ((uint8_t)(VUPDATE1-IRQ_TYPE_VBLANK)=250) plus a fixed-mode loop proving all six VUPDATE sources arm pipes 0..5.
Verified recommended fix
Derive the pipe from the table INDEX in dce110_vblank_set (vblank_pipe_offset helper, provably 0..5 for both ranges β DF-4933/DF-5055's shape) and give the dce120 vupdate entries their true src ids (.src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + 2*reg_num) β fix.diff carries both hunks.
Verdict
Reproduced at logic level with a verbatim userland replica of the dce120 vupdate/vblank entry macros + the dce120-local vblank_irq_info_funcs {.set=dce110_vblank_set} (irq_service_dce120.c:82-85,138-153), dce110_vblank_set itself (irq_service_dce110.c:195-219), to_dal_irq_source_dce110, and the set dispatch (irq_service.c:77-124), run on the audit guest against a 0x5a-poisoned 8MiB current_state with valid timing generators in pipes 0..5. The dce120 vupdate entries carry src_id=0/ext_id=0 (vupdate_int_entry initializes no .src_id, unlike the vblank entries' consecutive 19..24), so dce110_vblank_set's re-derivation to_dal_irq_source_dce110(0,0) returns DC_IRQ_SOURCE_INVALID and 'uint8_t pipe_offset = dal_irq_src - IRQ_TYPE_VBLANK' underflows to 178; core_dc->current_state->res_ctx.pipe_ctx[178] reads the poisoned mapping and the very next 'tg->funcs->arm_vert_intr(tg,2)' faults β SIGBUS exit 138, deterministic x2. VBLANK1..6 controls arm pipes 0..5 through the REAL conversion path, proving the in-tree-reachable vblank path healthy β this is the DF-4933 (dce110) / DF-5055 (dce80) twin wired independently by the DCE120 table's own macros. The replica also prints the double-baked note: even a correctly remapped VUPDATE source underflows ((uint8_t)(72-78)=250), since the subtraction base is IRQ_TYPE_VBLANK not IRQ_TYPE_VUPDATE (true V_UPDATE src ids are the non-consecutive 7,9,11,13,15,17 per ivsrcid_vislands30.h:30-61). Latent like the family: no in-tree caller ever .set's a VUPDATE source (suspend/resume HPD/HPDRX-only per amdgpu_dm_irq.c:353/380/406,625-645; vblank enable = IRQ_TYPE_VBLANK+otg_inst per amdgpu_dm.c:3014; pflip = IRQ_TYPE_PFLIP; the IH converter returns VUPDATE only for src_ids 7,9..17 which amdgpu_dm never registers), the service exists only on DCE12 hardware (dce120_resource.c:1011), and the guest has no AMD GPU. Ceiling if a future caller appears: far-OOB read feeding a wild indirect call β a groomed heap around the dc_state allocation could in principle aim tg (not demonstrated; honest ceiling is a kernel panic). FIX VALIDATED: fix.diff (dce120 macro gains .src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + 2*reg_num = 7,9,..,17; plus the vblank_pipe_offset() table-index helper in dce110_vblank_set β byte-identical to DF-4933/DF-5055's fix shape, the shared-function owner) applied to guest /usr/src together with DF-5078's (hunks at dce110:192/:226 and dce120:140); full amdgpu.ko rebuild under -Werror rc=0, zero errors, zero warnings, irq_service_dce110.o recompiled and linked (amdgpu_module_build.log:572); replica fixed mode arms the correct pipe 0..5 for all 12 VUPDATE/VBLANK sources, exit 0 vs baseline 138; sources reverted md5-verified pristine, make clean, guest returned to clean snapshot.
No comments yet.