Skip to content

Commit 2bda53f

Browse files
virtio-queue: verify add_used method of the queue
Signed-off-by: Siddharth Priya <[email protected]>
1 parent 827bf88 commit 2bda53f

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

virtio-queue/src/queue/verification.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,3 +509,59 @@ fn verify_spec_2_7_7_2() {
509509
// So we do not care
510510
}
511511
}
512+
513+
/// Reads the idx field from the used ring in guest memory, as stored by Queue::add_used.
514+
/// Returns the value as u16.
515+
fn get_used_idx(
516+
queue: &Queue,
517+
mem: &SingleRegionGuestMemory,
518+
) -> Result<u16, vm_memory::GuestMemoryError> {
519+
let addr =
520+
queue
521+
.used_ring
522+
.checked_add(2)
523+
.ok_or(vm_memory::GuestMemoryError::InvalidGuestAddress(
524+
queue.used_ring,
525+
))?;
526+
let val = mem.load(addr, Ordering::Acquire)?;
527+
Ok(u16::from_le(val))
528+
}
529+
530+
/// # Specification (VirtIO 1.3, Section 2.7.14: "Receiving Used Buffers From The Device")
531+
///
532+
/// Kani proof harness for verifying the behavior of the `add_used` method of
533+
/// the `Queue`. When the device has finished processing a buffer, it must add
534+
/// an element to the used ring, indicating which descriptor chain was used and
535+
/// how many bytes were written. The device must increment the used index after
536+
/// writing the element. Additionally, for this implementation, we verify that
537+
/// if the descriptor index is out of bounds, the operation must fail and the
538+
/// used index must not be incremented. Note that this proof does not verify
539+
/// Section 2.7.8.2: "Device Requirements: The Virtqueue Used Ring"
540+
#[kani::proof]
541+
#[kani::unwind(0)]
542+
fn verify_add_used() {
543+
let ProofContext { mut queue, memory } = kani::any();
544+
let used_idx = queue.next_used;
545+
let used_desc_table_index = kani::any();
546+
let old_val = get_used_idx(&queue, &memory).unwrap();
547+
let old_num_added = queue.num_added;
548+
if queue
549+
.add_used(&memory, used_desc_table_index, kani::any())
550+
.is_ok()
551+
{
552+
assert_eq!(queue.next_used, used_idx + Wrapping(1));
553+
assert_eq!(queue.next_used.0, get_used_idx(&queue, &memory).unwrap());
554+
assert_eq!(old_num_added + Wrapping(1), queue.num_added);
555+
kani::cover!();
556+
} else {
557+
// On failure, we expect the next_used to remain unchanged
558+
// and the used_desc_table_index to be out of bounds.
559+
assert_eq!(queue.next_used, used_idx);
560+
assert!(used_desc_table_index >= queue.size());
561+
// The old value should still be the same as before the add_used call.
562+
assert_eq!(old_val, get_used_idx(&queue, &memory).unwrap());
563+
// No change in num_added field.
564+
assert_eq!(old_num_added, queue.num_added);
565+
kani::cover!();
566+
}
567+
}

0 commit comments

Comments
 (0)