β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5054

irq_source_info_dce80[] leaves DC_IRQ_SOURCE_DC{1..6}_VLINE0 (enum 84..89) zero-initialized with .funcs==NULL; dal_irq_service_set/ack dereference info->funcs unguarded β€” DF-2168/DF-4932/DF-4995 family instance on the DCE80 table

Summary

Designators cover 0..83 while the enum runs to 90 - slots 84..89 stay zeroed, and set/ack fault at irq_service.c:152 reading NULL+offset. No in-tree caller passes 84..89 (proven by 256x13 sweep of the IH converter) - latent like the dce110 twin. Verbatim replica SIGSEGV x2 deterministic; six dummy entries + hub guards fix; amdgpu.ko rebuild -Werror clean.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5054 Β· 14 files
FileTypeDescriptionSize
README.md β€” 2.7 KB ↓ raw
VERDICT.md β€” 5.3 KB ↓ raw
dce80_vline0gap.c β€” 28.6 KB view raw
build.sh β€” 128 B view raw
run.sh β€” 117 B view raw
build.log β€” 185 B view raw
run.log β€” 2.2 KB view raw
run.2.log β€” 187 B view raw
env.txt β€” 397 B view raw
fix.diff β€” 986 B view raw
amd_fix_apply.log β€” 850 B view raw
amdgpu_module_build.log β€” 1.2 MB ↓ download
manifest.json β€” 2.2 KB view raw
verdict.json β€” 5.3 KB view raw

DF-5054 β€” irq_service_dce80[] leaves DC_IRQ_SOURCE_DC{1..6}_VLINE0 (enum 84..89) zero-initialized with .funcs==NULL β€” DF-2168/DF-4932/DF-4995 family on the DCE80 (Sea Islands) table

What

irq_source_info_dce80[DAL_IRQ_SOURCES_NUMBER=90] (sys/dev/drm/amd/display/dc/irq/dce80/irq_service_dce80.c:188-274) has designators covering exactly indices 0..83 (last: vblank_int_entry(5) == DC_IRQ_SOURCE_VBLANK6 == 83). The shared enum (irq_types.h:140-147) continues with DC_IRQ_SOURCE_DC1_VLINE0..DC6_VLINE0 = 84..89 β€” those six slots stay zero-initialized, i.e. .funcs == NULL. find_irq_source_info (irq_service.c:82) bounds-checks only the index, and dal_irq_service_set/dal_irq_service_ack dereference info->funcs->set/->ack unguarded (irq_service.c:118/:152) β€” a set/ack of any VLINE0 source faults reading NULL+offsetof. Hub finding: DF-2168; family: DF-4932 (dce110), DF-4995 (dcn10).

Reproduce (on the QEMU guest; no AMD GPU needed β€” verbatim userland replica)

# build:  sh build.sh    # cc -O2 -Wall -std=gnu11 [-DFIX_DF5054]
# run:    sh run.sh      # baseline then fixed

The replica carries the enum, structs, the dce80 entry macros (including the dce80-specific [DC_IRQ_SOURCE_INVALID + reg_num] / [DC_IRQ_SOURCE_HPD6 + reg_num] designator forms), the table body, to_dal_irq_source_dce110 (the converter dce80 installs, dce80:277, with the real VISLANDS30 constants), and the irq_service.c dispatch β€” verbatim. Register addresses/masks are stubbed inert data.

Expected output

Baseline (run.log / run.2.log): table[84..89] (.funcs == NULL) x6, controls pass (OOB index 90 rejected; PFLIP1 generic set programs its enable reg; dce80 hpd_ack toggles polarity; 256x13 to_dal sweep: 0 VLINE0 returns β†’ latent), then dal_irq_service_set(DC_IRQ_SOURCE_DC1_VLINE0) β†’ SIGSEGV exit 139 at the info->funcs->ack deref. Deterministic x2.

Fixed (-DFIX_DF5054): 0 NULL-funcs entries, dummy handlers log and return, exit 0. Also validated by a full -Werror amdgpu.ko module rebuild with fix.diff applied (RC=0, irq_service_dce80.o linked, amdgpu_module_build.log), then sources reverted md5-verified.

Severity rationale

Low (family rating): kernel NULL-deref panic, latent β€” no in-tree caller passes a VLINE0 source (traced: suspend/resume touch HPD/HPDRX only (amdgpu_dm_irq.c:353/380/406), vblank enable is IRQ_TYPE_VBLANK+otg_inst (amdgpu_dm.c:3014-3015), pflip is IRQ_TYPE_PFLIP (amdgpu_dm_irq.c:570), and the IH path converts through to_dal_irq_source_dce110 which can never return 84..89 β€” proven by the replica's full src-id sweep). Guest has no AMD GPU. One future caller away from a remote-ish panic on DCE80-era ASICs (Oland/Bonaire/Hawaii/Kabini).

VERDICT.md
↓ download raw

DF-5054 β€” VERDICT

Status: reproduced (panic-equivalent SIGSEGV in the verbatim replica), fix validated (module -Werror rebuild + replica fixed mode). Impact: kernel NULL-deref panic (Low, latent). Confidence: certain.

The bug (path:line)

  • irq_types.h:140-147 β€” the shared enum runs to DAL_IRQ_SOURCES_NUMBER=90 with DC_IRQ_SOURCE_DC{1..6}_VLINE0 = 84..89 (replica prints the arithmetic: VBLANK6=83, DC1_VLINE0=84, DC6_VLINE0=89, NUMBER=90).
  • irq_service_dce80.c:188-274 β€” designators cover 0..83 only: [INVALID]=0 (dce80:190), hpd(1..6)=[INVALID+1..+6]=1..6 (dce80:191-196), hpd_rx(1..6)=[HPD6+1..+6]=7..12 (dce80:197-202), i2c(1..6)=13..18, dpsink(1..6)=19..24, [TIMER]=25, pflip(0..5)=[PFLIP1+0..5]=26..31, [PFLIP_UNDERLAY0]=32, gpio(0..30)=33..63, underflow(1..6)=64..69, [DMCU_SCP]=70, [VBIOS_SW]=71, vupdate(0..5)=72..77, vblank(0..5)=78..83. Slots 84..89 have NO designators β†’ C zero-init β†’ .funcs == NULL.
  • irq_service.c:82 β€” the index bounds check passes for 84..89 (< DAL_IRQ_SOURCES_NUMBER), so find_irq_source_info returns &info[84] (non-NULL).
  • irq_service.c:116 β†’ dal_irq_service_ack β†’ irq_service.c:152 if (info->funcs->ack) β€” the NULL+offsetof(.ack) load faults (dal_irq_service_set calls ack FIRST, so set() never even reaches its own :118 deref).

Hub: DF-2168 (the missing !info->funcs guards). Family twins already filed: DF-4932 (dce110 table), DF-4995 (dcn10 table). This is the DCE80 table instance β€” the last-but-one ASIC table (dce120 remains, noted in DF-4932's summary).

Reproduction (verbatim userland replica on the audit guest)

dce80_vline0gap.c β€” enum/structs/macros/table/converter/dispatch verbatim (register data stubbed inert); cc 8.3 [DragonFly].

run.log (baseline): - table[84]..table[89] (.funcs == NULL) — 6 uncovered enum values; - controls: OOB index 90 rejected by find_irq_source_info (ok); PFLIP1 set via generic path programs enable_reg 0x2200→1 (ok); HPD1 ack runs the dce80-local hpd_ack, polarity bit 0xffffffff→0xfffffffb (ok); to_dal sweep 256 src x 13 ext: 246 mapped to covered sources, 0 to VLINE0 (latent proof for hunt item c); - trigger: dal_irq_service_set(DC_IRQ_SOURCE_DC1_VLINE0=84) → Segmentation fault, exit 139. run.2.log: second baseline, exit 139 again (deterministic). Fixed (-DFIX_DF5054): 0 NULL-funcs; dal_irq_service_dummy_ack/set log-and-return; survived; exit 0.

Fix validation (fix_status: fixed)

  • fix.diff: six [DC_IRQ_SOURCE_DCx_VLINE0] = dummy_irq_entry() designators making the table total over the shared enum (DF-4932/ DF-4995's shape). Applies clean to the real tree (patch -p1, Hunk #1 succeeded at 271; dry-run verified host-side first).
  • Guest /usr/src patched (amd_fix_apply.log: patched md5 0cdb51fa69c1019438882d26fe100eda), full make -j6 amdgpu.ko module rebuild under the module -Werror flag set: RC=0, zero error/warning diagnostics, irq_service_dce80.o linked (amdgpu_module_build.log, 777 lines untrimmed; amdgpu.ko 3741304 B).
  • Behavioral: replica fixed mode exit 0 (re-run post-build on the same guest).
  • Revert: reverse patch, md5 back to pristine f20f096b3f00c5186a514e7aa3b84db4; make clean; scratch removed; guest reset to the with-src snapshot afterwards.
  • Defense-in-depth (DF-2168's territory, recommended alongside): add if (!info->funcs) return false; guards in dal_irq_service_set/ack.

Reachability / ceiling (honest)

Latent: no in-tree caller passes 84..89 (see README). The guest has no AMD GPU, so the kernel path cannot be driven on this machine β€” hence the family's replica-harness methodology (identical to DF-4932/DF-4933/ DF-4995 verdicts). Ceiling if a future caller appears: kernel panic (NULL-page read), not controllable corruption (the faulting deref reads a fixed NULL+offset).

Also checked on this file (pass-2 negatives)

  • hpd_rx_irq_info_funcs/pflip_irq_info_funcs (.set/.ack all NULL, dce80:72-80): SAFE β€” funcs itself is non-NULL and irq_service.c:118/ 152 NULL-check the members before calling; the generic fallback runs.
  • struct irq_service_funcs has exactly one member (to_dal_irq_source, irq_service.h:59-64); irq_service_funcs_dce80 sets it (dce80:276-278) β€” no dce80/dce110 type mismatch (hunt item d).
  • hpd pin ranges: mmDC_HPD1..6 for hpd(1..6)/hpd_rx(1..6), field masks borrowed from HPD1 across pins (identical DCE8 layout, AMD pattern); pflip(0..5) β†’ mmDCP0..5; gpio 0..30; vupdate/vblank CRTC0..5 β€” all in range on DCE80 (hunt item e).
  • vblank .src_id = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 + reg_num (dce80:162): src_ids 19..24 are consecutive (ivsrcid_vislands30.h:66-156) β€” correct.
  • DAL_VALID_IRQ_SRC_NUM off-by-one (irq_types.h:157-158 allows src==90): DF-2168/DF-4932-noted, amdgpu_dm_irq.c-side, harmless against find_irq_source_info's own bounds check β€” not re-filed.
  • vupdate entries lacking .src_id β†’ DF-5055 (filed separately).

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

fixed
baseline reproduced→ patch + rebuild →patched clean

fixed: fix.diff (six VLINE0 dummy entries) applied to guest /usr/src (patched md5 0cdb51fa69c1019438882d26fe100eda, together with DF-5055's diff); full make -j6 amdgpu.ko rebuild under -Werror RC=0, zero error/warning diagnostics, irq_service_dce80.o linked (amdgpu_module_build.log); replica fixed mode exit 0 vs baseline SIGSEGV 139; sources reverted md5-verified pristine (f20f096b3f00c5186a514e7aa3b84db4), module objects cleaned, guest reset to the with-src snapshot.

fix.diff; amd_fix_apply.log; amdgpu_module_build.log; run.log (baseline crash + fixed exit 0); run.2.log; VERDICT.md
↓ fix.diffper-fix-DF-5054

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log + run.2.log (decisive: 'table[84..89] (.funcs == NULL)' x6; trigger 'dal_irq_service_set(DC_IRQ_SOURCE_DC1_VLINE0=84)' -> Segmentation fault exit 139 both runs; fixed section exit 0); dce80_vline0gap.c (verbatim replica); amd_fix_apply.log + amdgpu_module_build.log (777-line -Werror amdgpu.ko rebuild, RC=0); fix.diff; VERDICT.md

PoC changes

Authored fresh (no seed), modeled on DF-4995's dcn10 replica with the DCE80 file's own macros verbatim: one compile fix (missing bool param in the set_generic forward decl) plus keeping the vblank funcs wiring faithful (.set = dce110_vblank_set stub, cross-referenced to DF-5055 where the real body is exercised).

Verified recommended fix

Add [DC_IRQ_SOURCE_DC1..6_VLINE0] = dummy_irq_entry() designators so the table totals the shared enum; alongside (DF-2168), guard if (!info->funcs) return false; in dal_irq_service_set/ack.

Verdict

Reproduced at logic level with a verbatim userland replica of irq_source_info_dce80[] (entry macros incl. the dce80-specific [DC_IRQ_SOURCE_INVALID+reg_num]/[DC_IRQ_SOURCE_HPD6+reg_num] designator forms, table body, dce80 hpd_ack, funcs tables β€” irq_service_dce80.c:40-274), to_dal_irq_source_dce110 (dce110:315-393, the converter dce80 installs at :277) and the irq_service.c dispatch (:77-158), run on the audit guest. Designators cover exactly 0..83 while the shared enum runs to DAL_IRQ_SOURCES_NUMBER=90: DC_IRQ_SOURCE_DC{1..6}_VLINE0 (84..89) stay zero-initialized with .funcs==NULL; find_irq_source_info's index bounds check (:82) passes them through and dal_irq_service_ack (:152, called first by dal_irq_service_set at :116) dereferences info->funcs->ack on NULL -> SIGSEGV exit 139, deterministic x2. Controls prove the rest of the table healthy (OOB index 90 rejected; PFLIP1 generic set; dce80 hpd_ack polarity toggle) and latency (256-src x 13-ext to_dal sweep: 0 returns in 84..89 β€” no in-tree caller can reach the gap; suspend/resume are HPD/HPDRX-only, vblank enable is IRQ_TYPE_VBLANK+otg_inst, pflip is IRQ_TYPE_PFLIP). FIX VALIDATED: six VLINE0 dummy_irq_entry() designators (fix.diff) -> replica fixed mode exit 0 with dummy log-and-return handlers; full amdgpu.ko module rebuild with the fix on guest /usr/src under -Werror RC=0, zero diagnostics, irq_service_dce80.o linked (amdgpu_module_build.log); sources reverted md5-verified pristine. DF-2168 (hub guards) / DF-4932 (dce110) / DF-4995 (dcn10) family instance on the DCE80 (Sea Islands) table; guest has no AMD GPU so the kernel path itself cannot be driven β€” family-standard replica methodology. Ceiling if a future caller appears: kernel NULL-page-read panic, not controllable corruption.