put FD validity behind late debug_asserts checking

uses the same machinery as assert_unsafe_precondition
This commit is contained in:
The 8472 2024-04-21 17:19:15 +02:00
parent 25babe9a79
commit 1ba00d9cb2
2 changed files with 32 additions and 29 deletions

View File

@ -177,17 +177,9 @@ fn drop(&mut self) {
// opened after we closed ours.
#[cfg(not(target_os = "hermit"))]
{
use crate::sys::os::errno;
// ideally this would use assert_unsafe_precondition!, but that's only in core
if cfg!(debug_assertions) {
// close() can bubble up error codes from FUSE which can send semantically
// inappropriate error codes including EBADF.
// So we check file flags instead which live on the file descriptor and not the underlying file.
// The downside is that it costs an extra syscall, so we only do it for debug.
if libc::fcntl(self.fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
rtabort!("IO Safety violation: owned file descriptor already closed");
}
}
#[cfg(unix)]
crate::sys::fs::debug_assert_fd_is_open(self.fd);
let _ = libc::close(self.fd);
}
#[cfg(target_os = "hermit")]

View File

@ -868,28 +868,39 @@ fn next(&mut self) -> Option<io::Result<DirEntry>> {
}
}
/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled
///
/// Many IO syscalls can't be fully trusted about EBADF error codes because those
/// might get bubbled up from a remote FUSE server rather than the file descriptor
/// in the current process being invalid.
///
/// So we check file flags instead which live on the file descriptor and not the underlying file.
/// The downside is that it costs an extra syscall, so we only do it for debug.
#[inline]
pub(crate) fn debug_assert_fd_is_open(fd: RawFd) {
use crate::sys::os::errno;
// this is similar to assert_unsafe_precondition!() but it doesn't require const
if core::ub_checks::check_library_ub() {
if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF {
rtabort!("IO Safety violation: owned file descriptor already closed");
}
}
}
impl Drop for Dir {
fn drop(&mut self) {
// ideally this would use assert_unsafe_precondition!, but that's only in core
#[cfg(all(
debug_assertions,
not(any(
// dirfd isn't supported everywhere
#[cfg(not(any(
miri,
target_os = "redox",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
))
))]
)))]
{
use crate::sys::os::errno;
// close() can bubble up error codes from FUSE which can send semantically
// inappropriate error codes including EBADF.
// So we check file flags instead which live on the file descriptor and not the underlying file.
// The downside is that it costs an extra syscall, so we only do it for debug.
let fd = unsafe { libc::dirfd(self.0) };
if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF {
rtabort!("IO Safety violation: DIR*'s owned file descriptor already closed");
}
debug_assert_fd_is_open(fd);
}
let r = unsafe { libc::closedir(self.0) };
assert!(