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

patch_gamut_packet_checksum reads up to 227 bytes past dc_info_packet.sb[32] (length byte unbounded; dead path β€” no producer ever sets gamut.valid)

Summary

The checksum loop iterates to the data byte sb[1] (<=255) against uint8_t sb[32] - reads up to 227 bytes past the struct. Currently dead: no producer ever sets gamut.valid=true (grep-verified); consumers exist in dce_stream_encoder. Filed as Info hardening (same dead-API treatment as DF-1300): if a future DC consumer wires gamut emission, this becomes a slow kernel-memory info-leak oracle into HDMI infoframes. Bound-the-loop fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3944 Β· 6 files
FileTypeDescriptionSize
check_gamut_dead.sh β€” 766 B view raw
run.log β€” 884 B view raw
env.txt β€” 68 B view raw
VERDICT.md β€” 1.7 KB ↓ raw
fix.diff β€” 572 B view raw
verdict.json β€” 1.9 KB view raw
VERDICT.md
↓ download raw

VERDICT β€” DF-3944

Bottom line

not_reproduced β€” provably dead path in this tree; latent OOB read (Info).

Root cause

dc_resource.c:2152-2155: ptr = &gamut_packet->sb[3]; for (i = 0; i <= gamut_packet->sb[1]; i++) / sb[1] ∈ [0,255] / chk_sum += ptr[i]; struct dc_info_packet is {... uint8_t sb[32]; } (dc_types.h:524-531). Maximum index touched = 3 + 255 = 258 β†’ 227 bytes OOB read; the summed byte lands in sb[2] and would be transmitted as an HDMI Gamut infoframe checksum.

Why dead (path:line)

  • Only caller: resource_build_info_frame() dc_resource.c:2651, on &pipe_ctx->stream_res.encoder_info_frame.gamut which the same function invalidated at :2625 (info->gamut.valid = false).
  • patch_gamut_packet_checksum() early-returns unless gamut_packet->valid (dc_resource.c:2146).
  • No in-tree producer ever sets encoder_info_frame.gamut.valid = true (guest-wide grep returned 0 matches β€” run.log). Consumers exist (dce_stream_encoder.c:771,784; dcn10_stream_encoder.c:646) but only read. Filed at Info (same "dead API, latent hazard" treatment as DF-1300), with the bounds fix in fix.diff so any future producer cannot resurrect the OOB read.

Guest run

run.log = output of check_gamut_dead.sh against the guest's /usr/src (uname in env.txt). No AMD GPU in the guest; no runtime reproduction claimed.

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

path dead in-tree; fix.diff applies clean and bounds the read for any future producer

fix.diff; git apply --check clean
↓ fix.diffper-fix-DF-3944

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (guest-wide grep: 0 producers; vulnerable loop; sb[32] size), check_gamut_dead.sh, fix.diff (git-apply-clean)

PoC changes

no seed; static presence checker

Verified recommended fix

bound the checksum loop by sizeof(sb)-3 (fix.diff) or delete the dead gamut emission path

Verdict

Latent OOB read: patch_gamut_packet_checksum (dc_resource.c:2152-2155) sums ptr[i] for i up to sb[1] (max 255) starting at &sb[3] of a 32-byte array -> up to 227 bytes past struct dc_info_packet. Path is dead in this tree: the only assignment to the gamut packet's valid flag is info->gamut.valid=false (dc_resource.c:2625) and a tree-wide grep finds no producer setting it true; the checksum patcher early-returns on !valid (:2146). Filed as Info hardening with an array-bounds fix; upstream DC later dropped gamut emission entirely.