Skip to content

Commit 65306ea

Browse files
feat(kani): stub out guest memory methods for add_used
GuestMemory trait brings in Bytes trait and therefore hardcodes impl for write_obj and store methods. This does not work well with Kani which now has to process these methods symbolically. The solution is to provide NOP impl for these methods. My current solution is to use kani stub macro to replace these methods by local stubs. Another solution can be to provide the proof writer a way to construct GuestMemory such that they can stub out methods as needed. Signed-off-by: Siddharth Priya <[email protected]>
1 parent 57e3370 commit 65306ea

File tree

1 file changed

+65
-12
lines changed

1 file changed

+65
-12
lines changed

virtio-queue/src/queue.rs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,28 @@ impl Queue {
272272
}
273273
}
274274

275+
fn write_elem<M: GuestMemory>(
276+
mem: &M,
277+
addr: GuestAddress,
278+
head_index: u16,
279+
len: u32,
280+
) -> Result<(), Error> {
281+
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
282+
.map_err(Error::GuestMemory)
283+
}
284+
285+
fn store_elem<M: GuestMemory>(
286+
mem: &M,
287+
used_ring: GuestAddress,
288+
next_used: u16,
289+
) -> Result<(), Error> {
290+
let addr = used_ring
291+
.checked_add(2)
292+
.ok_or(Error::AddressOverflow)?;
293+
mem.store(u16::to_le(next_used), addr, Ordering::Release)
294+
.map_err(Error::GuestMemory)
295+
}
296+
275297
#[cfg(kani)]
276298
#[allow(dead_code)]
277299
mod verification {
@@ -281,7 +303,7 @@ mod verification {
281303
use std::num::Wrapping;
282304
use vm_memory::FileOffset;
283305

284-
use vm_memory::{GuestMemoryRegion, MemoryRegionAddress};
306+
use vm_memory::{GuestRegionMmap, GuestMemoryRegion, MemoryRegionAddress, AtomicAccess, GuestMemory, GuestMemoryError};
285307

286308
use super::*;
287309

@@ -517,8 +539,47 @@ mod verification {
517539
// So we do not care
518540
}
519541
}
520-
}
521542

543+
fn stub_write_elem<M: GuestMemory>(
544+
_mem: &M,
545+
_addr: GuestAddress,
546+
_head_index: u16,
547+
_len: u32,) -> Result<(), Error> {
548+
Ok(())
549+
}
550+
551+
fn stub_store_elem<M: GuestMemory>(
552+
_mem: &M,
553+
_used_ring: GuestAddress,
554+
_next_used: u16,) -> Result<(), Error> {
555+
Ok(())
556+
}
557+
558+
#[kani::proof]
559+
#[kani::unwind(0)]
560+
#[kani::stub(write_elem, stub_write_elem)]
561+
#[kani::stub(store_elem, stub_store_elem)]
562+
fn verify_add_used() {
563+
let ProofContext(mut queue, mem) = kani::any();
564+
let used_idx = queue.next_used;
565+
566+
let used_desc_table_index = kani::any();
567+
if queue.add_used(&mem, used_desc_table_index, kani::any()).is_ok() {
568+
assert_eq!(queue.next_used, used_idx + Wrapping(1));
569+
} else {
570+
assert_eq!(queue.next_used, used_idx);
571+
572+
// Ideally, here we would want to actually read the relevant values from memory and
573+
// assert they are unchanged. However, kani will run out of memory if we try to do so,
574+
// so we instead verify the following "proxy property": If an error happened, then
575+
// it happened at the very beginning of add_used, meaning no memory accesses were
576+
// done. This is relying on implementation details of add_used, namely that
577+
// the check for out-of-bounds descriptor index happens at the very beginning of the
578+
// function.
579+
assert!(used_desc_table_index >= queue.size());
580+
}
581+
}
582+
}
522583
impl<'a> QueueGuard<'a> for Queue {
523584
type G = &'a mut Self;
524585
}
@@ -716,20 +777,12 @@ impl QueueT for Queue {
716777
.used_ring
717778
.checked_add(offset)
718779
.ok_or(Error::AddressOverflow)?;
719-
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
720-
.map_err(Error::GuestMemory)?;
780+
write_elem(mem, addr, head_index, len)?;
721781

722782
self.next_used += Wrapping(1);
723783
self.num_added += Wrapping(1);
724784

725-
mem.store(
726-
u16::to_le(self.next_used.0),
727-
self.used_ring
728-
.checked_add(2)
729-
.ok_or(Error::AddressOverflow)?,
730-
Ordering::Release,
731-
)
732-
.map_err(Error::GuestMemory)
785+
store_elem(mem, self.used_ring, self.next_used.0)
733786
}
734787

735788
fn enable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {

0 commit comments

Comments
 (0)