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)
PoC verification
Evidence pack
findings/poc/DF-1294 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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_getcallsconvert_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.
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.enais 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_testablecompile 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.
No comments yet.