Skip to content

virtio: validate indirect descriptor table size#3137

Open
benhillis wants to merge 1 commit intomicrosoft:mainfrom
benhillis:fix/virtio-indirect-desc-size-v2
Open

virtio: validate indirect descriptor table size#3137
benhillis wants to merge 1 commit intomicrosoft:mainfrom
benhillis:fix/virtio-indirect-desc-size-v2

Conversation

@benhillis
Copy link
Copy Markdown
Member

The indirect descriptor table length from the guest was not validated to be a non-zero multiple of the descriptor size (16 bytes). A zero length would produce an empty indirect table, and a misaligned length would cause the last partial descriptor to be silently truncated via integer division.

Add an explicit check that rejects zero or misaligned indirect table byte lengths with a new InvalidIndirectSize error variant.

The indirect descriptor table length from the guest was not validated
to be a non-zero multiple of the descriptor size (16 bytes). A zero
length would produce an empty indirect table, and a misaligned length
would cause the last partial descriptor to be silently truncated via
integer division.

Add an explicit check that rejects zero or misaligned indirect table
byte lengths with a new InvalidIndirectSize error variant.
@benhillis benhillis requested a review from a team as a code owner March 26, 2026 00:41
Copilot AI review requested due to automatic review settings March 26, 2026 00:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the virtio queue descriptor-chain parsing by validating the guest-provided indirect descriptor table byte length, preventing zero-length indirect tables and misaligned lengths that previously resulted in silent truncation.

Changes:

  • Add a new QueueError::InvalidIndirectSize(u32) error variant.
  • Validate indirect descriptor table byte length is non-zero and a multiple of the descriptor size before mapping/parsing it.

Comment on lines +482 to +487
let indirect_byte_len = descriptor.length;
if indirect_byte_len == 0
|| !(indirect_byte_len as usize).is_multiple_of(size_of::<SplitDescriptor>())
{
return Err(QueueError::InvalidIndirectSize(indirect_byte_len));
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

This new validation ensures the indirect table byte length is non-zero and aligned, but the computed entry count is still later derived via division and cast down to u16 without a bounds check. For large aligned descriptor.length values, that cast can truncate and make an oversized indirect table appear small (notably for packed indirect traversal which uses active_indirect_len). Add an explicit check that (descriptor.length / 16) fits in u16 (and reject otherwise) before proceeding.

Copilot uses AI. Check for mistakes.
Comment on lines +482 to +487
let indirect_byte_len = descriptor.length;
if indirect_byte_len == 0
|| !(indirect_byte_len as usize).is_multiple_of(size_of::<SplitDescriptor>())
{
return Err(QueueError::InvalidIndirectSize(indirect_byte_len));
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The new InvalidIndirectSize path should be covered by a unit/integration test (e.g., an INDIRECT descriptor with length == 0 and another with a non-multiple-of-16 length should cause queue.try_next() to return an error). There are already indirect-descriptor tests in vm/devices/virtio/virtio/src/tests.rs, but none appear to exercise this new validation.

Copilot uses AI. Check for mistakes.
TooLong,
#[error("Invalid queue size {0}. Must be a power of 2.")]
InvalidQueueSize(u16),
#[error("indirect descriptor table has invalid byte length {0}")]
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The InvalidIndirectSize error message doesn’t state the actual constraint (must be a non-zero multiple of the descriptor size, 16 bytes). Consider updating the message to include that requirement so failures are actionable when diagnosing guest issues.

Suggested change
#[error("indirect descriptor table has invalid byte length {0}")]
#[error(
"indirect descriptor table has invalid byte length {0}; must be a non-zero multiple of the descriptor size (16 bytes)"
)]

Copilot uses AI. Check for mistakes.
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.

2 participants