Thanks to visit codestin.com
Credit goes to github.com

Skip to content

block: vhdx: use logical sector size for block transfers - #8352

Closed
metsw24-max wants to merge 1 commit into
cloud-hypervisor:mainfrom
metsw24-max:vhdx-logical-sector-size
Closed

metsw24-max wants to merge 1 commit into
cloud-hypervisor:mainfrom
metsw24-max:vhdx-logical-sector-size

Conversation

@metsw24-max

@metsw24-max metsw24-max commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Wrong transfer length for VHDX images with 4K logical sectors

read and write size each block transfer as free_sectors * 512, yet the loop cursor advances by free_sectors * logical_sector_size, so a 4096-byte-sector image moves only an eighth of every block and read can return more bytes than the destination buffer holds (a 512-byte read then panics in the caller on &buf[..result]). Switched the three sites to the already-computed free_bytes, which leaves 512-byte images byte-for-byte unchanged.

The byte-to-sector conversion in the Read/Write wrappers had the same assumption baked in: it rounded the sector count up and the offset down, so a sub-sector request made io::read slice past the end of the buffer and an unaligned offset silently transferred the wrong sector. Requests whose offset or length isn't a multiple of the logical sector size are now rejected with InvalidInput, which guarantees sector_count * logical_sector_size == buf.len() by the time io::read/io::write run.

@metsw24-max
metsw24-max requested a review from a team as a code owner June 9, 2026 04:57
@phip1611

phip1611 commented Jun 9, 2026

Copy link
Copy Markdown
Member

@weltling could you please take a look?

@liuw

liuw commented Jun 9, 2026

Copy link
Copy Markdown
Member

@metsw24-max Please follow the contribution guideline to add a Signed-off-by trailer.

@metsw24-max
metsw24-max force-pushed the vhdx-logical-sector-size branch from e47a129 to 33dd29a Compare June 10, 2026 04:27
@metsw24-max

Copy link
Copy Markdown
Contributor Author

Done. Amended the commit with a Signed-off-by trailer and force-pushed.

@weltling weltling left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM.

free_bytes is how the loop advances, so it's more accurate than free_sectors * 512.

[read_count..(read_count + (sector.free_sectors * SECTOR_SIZE) as usize)],
)
.map_err(VhdxIoError::ReadSectorBlock)?;
f.read_exact(&mut buf[read_count..(read_count + sector.free_bytes as usize)])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is still wrong:

For a 4096-byte logical-sector image with a 512-byte virtio read (buf.len() == 512):

  1. mod.rs:103: sector_count = div_ceil(512, 4096) = 1
  2. Sector::new: free_sectors = 1, free_bytes = 1 * 4096 = 4096
  3. io.rs:117: buf[0..(0 + 4096)] on a 512-byte slice → panic (out of bounds)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

And I think there is a similar problem in mod.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the close read, @rbradford. My initial calculation was free_sectors * logical_sector_size, which dynamically matches what the loop commits to via read_count += free_bytes. The div_ceil in mod.rs seems preexisting and a correctness fix belongs there.

Thanks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right. Once the transfer length was honest about the sector size, the round-up in mod.rs meant io::read could still slice past the end of the buffer, so the panic just moved out of the caller and into io.rs. The round-down on the offset had the same assumption baked in and would silently read the wrong sector.

I've made the conversion in mod.rs exact instead: read and write now reject requests whose offset or length isn't a multiple of the logical sector size with InvalidInput, which guarantees sector_count * logical_sector_size == buf.len() by the time io::read/io::write run, so the slice bounds can't be exceeded. This also covers 512-byte images, where a buffer length that wasn't a multiple of 512 could previously hit the same round-up overrun.

Supporting sub-sector requests properly would mean plumbing the intra-sector offset through io::read/io::write; I'd rather do that as a follow-up than grow this fix, but happy to take it on if you'd prefer 512-byte requests against 4K-sector images to work rather than fail cleanly.

read and write sized each transfer as free_sectors * 512, but the loop
cursor advances by free_sectors * logical_sector_size, so a 4096-byte
sector image moved only an eighth of every block and read could report
more bytes than the destination buffer holds. Use the already-computed
free_bytes at the three I/O sites.

The byte-to-sector conversion in the Read/Write wrappers also assumed
sector alignment: it rounded the count up and the offset down, so a
sub-sector request (e.g. a 512-byte read on a 4096-byte sector image)
made io::read slice past the end of the buffer, and an unaligned offset
silently transferred the wrong sector. Reject requests whose offset or
length is not a multiple of the logical sector size with InvalidInput,
which guarantees sector_count * logical_sector_size == buf.len() by the
time io::read and io::write run.

Signed-off-by: metsw24-max <[email protected]>
@metsw24-max
metsw24-max force-pushed the vhdx-logical-sector-size branch from 33dd29a to 3441add Compare June 10, 2026 15:16
@rbradford

Copy link
Copy Markdown
Member

Is this change still needed in light of #8378 merging.

@rbradford
rbradford marked this pull request as draft June 16, 2026 22:05
@rbradford

Copy link
Copy Markdown
Member

I've lost track if this is still relevant - I don't think so?

@rbradford rbradford closed this Jun 19, 2026
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