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

Skip to content

block: vhdx: fix incomplete bounds check in sync I/O worker - #8529

Merged
rbradford merged 4 commits into
cloud-hypervisor:mainfrom
Alvov1:fix/vhdx-sync-bounds-check
Jul 7, 2026
Merged

rbradford merged 4 commits into
cloud-hypervisor:mainfrom
Alvov1:fix/vhdx-sync-bounds-check

Conversation

@Alvov1

@Alvov1 Alvov1 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

VhdxSync::submit_data_operation() passed every read/write straight to the underlying Vhdx without checking the request against the virtual disk's logical size. A request that started inside the image but extended past its end (or an offset past the end entirely) was passed through unchecked, silently reading/writing out of the intended bounds

Add AsyncIoOperation::validate_bounds() as a shared helper on the operation type itself, and call it from VhdxSync::submit_data_operation() before dispatching the operation, the same way the VHD sync worker already does (#8394). The check rejects any request whose offset + length exceeds the logical size

The VHD workers are also switched over to the shared helper, replacing their local validate_operation_bounds() copy in vhd/worker/common.rs

@Alvov1
Alvov1 requested a review from a team as a code owner July 4, 2026 12:29

@rbradford rbradford 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.

This should be structured as 4 commits:

  1. Add these bounds check methods to AsyncIoOperation
  2. Test refactoring setup for vhdx
  3. Switch vhd to use them
  4. Switch vhdx to use them

Comment thread block/src/formats/vhdx/worker/common.rs Outdated
Comment on lines +1 to +38
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0

use std::io;

use crate::async_io::{AsyncIoError, AsyncIoOperation, AsyncIoResult};

pub(super) fn validate_operation_bounds(op: &AsyncIoOperation, size: u64) -> AsyncIoResult<()> {
let offset = u64::try_from(op.offset()).map_err(|_| bounds_error(op, size))?;
let len = u64::try_from(op.total_len()).map_err(|_| bounds_error(op, size))?;
let end = offset
.checked_add(len)
.ok_or_else(|| bounds_error(op, size))?;

if end > size {
return Err(bounds_error(op, size));
}

Ok(())
}

fn bounds_error(op: &AsyncIoOperation, size: u64) -> AsyncIoError {
let error = io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Invalid request offset {} and length {}, can't exceed file size {}",
op.offset(),
op.total_len(),
size
),
);
if op.is_read() {
AsyncIoError::ReadVectored(error)
} else {
AsyncIoError::WriteVectored(error)
}
}

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.

This is a duplicate of vhd - please do something like:

  impl AsyncIoOperation {
      /// Rejects an operation whose byte range falls outside a disk of `size` bytes.
      pub(crate) fn validate_bounds(&self, size: u64) -> AsyncIoResult<()> {
          let out_of_bounds = || {
              let error = io::Error::new(
                  io::ErrorKind::InvalidData,
                  format!(
                      "Invalid request offset {} and length {}, can't exceed file size {size}",
                      self.offset(),
                      self.total_len(),
                  ),
              );
              if self.is_read() {
                  AsyncIoError::ReadVectored(error)
              } else {
                  AsyncIoError::WriteVectored(error)
              }
          };

          let offset = u64::try_from(self.offset()).map_err(|_| out_of_bounds())?;
          let len = u64::try_from(self.total_len()).map_err(|_| out_of_bounds())?;
          if offset.checked_add(len).ok_or_else(out_of_bounds)? > size {
              return Err(out_of_bounds());
          }
          Ok(())
      }
  }

and port vhd

@Alvov1
Alvov1 force-pushed the fix/vhdx-sync-bounds-check branch from 9ef1e25 to 7dc19d4 Compare July 7, 2026 08:01
Implement global helper to validate vhd and vhdx sync workers'
I/O requests whose offset + length exceeds the virtual disk's
logical size.

Signed-off-by: Alexander Lvov <[email protected]>

@rbradford rbradford 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.

Thanks! Really good - could we just make that small rename?

Comment thread block/src/formats/vhdx/test_util.rs Outdated

/// Generate a small dynamic VHDX with `qemu-img`. Returns `None` (and the
/// test is skipped) when `qemu-img` is unavailable, e.g. in minimal CI.
pub(crate) fn dynamic_vhdx(size_mib: u64) -> Option<TempFile> {

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 name is confusing (I know this PR just moved it around). It sounds like it could be an method to identify if this a "dynamic vhdx" file. I think create_dynamic_vhdx would be better.

@Alvov1

Alvov1 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Sure! Please wait, I missed one small detail, there will be one more fpush

Alvov1 added 3 commits July 7, 2026 11:44
Extract the dynamic VHDX qemu-img helper into a shared vhdx::
test_util module to reuse inside the upcoming VhdxSync bounds-check.

Signed-off-by: Alexander Lvov <[email protected]>
Reuse global validate_bounds() operation helper instead of
having a local implementation in vhd/worker/common.rs

Signed-off-by: Alexander Lvov <[email protected]>
VhdxSync::submit_data_operation() passed every read/write straight to
the underlying Vhdx without checking the request against the virtual
disk's logical size. A request that started inside the image but
extended past its end (or an offset past the end entirely) was passed
through unchecked, silently reading/writing out of the intended
bounds.

Call AsyncIoOperation::validate_bounds() from submit_data_operation()
before dispatching the operation, the same way the VHD sync worker
does. The check rejects any request whose offset + length exceeds the
logical size.

Signed-off-by: Alexander Lvov <[email protected]>
@Alvov1
Alvov1 force-pushed the fix/vhdx-sync-bounds-check branch from 7dc19d4 to fa38a13 Compare July 7, 2026 09:07
@Alvov1

Alvov1 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Now should be good. Implemented requested changes and updated the PR description

@rbradford
rbradford enabled auto-merge July 7, 2026 09:20
@rbradford
rbradford added this pull request to the merge queue Jul 7, 2026
Merged via the queue into cloud-hypervisor:main with commit fa7cad4 Jul 7, 2026
37 of 41 checks passed
@Alvov1
Alvov1 deleted the fix/vhdx-sync-bounds-check branch July 7, 2026 10:56
@rbradford rbradford added the bug-fix Bug fix to include in release notes label Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix Bug fix to include in release notes

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants