Skip to content

Commit 7113f45

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 <s2priya@uwaterloo.ca>
1 parent 8c2669f commit 7113f45

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
@@ -269,6 +269,28 @@ impl Queue {
269269
}
270270
}
271271

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

281-
use vm_memory::{GuestMemoryRegion, MemoryRegionAddress};
303+
use vm_memory::{GuestRegionMmap, GuestMemoryRegion, MemoryRegionAddress, AtomicAccess, GuestMemory, GuestMemoryError};
282304

283305
use super::*;
284306

@@ -514,8 +536,47 @@ mod verification {
514536
// So we do not care
515537
}
516538
}
517-
}
518539

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

719779
self.next_used += Wrapping(1);
720780
self.num_added += Wrapping(1);
721781

722-
mem.store(
723-
u16::to_le(self.next_used.0),
724-
self.used_ring
725-
.checked_add(2)
726-
.ok_or(Error::AddressOverflow)?,
727-
Ordering::Release,
728-
)
729-
.map_err(Error::GuestMemory)
782+
store_elem(mem, self.used_ring, self.next_used.0)
730783
}
731784

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

0 commit comments

Comments
 (0)