Pin-busy bitmap indexed by unbounded caller-supplied en (no number_of_pins bound) and NULL create_generic/sync/gsl creator pointers invoked unchecked
Summary
is/set_pin_busy index the busyness bitmap with a raw uint32 en never bounded against number_of_pins; all four ASIC factory tables set create_generic/sync/gsl=NULL while advertising pin counts, called unchecked. No current caller reaches either arm (translate tables clamp en) - defense-in-depth: a future unvalidated en makes set_pin_busy a small-offset OOB heap write. Bound-at-entry fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4788 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| env.txt | β | 920 B | view raw | |
| fix.diff | β | 500 B | view raw | |
| manifest.json | β | 762 B | view raw | |
| verdict.json | β | 2.4 KB | view raw |
DF-4788 β gpio_service pin-busy bitmap indexed by unbounded en; NULL creator pointers called unchecked
What (two hardening gaps in dal_gpio_service_open / pin-busy helpers)
- Unbounded
enindexing.gpio_service.c:190-222:c const uint32_t *slot = service->busyness[id] + (en / bits_per_uint); /* is_pin_busy */ service->busyness[id][en / bits_per_uint] |= (1 << (en % bits_per_uint)); /* set_pin_busy */ service->busyness[id][en / bits_per_uint] &= ~(1 << (en % bits_per_uint)); /* set_pin_free */The bitmap is sized fromfactory.number_of_pins[id](gpio_service.c:89-94, e.g. 8 for DDC_DATA, 6 for HPD, perhw_factory_dce110.c:168-171). Nothing indal_gpio_service_open(gpio_service.c:233-241only checksbusyness[id] != NULL) enforcesen < number_of_pins[id]. An out-of-rangeenwould be an OOB heap read+write past the kcalloc'd bitmap.
Today every caller passes en obtained from the hw-translate mask switches
(hw_translate_dce110.c:41-101, bounded enum constants) or compile-time enums, so the
OOB is NOT currently reachable β dal_hw_ddc_create/dal_hw_hpd_create additionally
range-check en (hw_ddc.c:219, hw_hpd.c:149) β but the service layer itself
trusts a raw uint32_t it does not own.
- NULL creator function pointers. All four shipped ASIC factory tables set
.create_generic = .create_sync = .create_gsl = NULL(hw_factory_dce110.c:148-151,hw_factory_dce80.c:152-155,hw_factory_dce120.c:169-172,hw_factory_dcn10.c:164-167) while still advertisingnumber_of_pins[GENERIC]=7 / [SYNC]=2 / [GSL]=4.dal_gpio_service_open(gpio_service.c:254-270) calls these pointers with no NULL check β NULL function-pointer call panic. Tree-wide grep shows no caller ever opens GENERIC/SYNC/GSL pins (only DDC + HPD are opened viadal_ddc_open/ HPD IRQ registration), so this arm is latent β butnumber_of_pinsadvertising pins the factory cannot create is a trap for any future caller.
Impact
Defense-in-depth only (no demonstrated reachable path). Filed as Info.
Fix
Bound en against number_of_pins[id] at the top of dal_gpio_service_open and reject
NULL create_* pointers. The en bound + creator guards are included in
findings/poc/DF-4787/fix.diff (same function); the minimal en hunk is repeated here as
fix.diff.
Verdict
untested β hardening finding, no trigger path exists on current callers; guest has no
AMD GPU in any case.
DF-4788 VERDICT
- Status: untested (hardening / latent, no reachable path)
- Confidence: likely
- Severity filed: Info
Analysis
Unbounded en bitmap indexing (CWE-129 / CWE-787 latent)
- Sizing:
gpio_service.c:88-118βkcalloc((number_of_pins[id] + 31) / 32, 4)stored inservice->busyness[id]. - Use:
gpio_service.c:190-222βis_pin_busy/set_pin_busy/set_pin_freeindexbusyness[id][en/32]with a caller-supplieduint32_t en; no comparison againstnumber_of_pins[id]anywhere betweendal_gpio_service_open(:224) and the bitmap touch.set_pin_busyis called at:287with the rawen. - Bound sources today (why unreachable):
- DDC:
dal_gpio_create_ddc(gpio_service.c:418) getsenfromtranslate.funcs->offset_to_id()whose mask switches only emitGPIO_DDC_LINE_DDC1..I2C_PAD(dce110/hw_translate_dce110.cDDC case);dal_hw_ddc_createre-validates (hw_ddc.c:219-222) before allocating. - HPD:
dal_gpio_service_create_irq(gpio_service.c:151) β same translate mechanism, masks emitGPIO_HPD_1..6only;dal_hw_hpd_createre-validates (hw_hpd.c:144-152). dal_gpio_create_irqrejects every id except HPD/GPIO_PAD (gpio_service.c:373-381) and GPIO_PAD pins cannot open at all (no case indal_gpio_service_open).- Consequence if a future caller passes raw
en: OOB heap write (set_pin_busy) / read (is_pin_busy) β the bitmap is a small standalone kmalloc, so an overflow lands in adjacent slab objects. That is why the missing bound is worth fixing even though it is presently dead.
NULL creator pointers
hw_factory_dce110.c:144-154,hw_factory_dce80.c:150-156,hw_factory_dce120.c:167-173,hw_factory_dcn10.c:162-169:.create_generic,.create_sync,.create_gslare NULL;.define_*_registerspresent.gpio_service.c:254-270invokes them without a NULL check (NULL text pointer call β panic at address 0).- Tree-wide
rg 'GPIO_ID_GENERIC|GPIO_ID_SYNC|GPIO_ID_GSL'shows users only inside the gpio directory itself (translate tables,dal_gpio_get_sync_sourcewhich never opens); no display component opens those ids β latent.
Why untested
No AMD GPU in the audit QEMU guest (env.txt), and even on hardware there is no caller
that reaches the defective arms β this is a defense-in-depth finding by construction.
Recommended fix summary
Add en >= service->factory.number_of_pins[id] β GPIO_RESULT_INVALID_DATA at the top of
dal_gpio_service_open, and NULL-check the create_* pointers (already included in
DF-4787/fix.diff).
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_testableone-line bounds check; no behavioral change for any current caller (all pass in-range enums); cannot be boot-validated without AMD hardware
findings/poc/DF-4788/fix.diff
Confirmed kernel references
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:190
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:202
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:213
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:233
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:254
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:263
- sys/dev/drm/amd/display/dc/gpio/gpio_service.c:267
- sys/dev/drm/amd/display/dc/gpio/dce110/hw_factory_dce110.c:148
Detail
Evidence (decisive lines)
README.md, VERDICT.md (bounds trace), env.txt (guest pci inventory), fix.diff (minimal en-bound hunk)
PoC changes
no PoC produced β no reachable trigger path exists in current callers; hardware-gated besides
Verified recommended fix
Bound en against factory.number_of_pins[id] in dal_gpio_service_open and NULL-check the create_* factory pointers
Verdict
Latent hardening gaps in dal_gpio_service_open: (1) is_pin_busy/set_pin_busy/set_pin_free index the number_of_pins-sized busyness bitmap with a raw caller-supplied uint32_t en that is never bounds-checked in the service layer (gpio_service.c:197-221 vs sizing at :89-94) β an out-of-range en would be an OOB heap read/write, but every current caller passes translate-table-bounded enum constants so the path is unreachable today; (2) .create_generic/.create_sync/.create_gsl are NULL on all four shipped ASIC factory tables yet are invoked without NULL checks (gpio_service.c:254-270) β no display code ever opens those pin classes, so the NULL call is also latent. Filed as defense-in-depth; not exercisable on the audit guest (no AMD GPU).
No comments yet.