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

Signed table_id bounds check allows OOB heap read/write in SMU10 table copy

Summary

smu10_copy_table_from_smc (smu10_smumgr.c:116) and _to_smc (line 144) take int16_t table_id; guard with signed "table_id < MAX_SMU_TABLE(=2)" at 121/149. Public API contract (hwmgr.h:218, smumgr.h:114, local wrapper smu10_smc_table_manager at 277) declares uint16_t. Caller passing table_id>=0x8000 wraps to negative int16_t, passes signed <2 check, indexes priv->smu_tables.entry[negative] OOB: _from_smc reads OOB entry fields (table ptr, size) then memcpy to caller buffer (arbitrary heap read); _to_smc writes attacker bytes to OOB-derived destination (arbitrary heap write). Identical class to DF-2034 (vega10) and DF-2021 (vega12). All current callers (smu10_hwmgr.c:427 SMU10_CLOCKTABLE=1, :1141 SMU10_WMTABLE=0) pass compile-time constants [0,1] so NOT directly triggerable from userspace today. Latent: one future/fuzzy caller with table_id>=0x8000 -> kernel heap corruption -> local priv-esc. Mailbox timeout robustness note: smu10_wait_for_response discards phm_wait_for_register_unequal return; send_msg always returns 0 -> silent firmware failure but subsequent memcpy uses kernel-set sizes no OOB. Lifecycle clean (init sets backend after kzalloc, fini NULLs). Fix: int16_t -> uint16_t parameter type.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2072 Β· 7 files
FileTypeDescriptionSize
VERDICT.md verdict Full source-trace narrative (uint16_t/int16_t contract mismatch) + fix validation 3.8 KB ↓ raw
README.md readme Original latent-primitive description 1.1 KB ↓ raw
fix.diff suggested-fix git-apply-able: int16_t -> uint16_t in both copy functions 705 B view raw
build.sh reproduce Combined kernel build script 755 B view raw
run.sh reproduce Source-only confirmation; no runtime PoC 543 B view raw
fix_build.log build-log Full untrimmed combined kernel build (rc=0, 0 errors) 5.6 MB ↓ download
env.txt environment uname, kern.version, cc version 501 B view raw
README.md readme Original latent-primitive description
↓ download raw

DF-2072 PoC β€” SMU10 signed table_id OOB (latent / sibling of DF-2021/DF-2034)

Status: VERIFIED (source-only) + FIX VALIDATED

smu10_copy_table_from_smc/_to_smc take int16_t table_id (smu10_smumgr.c:116/144) while their public API contract is uint16_t (smumgr.h:114, hwmgr.h:218, local wrapper at :277). A caller passing table_id=0xFFFF narrows to int16_t -1, passes the signed table_id < MAX_SMU_TABLE(=2) guard, and indexes priv->smu_tables.entry[-1] OOB. .size controls memcpy length, .table/.mc_addr control source/destination.

Latent: both SMU10 in-tree callers pass compile-time constants in [0,1]. Same pattern as DF-2021 (vega12) and DF-2034 (vega10).

See VERDICT.md for the source trace and fix validation.

Reproduce

./build.sh   # rebuilds the patched kernel (rc=0 with -Werror)
./run.sh     # source-only confirmation; no runtime PoC (latent)

Fix

fix.diff widens the parameter type from int16_t to uint16_t in both copy functions, matching the public API contract. The PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE, ...) guard then operates on an unsigned value, so 0xFFFF < 2 correctly fails.

VERDICT.md verdict Full source-trace narrative (uint16_t/int16_t contract mismatch) + fix validation
↓ download raw

DF-2072 β€” REPRODUCED (source-confirmed, latent) + FIX VALIDATED

Verdict

REPRODUCED via source-only trace. The signed int16_t table_id parameter in smu10_copy_table_from_smc / _to_smc lets a caller-passed uint16_t value of 0xFFFF narrow to int16_t -1, sail through the table_id < MAX_SMU_TABLE guard, and index the entry[] array at [-1] OOB. Same pattern as DF-2021 (vega12) and DF-2034 (vega10). Latent (no in-tree caller forwards an attacker-controlled table_id).

Mechanism (path:line)

The two copy functions take int16_t table_id but their public API contract is uint16_t:

The bound check uses signed comparison:

  • smu10_smumgr.c:121 and :149 c PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE, "Invalid SMU Table ID!", return -EINVAL;);

MAX_SMU_TABLE is 2 (smu10_smumgr.h:30) and entry[] is a 2-element array (smu10_smumgr.h:42).

The public API contract is uint16_t in three places:

So when smu10_smc_table_manager (uint16_t) calls smu10_copy_table_from_smc (int16_t) with table_id = 0xFFFF, the implicit narrowing converts 0xFFFF to int16_t -1. Then -1 < MAX_SMU_TABLE(=2) is true (signed compare), so the guard passes, and priv->smu_tables.entry[-1] is read OOB. The OOB .size controls the memcpy length; the OOB .table/.mc_addr control source and destination. Net: arbitrary kernel heap read (_from_smc) or heap write (_to_smc).

Reachability / impact ceiling

Latent. Both SMU10 callers (smu10_hwmgr.c:427 with SMU10_CLOCKTABLE=1 and :1141 with SMU10_WMTABLE=0) pass compile-time constants in [0,1]. No sysfs/ioctl/debugfs path forwards a user-controlled table_id for the SMU10 driver. If a future code path routed a user- or firmware-controlled 16-bit selector through smum_smc_table_manager(hwmgr, buf, 0xFFFF, true), the result would be a heap OOB read/write β€” the same primitive as DF-2021/DF-2034.

Fix

fix.diff widens the parameter type from int16_t to uint16_t in both copy functions, matching the public API contract. The PP_ASSERT_WITH_CODE(table_id < MAX_SMU_TABLE, ...) guard now operates on an unsigned value, so 0xFFFF < 2 correctly fails and returns -EINVAL before the OOB index.

Matches the finding markdown's recommendation (int16_t β†’ uint16_t parameter type in both copy functions).

Phase-8 build validation

Combined kernel + modules build (DF-2068 / DF-2069 / DF-2070 / DF-2071 / DF-2072) on DragonFly 6.5-DEVELOPMENT #0 baseline:

  • === NK_DONE rc=0 === (2026-07-25 11:31:20 UTC)
  • 0 error: lines in the full 35,696-line build log
  • smu10_smumgr.c is part of amdgpu.ko; the patched TU compiled clean with the module's default -Wno-pointer-sign -Werror flags and linked into amdgpu.ko (verified: smu10_smumgr.o present).

The default kernel build IS a -Werror build; see fix_build.log and env.txt.

Reproduce

./build.sh    # rebuilds the patched kernel (rc=0 with -Werror)
./run.sh      # source-only confirmation; no runtime PoC (latent)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: combined kernel build rc=0 -Werror

VALIDATED: combined kernel build rc=0 -Werror
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

Source-confirmed: smu10_copy_table_from/to_smc int16_t table_id vs uint16_t API contract. Latent (callers pass constants).

Verified recommended fix

Source-confirmed: smu10_copy_table_from/to_smc int16_t table_id vs uint16_t API contract. Latent (callers pass constants).

Verdict

Source-confirmed: smu10_copy_table_from/to_smc int16_t table_id vs uint16_t API contract. Latent (callers pass constants).