Skip to content

Conversation

priyasiddharth
Copy link

@priyasiddharth priyasiddharth commented Jun 18, 2025

Summary of the PR

virtio-queue: add verification for add_used operation

Kani proofs like verify_add_used do not finish in practical time if we use the production memory region. So we implement a stub region with a simple vector backing it. This will help subsequent proofs work and also enable stateful proofs.

Note that unsafe code is added only for StubRegion that will run in Kani.
Kani verifies all unsafe accesses do not cause undefined behaviour (in the context of unit proof execution).

Requirements

Before submitting your PR, please make sure you addressed the following
requirements:

  • All commits in this PR have Signed-Off-By trailers (with
    git commit -s), and the commit message has max 60 characters for the
    summary and max 75 characters for each description line.
  • All added/changed functionality has a corresponding unit/integration
    test.
  • All added/changed public-facing functionality has entries in the "Upcoming
    Release" section of CHANGELOG.md (if no such section exists, please create one).
  • Any newly added unsafe code is properly documented.

@priyasiddharth

This comment was marked as outdated.

@priyasiddharth priyasiddharth force-pushed the verify_add_used branch 4 times, most recently from 96d3228 to 89e9614 Compare July 29, 2025 15:15
@priyasiddharth priyasiddharth changed the title verify add_used operation using guestmemory stubs unit proof for add_used operation Aug 5, 2025
@priyasiddharth priyasiddharth marked this pull request as ready for review August 5, 2025 15:24
@MatiasVara
Copy link
Contributor

LGTM!

Copy link
Member

@stefano-garzarella stefano-garzarella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please for the commit description, follow the style of other commits, using the crate as prefix, something like this:

virtio-queue: add unit proof for add_used method (sec 2.8.22)

@priyasiddharth priyasiddharth reopened this Aug 7, 2025
@priyasiddharth priyasiddharth changed the title unit proof for add_used operation virtio-queue: add stub memory region for kani proofs Aug 7, 2025
@priyasiddharth priyasiddharth changed the title virtio-queue: add stub memory region for kani proofs virtio-queue: add verify_add_used and stub memory region Aug 8, 2025
@priyasiddharth priyasiddharth force-pushed the verify_add_used branch 3 times, most recently from 7f83b55 to 6451a84 Compare August 19, 2025 18:37
Kani proofs do not finish in practical time if we use the production
memory region. So we implement a stub region with a simple vector backing it.
This will help subsequent proofs work and also enable stateful
proofs.

Signed-off-by: Siddharth Priya <[email protected]>
Copy link
Member

@stefano-garzarella stefano-garzarella left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comments from my side. LGTM but I'd like an approval from @MatiasVara and @roypat

@stefano-garzarella
Copy link
Member

minor comments from my side. LGTM but I'd like an approval from @MatiasVara and @roypat

and @epilys

@@ -458,19 +458,22 @@ impl kani::Arbitrary for ProofContext {
}
}

/// # Specification (VirtIO 1.3, Section 2.7.7.2: "Device-to-driver notification suppression")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In virtio 1.3 (https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.html#x1-510007) the section 2.7.7.2 is "2.7.7.2 Device Requirements: Used Buffer Notification Suppression", so I guess here we should update the title. (Is the 2.7.7.2 correct?)

Copy link
Member

@epilys epilys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! LGTM, very clean code. I left some very minor suggestions to make the code more readable/maintainable in my honest opinion, but they are not blockers.

Comment on lines +13 to +17
pub struct StubRegion {
buffer: *mut u8,
region_len: usize,
region_start: GuestAddress,
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: does this need to be pub?

// between guest addresses and memory region addresses, and to check offsets
// within the region.
impl StubRegion {
pub fn new(buf_ptr: *mut u8, buf_len: usize, start_offset: u64) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Comment on lines +513 to +514
/// Reads the idx field from the used ring in guest memory, as stored by Queue::add_used.
/// Returns the value as u16.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

Suggested change
/// Reads the idx field from the used ring in guest memory, as stored by Queue::add_used.
/// Returns the value as u16.
/// Returns the `idx` field from the used ring in guest memory, as stored by [`Queue::add_used`].

// On failure, we expect the next_used to remain unchanged
// and the used_desc_table_index to be out of bounds.
assert_eq!(queue.next_used, used_idx);
assert!(used_desc_table_index >= queue.size());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion (not a demand)

Suggested change
assert!(used_desc_table_index >= queue.size());
assert!(used_desc_table_index >= queue.size(), "{used_desc_table_index:?}, {:?}", queue.size());

Comment on lines +350 to +354
let size = GUEST_MEMORY_SIZE;
let start = GUEST_MEMORY_BASE;
Self {
the_region: StubRegion::new(memory, size, start),
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let size = GUEST_MEMORY_SIZE;
let start = GUEST_MEMORY_BASE;
Self {
the_region: StubRegion::new(memory, size, start),
}
Self {
the_region: StubRegion::new(memory, GUEST_MEMORY_SIZE, GUEST_MEMORY_BASE),
}

Comment on lines +274 to +282
assert!(region
.write(&bytes, MemoryRegionAddress(write_offset as u64))
.is_ok());

// Read back into a new buffer
let mut readback = kani::vec::exact_vec::<u8, 16>();
assert!(region
.read(&mut readback, MemoryRegionAddress(write_offset as u64))
.is_ok());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we unwrap errors instead? Otherwise the error message is not shown.

dst: &mut F,
count: usize,
) -> Result<(), Self::E> {
self.write_to(addr, dst, count).map(|_| ())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.write_to(addr, dst, count).map(|_| ())
self.write_to(addr, dst, count)?;
Ok(())

unsafe {
std::ptr::copy_nonoverlapping(
self.buffer.add(offset),
(&mut result as *mut T) as *mut u8,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use ByteValued method:

Suggested change
(&mut result as *mut T) as *mut u8,
result.as_mut_slice().as_mut_ptr(),

}

fn read_slice(&self, buf: &mut [u8], addr: MemoryRegionAddress) -> Result<(), Self::E> {
self.read(buf, addr).map(|_| ())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.read(buf, addr).map(|_| ())
self.read(buf, addr)?;
Ok(())

}

fn write_slice(&self, buf: &[u8], addr: MemoryRegionAddress) -> Result<(), Self::E> {
self.write(buf, addr).map(|_| ())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.write(buf, addr).map(|_| ())
self.write(buf, addr)?;
Ok(())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants