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

DCN10 copy of the uninitialized-*id defect: offset_to_id returns success for all eight DDC-family offsets (165-189) without writing *id β€” dal_gpio_service_create_irq switches on the uninitialized gpio_id; garbage==GPIO_ID_HPD smuggles DDC-line en=6 (DDC_VGA) / en=7 (I2C_PAD) into the 6-entry hpd_regs[] (OOB)

Summary

The DCN10 (Raven) unit carries the same contract violation as DF-4943/DF-4945 (proven verbatim with real DCN10 constants on the guest: id==0x7b7b7b7b UNTOUCHED across all 8 offsets, en=6/7 flagged). The dce80 GPIOPAD cross-domain producer is ABSENT here (harness-verified rejected) - the dcn10 factory has no aux_regs and the counts are consistent. Write-*id-in-DDC-branches family fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4946 Β· 12 files
FileTypeDescriptionSize
README.md β€” 1.4 KB ↓ raw
VERDICT.md β€” 4.3 KB ↓ raw
harness_dcn10.c β€” 17.7 KB view raw
harness_fixed.c β€” 6.5 KB view raw
b1.sh β€” 312 B view raw
r1.sh β€” 175 B view raw
build.log β€” 1.0 KB view raw
run.log β€” 4.2 KB view raw
env.txt β€” 166 B view raw
fix.diff β€” 1.1 KB view raw
manifest.json β€” 997 B view raw
verdict.json β€” 4.7 KB view raw

DF-4946 β€” DCN10 offset_to_id returns success on all DDC-family offsets without writing *id

File: sys/dev/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c:165-189 Family: DF-4943 (dce80) / DF-4945 (dce120) β€” this is the DCN10 (Raven) twin. Severity: Low (memcorrupt bucket) Β· CWE-457

Build

gcc -O2 -Wall -Wextra -o harness_dcn10 harness_dcn10.c
gcc -O2 -Wall -Wextra -o harness_fixed harness_fixed.c
gcc -O2 -Wall -Wextra -o harness_mask harness_mask.c   # DF-4947 side-car

Run

./harness_dcn10

Expected

All eight DDC-family register offsets (REG(DC_GPIO_DDC{1..6}_A) = 0x5d91..0x5da5, REG(DC_GPIO_DDCVGA_A) = 0x5da9, REG(DC_GPIO_I2CPAD_A) = 0x5dc1) return true after writing only *en β€” *id is left untouched (sprayed 0x7b7b7b7b survives). en=6 (DDC_VGA) and en=7 (I2C_PAD) exceed the 6-entry hpd_regs[] of hw_factory_dcn10.c:71-78, so a garbage id == GPIO_ID_HPD(3) decodes into define_hpd_registers β†’ &hpd_regs[6..7] OOB const-array read whose garbage register descriptors then drive MMIO. harness_fixed applies the fix (*id = GPIO_ID_DDC_DATA in every DDC branch) and reports 0 residual violations.

The kernel stage needs a Raven-class ASIC + crafted VBIOS gpio_pin_lut entry (absent in QEMU β€” 0 AMD devices, env.txt); the deterministic contract violation is proven at unit level with the verbatim function and real DCN10 constants.

VERDICT.md
↓ download raw

DF-4946 VERDICT β€” reproduced (deterministic unit-level; kernel stage needs Raven HW + crafted VBIOS)

What was run

harness_dcn10.c embeds verbatim copies of offset_to_id and id_to_offset from sys/dev/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c (lines 61-201, 203-388) with the single substitution ASSERT_CRITICAL(x) β†’ printf marker. Constants are the real DCN10 values: DCE_BASE__INST0_SEG2 = 0x34C0 (vega10_ip_offset.h:277) and the mmDC_GPIO_* offsets/BASE_IDX from dcn_1_0_offset.h:10349-10584. Enums are the real gpio_types.h values (GPIO_DDC_LINE_I2C_PAD aliases VIP_PAD = 7; COUNT = 8).

Result (run.log)

  • T1: all 8 DDC-family offsets return true with *id untouched (id==0x7b7b7b7b UNTOUCHED on every line). DDC1..6 β†’ en 0..5, DDCVGA β†’ en=6, I2CPAD β†’ en=7. hpd_regs[] in hw_factory_dcn10.c:71-78 has exactly 6 entries (indices 0..5) β€” en 6/7 are out of bounds.
  • T2 (negative): raw mmGPIOPAD-style offset 0x183 (the DF-4942 dce80 deterministic producer) is rejected β€” DCN10 has no GPIOPAD case, so the cross-domain producer is absent; REG(DC_GPIO_DEBUG) correctly returns false.
  • T4 (negative): the HPD branch is well-formed (writes *id, en 0..5).

Consumer chain (source-cited)

  1. get_hpd_gpio (core/dc_link.c:96-119) calls dal_gpio_service_create_irq with pin_info.offset/mask taken from the VBIOS atom gpio pin table (dcb->funcs->get_gpio_pin_info, bios_parser2.c:537-539).
  2. dal_gpio_service_create_irq (gpio_service.c:143-157) declares enum gpio_id id; uninitialized and passes &id to offset_to_id; on true it forwards the garbage id + DDC-line en to dal_gpio_create_irq.
  3. dal_gpio_create_irq (gpio_service.c:366-391) accepts GPIO_ID_HPD; dal_gpio_create β†’ dal_gpio_open_ex β†’ dal_gpio_service_open (gpio_service.c:224-290): busyness[HPD] is 1 uint (number_of_pins[HPD]=6, hw_factory_dcn10.c:186) so is_pin_busy(en=6/7) stays inside the allocation, and if the bit is clear β†’ create_hpd + define_hpd_registers(pin, 6/7).
  4. define_hpd_registers (hw_factory_dcn10.c:149-157) evaluates &hpd_regs[en] β€” OOB read of the 6-entry const array; the garbage struct hpd_registers (register offsets read past the array into adjacent rodata) then drives dal_hw_gpio_open/hw_hpd MMIO programming (wrong-register writes / reads at attacker-uncontrolled MMIO offsets) β†’ misbehavior to panic (dos). The final stage is probabilistic (depends on the uninitialized stack slot decoding to 3).

The primary DDC consumer, dal_gpio_create_ddc (gpio_service.c:408-457), ignores id and hardcodes GPIO_ID_DDC_DATA/CLOCK; with DCN10's ddc_data_regs[8]/ddc_clk_regs[8] (hw_factory_dcn10.c:94-114) and number_of_pins[DDC_*]=8 (hw_factory_dcn10.c:183-184), en 0..7 are in-bounds β€” so, unlike dce80 (DF-4942), the DDC happy path itself is safe on DCN10. The uninit-*id defect matters only through the IRQ path above.

Why not kernel-stage on this guest

env.txt: 0 AMD PCI devices in the QEMU guest β€” no amdgpu/DC attach, no VBIOS under test. Same situation as the verified family twins DF-4943/4945; their precedent (unit-level deterministic proof + cited kernel chain) is followed exactly.

Fix validation

fix.diff adds *id = GPIO_ID_DDC_DATA; to all eight DDC-family branches. harness_fixed.c applies the same edit to the embedded copy: every DDC-family success now reports id=0 (WRITTEN), and with id==GPIO_ID_DDC_DATA dal_gpio_create_irq takes its default branch and returns NULL β€” define_hpd_registers can never be reached with en=6/7 (0 residual violations, FIXEXIT=0). A kernel rebuild cannot alter the output of a userland unit harness; structural validation matches the family precedent (DF-4943/4945 fix_status=validated).

fix.diff applies cleanly?

Authored against the read-only sys/ tree at hw_translate_dcn10.c:165-189; hunks are one-line insertions on distinct case labels β€” no context drift.

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 reproduced→ patch + rebuild →patched clean

fix.diff adds *id = GPIO_ID_DDC_DATA to all eight DDC-family branches of the dcn10 offset_to_id; harness_fixed applies the identical edit to the embedded copy and demonstrates the uninitialized id is structurally eliminated (all 8 report id=0 WRITTEN) β€” with id==GPIO_ID_DDC_DATA, dal_gpio_create_irq (gpio_service.c:373-381) returns NULL, so define_hpd_registers can never see en=6/7: 0 residual violations (FIXEXIT=0). A kernel rebuild cannot change userland unit-harness output; structural validation matches the family precedent (DF-4943/4945).

fix.diff (hw_translate_dcn10.c hunks); run.log 'FIXED T1' section; harness_fixed.c e781a6d68365136864cb903683e6274e967e24514209508af01df7665cd3cc0d.
↓ fix.diffper-fix-DF-4946

Confirmed kernel references

Detail

Exploit chain

crafted VBIOS gpio_pin_lut v2.1 (data_a_reg_index = REG(DC_GPIO_DDCVGA_A)=0x5da9 or REG(DC_GPIO_I2CPAD_A)=0x5dc1, bios_parser2.c:537) -> get_hpd_gpio (dc_link.c:115) -> dal_gpio_service_create_irq -> offset_to_id true, id UNWRITTEN -> garbage id==GPIO_ID_HPD(3) -> dal_gpio_service_open(HPD, en=6/7) -> define_hpd_registers -> hpd_regs[6..7] OOB rodata read -> hw_gpio/hw_hpd MMIO at garbage offsets -> wrong-register programming/panic. Probabilistic final stage; no unpriv-user local route (requires GPU VBIOS control).

Evidence (decisive lines)

run.log T1: 'offset_to_id(REG(DDCVGA),0x100) = true en=6(id==0x7b7b7b7b UNTOUCHED)' and '(REG(I2CPAD),0x100) = true en=7(id==0x7b7b7b7b UNTOUCHED)' with the hpd_regs[6]/[7] OOB annotations; T2: 0x183 rejected (no GPIOPAD mapping β€” dce80-style producer absent); T4: HPD branch well-formed; fixrun section: harness_fixed reports id=0 WRITTEN on all 8 and 0 residual violations.

PoC changes

n/a β€” fresh harness (verbatim dcn10 offset_to_id + id_to_offset copies, real DCN10 constants: SEG2 base 0x34C0, mmDC_GPIO_DDC1_A=0x28d1 .. I2CPAD_A=0x2901, all BASE_IDX 2).

Verified recommended fix

Write *id (GPIO_ID_DDC_DATA) in every successful offset_to_id DDC-family branch (dcn10 file), matching the validated dce80/dce120 family fix.

Verdict

The DCN10 copy of offset_to_id (hw_translate_dcn10.c:165-189) has the family defect of DF-4943/DF-4945: all eight DDC-family branches (DDC1-6, DDCVGA, I2CPAD) return true after writing only en β€” id is never assigned. dal_gpio_service_create_irq (gpio_service.c:148-156) declares 'enum gpio_id id;' uninitialized and switches on the garbage value in dal_gpio_create_irq; garbage==GPIO_ID_HPD(3) with the DDC-line en of 6 (DDC_VGA) or 7 (I2C_PAD) reaches define_hpd_registers, which indexes the 6-entry hpd_regs[] (hw_factory_dcn10.c:71-78,149-157) out of bounds β€” garbage register descriptors then drive MMIO (hw_hpd/hw_gpio open path). Executed on the DF guest with a verbatim copy of the dcn10 function and real DCN10 constants (REG(DDCVGA)=0x5da9, REG(I2CPAD)=0x5dc1 β€” run.log T1: both return true with *id untouched; en=6/7 exceed hpd_regs). Kernel-stage execution requires a Raven-class ASIC + crafted VBIOS gpio_pin_lut (absent in QEMU: 0 AMD devices). Unlike dce80 there is no GPIOPAD case (T2 negative control), so no deterministic DF-4942 producer exists on DCN10; the primary dal_gpio_create_ddc consumer ignores id and its en 0..7 are in-bounds against the 8-entry ddc arrays β€” the uninit-id path via the HPD IRQ route is the only OOB route and is probabilistic.