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

Proximity-domain association compares only ProximityDomainLo (ignores ProximityDomainHi and x2APIC entries): NUMA domains >= 256 silently dropped or organized under the wrong socket

Summary

The memory-to-CPU association compares a u32 domain against the CPU entry's single Lo byte, ignoring Hi[3] and never handling Type-2 x2APIC entries: domains >=256 never match ('not found', ranges silently ignored) or collide with an unrelated socket's low byte (pages queued under the wrong NUMA domain). Downstream bounded - correctness/silent-NUMA-misdirection gap, no memory safety. full-domain-compare + x2APIC case fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5651 Β· 2 files
FileTypeDescriptionSize
README.md file 1.9 KB ↓ raw
verdict.json file 2.4 KB view raw
README.md file
↓ download raw

DF-5651 β€” SRAT proximity-domain association compares only the low byte (ProximityDomainLo), ignoring ProximityDomainHi and x2APIC entries

What

sys/platform/pc64/acpica/acpi_srat.c:112-113 and :124-125 associate a MEMORY affinity entry with a CPU affinity entry by comparing the memory entry's full 32-bit ProximityDomain (packed u32@+2) against the CPU entry's one-byte ProximityDomainLo (packed u8@+2):

if (cpu == NULL ||
    mem->mem.ProximityDomain != cpu->cpu.ProximityDomainLo) {   /* :113 */

The rev-1 CPU entry's upper three domain bytes (ProximityDomainHi[3], actbl3.h:377, packed @+9) are never consulted, and rev-2+ x2APIC CPU entries (ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY, Type 2, 32-bit domain + 32-bit APIC ID) are not handled at all (:118-120 dispatches only Type 0).

Effect (correctness/perf, no memory-safety impact)

  • Any proximity domain >= 256 can never match a rev-1 CPU entry whose low byte happens to differ β†’ the range prints "(not found)" (acpi_srat.c:140) and vm_numa_organize() is never called for it.
  • Worse, a domain like 0x0100 collides with Lo=0x00 of an unrelated socket β†’ memory is organized under the wrong chip's page-color domain (vm_page.c:546-547), cross-node memory placement for the machine's life.
  • On systems whose firmware only emits x2APIC CPU entries (legal since ACPI 4.0, required for APIC IDs > 255), no CPU entry ever matches and no SRAT memory range is ever organized β€” the NUMA code silently no-ops.

No memory corruption, leak, or privilege boundary is crossed: physid derived from the matched ApicId (u8) is bounded (mod cpu_topology_phys_ids, vm_page.c:547). Filed as Info (correctness gap / silent NUMA misdirection), untested on hardware that exposes it.

Suggested fix

Compare the CPU entry's full 20-bit domain (ProximityDomainHi[3]<<8 | ProximityDomainLo) against mem->mem.ProximityDomain, and handle Type-2 x2APIC entries' 32-bit domain/APIC ID.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Info-severity correctness gap; no fix build warranted (single-socket QEMU rig has cpu_topology_phys_ids==1 so vm_numa_organize is a no-op and misdirection is unobservable).

per-fix-DF-5651

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md cites acpi_srat.c:112-113,124-125 (Lo-only compare), :118-120 (Type-0-only dispatch), actbl3.h:377 (ProximityDomainHi), vm_page.c:534-547 (bounded downstream use).

PoC changes

No PoC authored (Info; single-socket rig cannot manifest NUMA misdirection).

Verified recommended fix

Compare full 20-bit domain (Hi[3]<<8|Lo) and handle ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY entries' 32-bit domain and APIC ID.

Verdict

Static-analysis finding (Info severity, no security impact): the memory-to-CPU affinity association compares the 32-bit memory ProximityDomain against the CPU entry's single ProximityDomainLo byte (acpi_srat.c:113,124-125), ignoring ProximityDomainHi[3] (actbl3.h:377) and the Type-2 x2APIC entries entirely (:118-120). Domains >= 256 either never match (range dropped, '(not found)', vm_numa_organize never called) or collide with an unrelated socket's low byte (pages organized under the wrong NUMA domain, vm_page.c:546-547). Downstream values are bounded (ApicId u8, physid mod cpu_topology_phys_ids), so this is a correctness/silent-NUMA-misdirection gap, not memory corruption. Not verified on the guest: the QEMU rig is single-socket (cpu_topology_phys_ids == 1, vm_numa_organize early-returns at vm_page.c:534) so no observable NUMA placement exists to misdirect.