Skip to content

Commit 959d811

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 cecce8d commit 959d811

File tree

1 file changed

+65
-13
lines changed

1 file changed

+65
-13
lines changed

virtio-queue/src/queue.rs

Lines changed: 65 additions & 13 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

@@ -295,7 +317,6 @@ mod verification {
295317

296318
impl GuestMemory for ProofGuestMemory {
297319
type R = vm_memory::GuestRegionMmap;
298-
//type I = Self;
299320

300321
fn num_regions(&self) -> usize {
301322
1
@@ -515,8 +536,47 @@ mod verification {
515536
// So we do not care
516537
}
517538
}
518-
}
519539

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+
}
520580
impl<'a> QueueGuard<'a> for Queue {
521581
type G = &'a mut Self;
522582
}
@@ -714,20 +774,12 @@ impl QueueT for Queue {
714774
.used_ring
715775
.checked_add(offset)
716776
.ok_or(Error::AddressOverflow)?;
717-
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
718-
.map_err(Error::GuestMemory)?;
777+
write_elem(mem, addr, head_index, len)?;
719778

720779
self.next_used += Wrapping(1);
721780
self.num_added += Wrapping(1);
722781

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

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

0 commit comments

Comments
 (0)