Skip to content

fix(virtio): set WRITE flag on packed used descriptors from len - #539

Merged
Lencerf merged 1 commit into
google:mainfrom
Lencerf:fix/packed-used-flags
Nov 11, 2026
Merged

Lencerf merged 1 commit into
google:mainfrom
Lencerf:fix/packed-used-flags

Conversation

@Lencerf

@Lencerf Lencerf commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

PackedQueue::set_used() previously loaded the overwritten available descriptor's flags and only toggled AVAIL and USED, preserving stale WRITE, NEXT, and INDIRECT bits in the used descriptor.

Per VirtIO 1.4 Sec 2.8.3 (Write Flag) and Sec 2.8.4 (Element Address and Length), VIRTQ_DESC_F_WRITE in a used descriptor specifies whether any data was written by the device into any part of the buffer, and Element Length is reserved and ignored by drivers when VIRTQ_DESC_F_WRITE is clear. As a result:

  • Any request starting with a readable header (virtio-blk IN/GET_ID, virtio-fs FUSE replies, virtio-net controlq), an indirect descriptor, or an out-of-order completion overwriting a readable slot produced a used descriptor with WRITE = 0 even when len > 0, so a driver that ignores Element Length as the spec allows would treat the written length as 0.
  • Completing a writable slot with len == 0 left WRITE = 1, and NEXT (reserved in used descriptors, Sec 2.8.6) or INDIRECT leaked into used descriptor flags.

Linux guests are not affected: virtqueue_get_buf_ctx_packed() reads the length with vring_read_packed_desc_len() regardless of VRING_DESC_F_WRITE, and is_used_desc_packed() only checks AVAIL and USED.

Construct the used descriptor flags from scratch instead, setting USED | AVAIL from index.wrap_counter() and WRITE iff len > 0.

Testing

  • Updated VirtQueueGuest::get_used() in packed_test.rs to assert reserved flags (NEXT | INDIRECT) are clear and ignore desc.len unless DescFlag::WRITE is set.
  • Added mixed readable+writable chain coverage to enabled_queue and a parameterized set_used_flags test, which starts from available descriptors whose AVAIL equals and USED differs from the wrap counter. Both fail with the previous set_used().
  • cargo +nightly fmt --check, cargo clippy --all-targets -D warnings on x86_64-unknown-linux-gnu/musl, aarch64-unknown-linux-gnu and aarch64-apple-darwin, and cargo test --workspace pass locally.

@coveralls

coveralls commented Nov 11, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 42942303169

Coverage increased (+0.03%) to 43.663%

Details

  • Coverage increased (+0.03%) from the base build.
  • Patch coverage: 8 of 8 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 16444
Covered Lines: 6968
Line Coverage: 42.37%
Relevant Branches: 1750
Covered Branches: 976
Branch Coverage: 55.77%
Branches in Coverage %: Yes
Coverage Strength: 905.77 hits per line

💛 - Coveralls

@Lencerf
Lencerf force-pushed the fix/packed-used-flags branch 2 times, most recently from b53284e to 54c776b Compare November 11, 2026 01:47
PackedQueue::set_used() previously loaded the overwritten available
descriptor's flags and only toggled AVAIL and USED, preserving stale
WRITE, NEXT, and INDIRECT bits in the used descriptor.

Per VirtIO 1.4 Sec 2.8.3 and 2.8.4, VIRTQ_DESC_F_WRITE in a used
descriptor specifies whether the device wrote any data into any part
of the buffer, and Element Length is reserved and ignored by drivers
when VIRTQ_DESC_F_WRITE is clear. Consequently, any request starting
with a readable header or an indirect descriptor (or completed out of
order into a readable slot) produced a used descriptor with WRITE
clear even when len > 0, so a driver that ignores Element Length as
the spec allows would treat the written length as 0. Conversely,
completing a writable slot with len == 0 left WRITE set, and NEXT,
which is reserved in used descriptors (Sec 2.8.6), or INDIRECT leaked
into used flags.

Linux is not affected, since it reads the length of a used descriptor
regardless of VIRTQ_DESC_F_WRITE.

Construct the used descriptor flags from scratch instead, setting
USED | AVAIL from the ring wrap counter and WRITE iff len > 0.

Fixes: f97eb72 ("feat(virtio): support packed queue")
Signed-off-by: Changyuan Lyu <changyuanl@google.com>
@Lencerf
Lencerf force-pushed the fix/packed-used-flags branch from 54c776b to 114f808 Compare November 11, 2026 01:54
@Lencerf
Lencerf added this pull request to the merge queue Nov 11, 2026
Merged via the queue into google:main with commit 8d777ce Nov 11, 2026
20 checks passed
@Lencerf
Lencerf deleted the fix/packed-used-flags branch November 11, 2026 02:06
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