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

Unvalidated hypervisor sq_idx causes stack buffer overflow in ena_com_ind_tbl_convert_from_device

Summary

ena_com_create_io_sq at ena_com.c:1156: io_sq->idx=cmd_completion.sq_idx (u16 0-65535 from hypervisor admin completion, NO bounds check vs ENA_TOTAL_NUM_QUEUES=256). ena_com_ind_tbl_convert_from_device at :1205-1206: dev_idx_to_host_tbl[ena_dev->io_sq_queues[i].idx]=i. dev_idx_to_host_tbl is u16[256]=512B on stack. sq_idx>=256 -> stack buffer overflow with attacker-controlled offset. Also affects io_cq->idx at :1333 (cq_idx). Sibling of DF-1221. Latent: ena_com_indirect_table_get not wired in current ena.c (only _set is wired at :2180 which validates). Fix: validate sq_idx/cq_idx<ENA_TOTAL_NUM_QUEUES in create handlers, bounds-check in convert_from_device.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1294 Β· 8 files
FileTypeDescriptionSize
README.md readme finding summary + latency note 3.4 KB ↓ raw
VERDICT.md verdict mechanism + citations + latency + fix 2.4 KB ↓ raw
fix.diff suggested-fix validate sq_idx/cq_idx<ENA_TOTAL_NUM_QUEUES after assignment + bounds-check in convert_from_device 1.3 KB view raw
build.sh build-script no PoC binary 357 B view raw
run.sh run-script no runtime PoC (ENA absent) 322 B view raw
env.txt environment guest PCI/kld/uname 862 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme finding summary + latency note
↓ download raw

DF-1294 β€” ena_com sq_idx/cq_idx unvalidated β†’ stack buffer overflow

Finding

ena_com_create_io_sq at sys/dev/virtual/amazon/ena/ena-com/ena_com.c:1156 stores cmd_completion.sq_idx (a u16 from the hypervisor admin completion) into io_sq->idx with no bounds check. The same defect is on the CQ path at line 1333: io_cq->idx = cmd_completion.cq_idx;.

ena_com_ind_tbl_convert_from_device at lines 1205-1206 then indexes a stack-local array with that attacker-controlled idx:

u16 dev_idx_to_host_tbl[ENA_TOTAL_NUM_QUEUES] = { ... };  // 256 entries = 512 B on stack
for (i = 0; i < ENA_TOTAL_NUM_QUEUES; i++)
    dev_idx_to_host_tbl[ena_dev->io_sq_queues[i].idx] = i;   // OOB write

ENA_TOTAL_NUM_QUEUES = 2 * ENA_MAX_NUM_IO_QUEUES = 256 (ena_com.h:44-46). An sq_idx of e.g. 65535 from a malicious hypervisor writes i (u16) at offset (65535 - 256) * 2 = ~130 KB past the stack array β‡’ stack buffer overflow with attacker-controlled offset.

Latency / reachability note

ena_com_indirect_table_get (the only caller of ena_com_ind_tbl_convert_from_device, ena_com.c:2613) is NOT wired in the current ena.c driver β€” only ena_com_indirect_table_set (which routes through the bounds-checking ena_com_ind_tbl_convert_to_device) is wired at ena.c:2180. So the OOB-write sink is currently dead at runtime. The idx assignment itself (io_sq->idx = cmd_completion.sq_idx at line 1156) is reached during normal queue creation whenever an ENA NIC is present.

Why we did not reproduce at runtime

The audit guest has no Amazon ENA NIC (the ENA device is an AWS nitro virtual NIC; not present in this QEMU/KVM VM). The ena driver is built as a module and is not loaded (kldstat -v shows no ena entry), and is not in X86_64_GENERIC. So neither the create_sq path nor the indirect-table-get path runs on this guest.

Source-level confirmation

  • ena_com.c:1156 β€” io_sq->idx = cmd_completion.sq_idx; (no bounds check vs ENA_TOTAL_NUM_QUEUES=256).
  • ena_com.c:1200 β€” u16 dev_idx_to_host_tbl[ENA_TOTAL_NUM_QUEUES] is a 512-B stack array.
  • ena_com.c:1205-1206 β€” dev_idx_to_host_tbl[ena_dev->io_sq_queues[i].idx] = i; β€” OOB if idx β‰₯ 256.
  • ena_com.c:1333 β€” io_cq->idx = cmd_completion.cq_idx; β€” same defect.
  • ena_com.h:44-46 β€” ENA_MAX_NUM_IO_QUEUES 128, ENA_TOTAL_NUM_QUEUES (2*128)=256.
  • ena_com.c:2593,2613 β€” ena_com_indirect_table_get calls convert_from_device.
  • grep ena_com_indirect_table_get sys/dev/virtual/amazon/ena/ena.c β€” no caller β‡’ sink currently dead.

Bug is real; sibling finding DF-1221 reports the same defect.

Realistic impact ceiling

Stack buffer overflow with attacker-controlled offset (idx up to 65535 β‡’ up to ~130 KB past the stack array). The threat model is a malicious hypervisor (AWS Nitro compromise) or a malicious VF that can spoof admin completions to a guest using an ENA NIC. The idx assignment on the create_sq path is reached during normal driver bring-up; the convert_from_device sink is currently dead but would become live if ena.c is ever updated to call ena_com_indirect_table_get.

Fix

fix.diff adds three guards: 1. After io_sq->idx = cmd_completion.sq_idx; (create_io_sq): reject if idx >= ENA_TOTAL_NUM_QUEUES. 2. After io_cq->idx = cmd_completion.cq_idx; (create_io_cq): same check. 3. Defense-in-depth in convert_from_device: bounds-check idx before indexing dev_idx_to_host_tbl.

VERDICT.md verdict mechanism + citations + latency + fix
↓ download raw

DF-1294 β€” ena_com sq_idx/cq_idx unvalidated β†’ stack buffer overflow

Verdict

NOT REPRODUCED β€” real source-level bug confirmed; unreachable on this guest (no Amazon ENA NIC; module not loaded) and additionally the stack-overflow sink is currently latent (the only caller, ena_com_indirect_table_get, is not wired in ena.c).

Mechanism (verified)

  • ena_com.c:1156 β€” io_sq->idx = cmd_completion.sq_idx; β€” u16 from hypervisor, no bounds check.
  • ena_com.c:1333 β€” io_cq->idx = cmd_completion.cq_idx; β€” same.
  • ena_com.c:1200 β€” u16 dev_idx_to_host_tbl[ENA_TOTAL_NUM_QUEUES] β€” 512-B stack array.
  • ena_com.c:1205-1206 β€” dev_idx_to_host_tbl[ena_dev->io_sq_queues[i].idx] = i; β€” OOB write if idx β‰₯ 256. Offset fully attacker-controlled (idx 0..65535 β‡’ write at [0..130 KB] past the array).
  • ena_com.h:44-46 β€” ENA_TOTAL_NUM_QUEUES = 256.

Latency confirmation: - ena_com.c:2593 β€” ena_com_indirect_table_get is the sole caller of convert_from_device. - grep ena_com_indirect_table_get ena.c β€” no caller in the current driver. Only ena_com_indirect_table_set is wired (ena.c:2180), and _set routes through convert_to_device which validates (ena_com.c:1184).

So the idx-assignment primitive at lines 1156/1333 is reachable on any guest with an ENA NIC, but the stack-overflow sink in convert_from_device is dead until the driver wires indirect_table_get. Both should be fixed.

Why not triggered on this guest

  • pciconf -l (env.txt): no Amazon ENA device. Only virtio + Intel.
  • kldstat -v | grep ena: empty β€” module not loaded.
  • ena is not in X86_64_GENERIC.

Phase 4(d): genuinely not reachable on this kernel.

Fix

fix.diff adds three guards: 1. create_io_sq after line 1156: if (io_sq->idx >= ENA_TOTAL_NUM_QUEUES) return ENA_COM_INVAL; 2. create_io_cq after line 1333: if (io_cq->idx >= ENA_TOTAL_NUM_QUEUES) return ENA_COM_INVAL; 3. convert_from_device line 1205-1206: bounds-check idx before indexing (defense-in-depth).

Fix validation

Compiles cleanly in the unified 5-fix kernel build (fix_build.log). Runtime before/after is not_testable β€” no ENA NIC and the sink is dead anyway.

Realistic impact

Stack buffer overflow with attacker-controlled offset, driven by a malicious hypervisor or VF. The idx assignment is reached on every ENA queue create; the stack-overflow conversion is currently latent.

Fix verification

not_testable

compile validated

nativekernel rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ena_com sq_idx/cq_idx no bounds vs [256] -> stack OOB write (latent: convert_from_device dead). ena module, no ENA NIC.