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

DAL_VALID_IRQ_SRC_NUM off-by-one ('<=') accepts irq_source == DAL_IRQ_SOURCES_NUMBER β€” one-past-end index into amdgpu_dm_irq.c's handler-list tables

Summary

The guard macro accepts src == 90, one past the last valid index of the handler-list tables; register_interrupt then list_add_tail's into tab[90], corrupting the adjacent pflip_params[] with kernel list pointers. Latent (all current callers in-range; unregister has zero callers) - one future caller of the public API gets list corruption. Inherited verbatim from upstream v4.17; every other bounds check in the DC irq layer uses '>'. Guest-executed verbatim-macro harness proves the off-by-one; one-character fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4731 Β· 7 files
FileTypeDescriptionSize
df4731_proof.c β€” 4.9 KB view raw
build.sh β€” 130 B view raw
run.log β€” 561 B view raw
VERDICT.md β€” 3.8 KB ↓ raw
fix.diff β€” 484 B view raw
verdict.json β€” 2.4 KB view raw
env.txt β€” 483 B view raw
VERDICT.md
↓ download raw

DF-4731 VERDICT

Status: untested (kernel path hardware-gated) β€” guard defect proven by guest-executed logic proof

What was verified

  1. The code defect is certain (static, line-cited). - sys/dev/drm/amd/display/dc/irq_types.h:147 β€” last enum member DAL_IRQ_SOURCES_NUMBER = 90 (counted: INVALID=0 … DC6_VLINE0=89, NUMBER=90). - sys/dev/drm/amd/display/dc/irq_types.h:157-158 β€” #define DAL_VALID_IRQ_SRC_NUM(src) ((src) <= DAL_IRQ_SOURCES_NUMBER && (src) > DC_IRQ_SOURCE_INVALID) accepts src == 90. - Arrays: sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.h:106-107 β€” struct irq_list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER]; struct list_head irq_handler_list_high_tab[DAL_IRQ_SOURCES_NUMBER]; β†’ valid indices 0..89. - Consumers in the audited file: amdgpu_dm_irq.c:176 (validate_irq_registration_params) β†’ :238/:242 (hnd_list = &adev->dm.irq_handler_list_{high,low}_tab[irq_source], then list_add_tail at :246); amdgpu_dm_irq.c:193 (validate_irq_unregistration_params) β†’ :123/:127 (list walk + list_del + kfree in remove_irq_handler). - DC side is correctly bounded elsewhere: irq_service.c:82 (find_irq_source_info uses >=), which shows <= is the outlier.

  2. Proof-of-logic executed on the DragonFly guest (run.log): with the tree's verbatim enum and macro, DAL_VALID_IRQ_SRC_NUM(90) == true, (89) == true, (91) == false β€” an exact off-by-one acceptance window of exactly one out-of-bounds index.

  3. Upstream comparison: Linux v4.17 drivers/gpu/drm/amd/display/dc/irq_types.h contains the identical <= macro (fetched and diffed during this audit) β€” the defect is inherited, not a DragonFly port typo.

Why the kernel path was not exercised

The QEMU guest has no AMD display hardware (/dev/dri absent; only vgapci0: <VGA-compatible display> chip=0x11111234 QEMU virtual VGA; no drm kld loaded β€” env.txt). amdgpu_dm_irq_register_interrupt() is only reachable behind a functioning amdgpu DC, so triggering the in-kernel OOB list_add_tail is impossible on this guest.

Reachability on real hardware (why Low, not higher)

Every caller of amdgpu_dm_irq_register_interrupt() on this tree passes an in-range source: - amdgpu_dm.c:1274-1292 (register_hpd_handlers): dc_link->irq_source_hpd{,_rx}, both guarded by != DC_IRQ_SOURCE_INVALID. - amdgpu_dm.c:1334-1345, 1356-1366 (dce110) and :1419-1452 (dcn10): dc_interrupt_to_irq_source(dc, i, 0) β€” every to_dal_irq_source_* implementation returns either a valid enum constant or DC_IRQ_SOURCE_INVALID (= 0, in-bounds) (irq_service_dce110.c:315-391). amdgpu_dm_irq_unregister_interrupt() has zero callers tree-wide. So no user-, hardware-, or packet-derived value currently reaches the guard with 90 β€” the finding is a latent guard defect / hardening item, exactly the kind of bound every future caller will trust.

Fix validation

Not testable (needs AMD GPU to build a baseline kernel scenario). fix.diff (<= β†’ <) is the minimal correct change; it cannot regress the valid range (89 still accepted, 0 still rejected β€” verified by the same harness logic).

Bottom line

Real one-past-end guard bug in the validation macro the audited file trusts for every handler-list index; inherited verbatim from Linux 4.17; latent (no live caller can pass the OOB value today); fixed by one character.

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_testable
baseline no→ patch + rebuild →patched clean

Single-character fix (fix.diff); cannot be kernel-validated without AMD GPU. The same harness logic confirms the fixed bound still accepts 0<src<90 and rejects 90+.

fix.diff; VERDICT.md 'Fix validation' section
↓ fix.diffper-fix-DF-4731

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log lines 'accept src=90 (ONE PAST) : true' and 'RESULT: OFF-BY-ONE CONFIRMED'; VERDICT.md reachability analysis (callers at amdgpu_dm.c:1274-1452 all in-range); env.txt documents absent AMD hardware

PoC changes

n/a - harness authored fresh (verbatim enum+macro extraction from the tree), no seed PoC existed

Verified recommended fix

irq_types.h:158 change '<=' to '<' in DAL_VALID_IRQ_SRC_NUM

Verdict

One-past-end guard defect in DAL_VALID_IRQ_SRC_NUM (irq_types.h:157-158, '<=' instead of '<') trusted by amdgpu_dm_irq.c's register/unregister validation (:176/:193) before indexing irq_handler_list_{low,high}_tab[irq_source] (:238/:242/:123/:127), arrays sized [DAL_IRQ_SOURCES_NUMBER]=90 (amdgpu_dm.h:106-107). The guard math was proven on the DragonFly guest with a verbatim-enum harness (run.log: src=90 accepted, src=89 accepted, src=91 rejected - exactly one OOB index passes). The in-kernel path is untestable on this guest (no AMD GPU; no /dev/dri; only QEMU virtual VGA) and is latent anyway: every register caller passes in-range sources and amdgpu_dm_irq_unregister_interrupt has zero callers tree-wide. Inherited verbatim from upstream Linux v4.17.