Skip to content

More I/O safety work#7888

Closed
DemiMarie wants to merge 2 commits intocloud-hypervisor:mainfrom
DemiMarie:no-use-after-close-2
Closed

More I/O safety work#7888
DemiMarie wants to merge 2 commits intocloud-hypervisor:mainfrom
DemiMarie:no-use-after-close-2

Conversation

@DemiMarie
Copy link
Copy Markdown
Contributor

The block layer did not use I/O safety as much as it could have.

An AsyncIo might outlive the device it is created from, so it is
necessary for it to have a copy of the underlying file descriptor.  The
previous code used RawFd instead of BorrowedFd and so the problem was
not detected.

Also get rid of the BorrowedDiskFd struct.  Its only purpose was to work
around mutex.lock().unwrap().as_fd() returning a temporary BorrowedFd
that is immediately invalid.  The error from rustc is in fact correct
here, as code with a mutable reference to what the mutex is protecting
can replace it and thus drop the underlying file descriptor.  This does
not happen in VhdxSync, but the compiler cannot know this.  Therefore,
use mem::transmute to cast the lifetime to a longer one.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
Generally AsRawFd is an antipattern outside of FFI.  Use AsFd instead
where possible.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
@DemiMarie DemiMarie requested a review from a team as a code owner March 24, 2026 12:05
Copy link
Copy Markdown
Member

@phip1611 phip1611 left a comment

Choose a reason for hiding this comment

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

I originally authored struct BorrowedDiskFd as a pragmatic solution. Your proposed design is cleaner - thank you! Left some remarks

Comment thread block/src/vhdx_sync.rs

impl AsFd for VhdxDiskSync {
fn as_fd(&self) -> BorrowedFd<'_> {
let wrong_lifetime_fd = self.vhdx_file.lock().unwrap();
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.

Generally, I think this PR is going into a great direction and I see why you are doing this here this way. This impl however feels a little to much like a hack 🤔

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.

What do you suggest instead?

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.

Store a dup'd OwnedFd in VhdxDiskSync at construction time:

lock_fd: OwnedFd,  // file.as_fd().try_clone_to_owned()?

Then as_fd() / fd() just returns self.lock_fd.as_fd(). No mutex, no transmute, no unsafe. One extra fd per instance.

Thanks

Comment thread block/src/vhdx_sync.rs

impl AsFd for VhdxDiskSync {
fn as_fd(&self) -> BorrowedFd<'_> {
let wrong_lifetime_fd = self.vhdx_file.lock().unwrap();
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.

Generally, I think this PR is going into a great direction and I see why you are doing this here this way. This impl however feels a little to much like a hack 🤔

Comment thread block/src/vhdx_sync.rs
}
}

impl AsFd for VhdxDiskSync {
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.

from commit message:

An AsyncIo might outlive the device it is created from, so it is
necessary for it to have a copy of the underlying file descriptor.

Please explain in the commit message why/when this is the case.

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.

I don’t know if it actually is the case! I suspect it is not, but this is a global invariant. This means that the block APIs are unsound.

Copy link
Copy Markdown
Member

@weltling weltling left a comment

Choose a reason for hiding this comment

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

Thanks for working on this. The direction is sound and the core fix for OwnedFd in AsyncIo is valuable. This also overlaps or goes in the same direction with the current refactoring efforts, while I was more on keeping things more general and reusable with various existing and upcoming disk formats.

IMO it would be better to keep AsFd off DiskFile. Semantically, a QCOW or VHDX image isn't a file descriptor and DiskFd names the purpose explicitly. The currently existing approach is close to the right design, it can be switched from BorrowedDiskFd+RawFd to the proper BorrowedFd. Fixing DiskFd to use BorrowedFd is cleaner than deleting it.

Thanks

@phip1611 phip1611 marked this pull request as draft March 30, 2026 17:13
@phip1611
Copy link
Copy Markdown
Member

phip1611 commented Mar 30, 2026

thanks for working on this and I think it is valuable! Given the numerous PRs we have right now, I drafted this PR. This PR is at least blocked by #7892 #7916 #7882 if I'm not mistaken.

Let's focus on getting them merged first, then we can discuss about this again :)

@DemiMarie DemiMarie closed this Apr 16, 2026
@DemiMarie DemiMarie deleted the no-use-after-close-2 branch April 16, 2026 16:54
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.

3 participants