drivers: validate device-supplied completion ids (virtio used-ring _id, NVMe cid) - #1450
drivers: validate device-supplied completion ids (virtio used-ring _id, NVMe cid)#1450gburd wants to merge 1 commit into
Conversation
c5ac056 to
6fb5280
Compare
There was a problem hiding this comment.
Pull request overview
This PR hardens OSV’s paravirtualized storage drivers against malicious or buggy device backends by validating device-supplied completion identifiers before using them as array indices, preventing potential out-of-bounds memory access in the kernel.
Changes:
- Add bounds checking for virtio used-ring
elem._idbefore indexing the_cookie[]array. - Add bounds checking for NVMe completion
cqe.cidbefore indexing the_pending_biostracking matrix, and ignore spurious/duplicate completions.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| drivers/virtio-vring.cc | Adds validation of device-supplied used-ring _id before indexing cookies. |
| drivers/nvme-queue.cc | Adds validation of device-supplied completion cid before indexing pending-bio tracking. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…VMe cid) A malicious or buggy device backend can write a completion identifier that the driver then uses as an array index. Harden the paravirtualized storage drivers so such an id cannot cause out-of-bounds access or wedge the queue. virtio-vring: - get_buf_elem() rejects a used-ring elem._id outside [0, _num) that would otherwise index _cookie[] out of bounds. Rather than return nullptr (which would stop the caller's drain loop without advancing the used head, stalling the queue forever on the same bad entry), it advances past the bogus entry and keeps draining. - get_buf_gc() now bounds-checks elem._id and every _desc[]._next chain link before indexing the descriptor table, and stops draining on a bad id instead of walking off the end. nvme-queue: - The completion path validates cqe.cid before use: it must map to a row within max_pending_levels AND to a row that was actually allocated (rows are allocated lazily, so an in-range-but-unallocated row would dereference a null _pending_bios[row]). A bogus cid is skipped (the CQ head was already advanced). Note assert() is compiled out in release builds, so an explicit runtime check is required here. Signed-off-by: Greg Burd <greg@burd.me>
6fb5280 to
68078e5
Compare
|
Thanks Copilot, these are good catches. Addressed in the current tip (68078e5, rebased onto master):
Needs a build to confirm it compiles; queued. |
|
Build-validated on x86_64: builds clean (virtio-vring.cc, nvme.cc, nvme-queue.cc). Hot-path smoke: ZFS root mounted off both virtio-blk and NVMe, tst-fallocate 9/9 on each, on 1- and 4-CPU boots. The get_buf_gc/get_buf_elem drain-loop restructure handles normal completion traffic with no queue stall or regression on either backend. (The crafted-malicious-id path needs a hostile backend harness to exercise directly; the guards are in place and normal draining is confirmed green.) |
Security fix — OOB read/write driven by a malicious or buggy device backend. Relevant when the hypervisor/device backend is not fully trusted (soft backends, some cloud/nested setups); in a unikernel these corrupt kernel memory.
1. virtio used-ring
elem._idunvalidated (drivers/virtio-vring.cc)The used-element
_idis written by the device and was indexed directly into_cookie[](allocated with_numentries):A backend returning
_id >= _num→ OOB read of a boguscookiepointer (subsequently dereferenced /deleted / passed tobiodone) plus an OOB NULL write past the array. Fix: rejectelem._id >= _num. Affects all virtio devices.2. NVMe completion
cidunvalidated (drivers/nvme-queue.cc)cid_to_row(cid) = cid / _qsize, but_pending_bioshas onlymax_pending_levels(4) rows. A controller returningcid / _qsize >= 4indexes out of bounds; theassertis a no-op in release builds. Fix: validatecid_to_row(cid) < max_pending_levelsand that the slot is non-null (skip duplicate/spurious completions) before use.Severity
High (CVSS ~7.4 each, AV:L via a malicious backend / kernel memory corruption). Both are in shipped
master.Verified: boot + virtio-blk I/O (tst-vblk) pass with the guards in place.