Skip to content

Commit 6451a84

Browse files
virtio-queue: verify add_used method of the queue
Signed-off-by: Siddharth Priya <[email protected]>
1 parent 841de4b commit 6451a84

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

virtio-queue/src/queue/verification.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,61 @@ fn verify_spec_2_7_7_2() {
507507
// So we do not care
508508
}
509509
}
510+
511+
/// Reads the idx field from the used ring in guest memory, as stored by Queue::add_used.
512+
/// Returns the value as u16.
513+
fn get_used_idx(
514+
queue: &Queue,
515+
mem: &SingleRegionGuestMemory,
516+
) -> Result<u16, vm_memory::GuestMemoryError> {
517+
let addr =
518+
queue
519+
.used_ring
520+
.checked_add(2)
521+
.ok_or(vm_memory::GuestMemoryError::InvalidGuestAddress(
522+
queue.used_ring,
523+
))?;
524+
let val = mem.load(addr, Ordering::Acquire)?;
525+
Ok(u16::from_le(val))
526+
}
527+
528+
/// Kani proof harness for verifying the behavior of the `add_used` method of
529+
/// the `Queue`.
530+
///
531+
/// # Specification (VirtIO 1.3, Section 2.7.14: "Receiving Used Buffers From The Device")
532+
///
533+
/// When the device has finished processing a buffer, it must add an element to
534+
/// the used ring, indicating which descriptor chain was used and how many bytes
535+
/// were written. The device must increment the used index after writing the
536+
/// element. Additionally, for this implementation, we verify that if the
537+
/// descriptor index is out of bounds, the operation must fail and the used
538+
/// index must not be incremented. Note that this proof does not verify Section
539+
/// 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)