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

dc_link_ddc: unchecked dal_ddc_aux/i2c_payloads_create() NULL in dal_ddc_service_query_ddc_data β€” OOM-gated (and zero-size-deterministic) NULL deref

Summary

Both create results are dereferenced via payloads_get (reads container at offset 0) with no NULL check; the creators return NULL on kzalloc OOM or when the vector ctor rejects capacity==0 (deterministic when both sizes are 0 - no in-tree caller does). OOM-gated panic during DDC transactions on AMD DC hardware. DF-4555's still-unfixed ignored-acquire at :655 confirmed present and deliberately not re-filed (already claimed, fix.diff covers it). NULL-check fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4663 Β· 7 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
fix.diff β€” 651 B view raw
run.log β€” 329 B view raw
env.txt β€” 329 B view raw
manifest.json β€” 627 B view raw
verdict.json β€” 2.9 KB view raw

DF-4663 β€” dc_link_ddc: unchecked payloads_create() NULL in dal_ddc_service_query_ddc_data

dal_ddc_service_query_ddc_data() (dc_link_ddc.c:552-633) calls dal_ddc_aux_payloads_create(ddc->ctx, payloads_num) (:580-581) or dal_ddc_i2c_payloads_create(...) (:606-607) and immediately dereferences the result via dal_ddc_aux_payloads_get(payloads) (:584) / dal_ddc_i2c_payloads_get(payloads) (:610) β€” reading payloads->payloads.container (offset 0, include/vector.h:29-35) β€” with no NULL check.

Both creators return NULL when: * the 16-byte kzalloc fails (GFP_KERNEL, OOM), or * dal_vector_construct() rejects capacity == 0 (basics/vector.c:33-37) β€” i.e. whenever write_size == 0 && read_size == 0 β€” or its kcalloc fails.

The zero-size case is not reachable from the current in-tree callers (all pass non-empty write or read sizes: dc_link_ddc.c:712-754 SCDC, dc_link.c:1641/1694 retimer (1,1)), so today this is an OOM-gated latent NULL-deref panic inside the amdgpu display driver. Filed as hardening (defense-in-depth; the sibling engine paths DF-4555-series showed this subsystem's allocation/error discipline is security-relevant).

Run / verification

Hardware-gated: the code only executes on an AMD DC GPU. The audit guest has none β€” pciconf -l | grep -c 1002 = 0 (run.log / env.txt), Bochs VGA only. Not reproducible there; defect is code-provable (cited lines).

Fix

fix.diff (git-apply verified): check both create results, return false on failure β€” consistent with the file's own bound check at :573-574.

VERDICT.md
↓ download raw

DF-4663 β€” VERDICT

not_reproduced (hardware-gated) β€” Info-severity hardening finding; defect code-provable.

Root cause (certain, from source)

dal_ddc_service_query_ddc_data (sys/dev/drm/amd/display/dc/core/dc_link_ddc.c:552):

580:  struct aux_payloads *payloads =
581:      dal_ddc_aux_payloads_create(ddc->ctx, payloads_num);
...
584:  .payloads = dal_ddc_aux_payloads_get(payloads),   /* NULL->payloads.container */

and the i2c twin at :606-610. dal_ddc_aux_payloads_create (:170-185) returns NULL when kzalloc(sizeof(struct aux_payloads)) fails or when dal_vector_construct fails β€” which it deterministically does for capacity == 0 (if (!struct_size || !capacity) ... return false, basics/vector.c:33-37, i.e. write_size == 0 && read_size == 0) and when its kcalloc fails. dal_ddc_aux_payloads_get(NULL) then reads NULL->payloads.container (container is the first member of struct vector, include/vector.h:29-35) β†’ near-NULL read β†’ panic.

Why not reproduced

The function only executes on amdgpu display hardware (driver core path). The audit guest: pciconf -l | grep -c 1002 β†’ 0 (no AMD device; Bochs 0x1234:0x1111 VGA) β€” run.log. Same gating as DF-4555/DF-4556.

Reachability analysis

  • OOM path: any amdgpu DC system under extreme memory pressure during a DDC transaction (EDID read / SCDC write / retimer query) β†’ NULL deref panic. Requires GFP_KERNEL failure of a 16-byte alloc β€” rare but real.
  • Zero-size path: payloads_num == 0 requires write_size==0 AND read_size==0. All current callers pass non-zero sizes (SCDC paths :712-754; retimer dc_link.c:1641/:1694 pass (1,1)) β†’ not reachable in-tree today; any future caller with both sizes zero panics deterministically. This makes the missing check a latent trap, hence Info severity.

Fix

fix.diff adds if (!payloads) return false; after both creators (git-apply --check verified against the pristine tree; never applied to sys/). No fix validation run: the defective path requires absent hardware, and the patch is a two-line guard whose semantics are total. fix_status: fixed (author-verified apply + logic), fix runs not testable on this guest β†’ fix_baseline_reproduced=0, fix_patched_reproduced=0.

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

fix.diff is a total two-line/one-line guard (git-apply --check verified against the pristine tree; never applied to sys/). No runtime fix validation: the defective path requires AMD DC hardware absent from the audit guest (0x1002 PCI count = 0).

fix.diff in pack; run.log/env.txt hardware inventory.
↓ fix.diffper-fix-DF-4663

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md root-cause citations (dc_link_ddc.c:580-584,:606-610; basics/vector.c:33-37; include/vector.h:29-35); run.log hardware inventory (no AMD device).

PoC changes

No executable PoC (hardware-gated); evidence pack documents the source trace and the guest hardware absence.

Verified recommended fix

NULL-check both payloads create results and return false (fix.diff).

Verdict

Code-provable latent NULL deref: dal_ddc_service_query_ddc_data (dc_link_ddc.c:580-584 aux, :606-610 i2c) dereferences the result of dal_ddc_aux_payloads_create / dal_ddc_i2c_payloads_create without a NULL check. Both creators return NULL when the 16-byte kzalloc fails (OOM) or when dal_vector_construct rejects capacity==0 (write_size==0 && read_size==0 β€” basics/vector.c:33-37); the immediate dal_ddc_*_payloads_get(NULL) then reads NULL->payloads.container (struct vector first member, include/vector.h:29-35) -> near-NULL read -> panic. Zero-size case not reachable from current in-tree callers (SCDC :712-754, retimer dc_link.c:1641/:1694 all pass non-zero sizes), so today the path is OOM-gated inside the amdgpu DC driver. Not reproduced: the audit guest has no AMD GPU (pciconf 0x1002 count = 0, run.log) β€” same hardware gating as DF-4555/4556. Hardening finding; fix.diff adds the two missing NULL guards.