Skip to content

Commit 3e0bb0d

Browse files
committed
Abort a process when FD ownership is violated
When an EBADF happens then something else already touched an FD in ways it is not allowed to. At that point things can already be arbitrarily bad, e.g. clobbered mmaps. Recovery is not possible. All we can do is hasten the fire.
1 parent c628501 commit 3e0bb0d

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

std/src/os/fd/owned.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,20 @@ impl Drop for OwnedFd {
176176
// something like EINTR), we might close another valid file descriptor
177177
// opened after we closed ours.
178178
#[cfg(not(target_os = "hermit"))]
179-
let _ = libc::close(self.fd);
179+
{
180+
use crate::sys::os::errno;
181+
// ideally this would use assert_unsafe_precondition!, but that's only in core
182+
if cfg!(debug_assertions) {
183+
// close() can bubble up error codes from FUSE which can send semantically
184+
// inappropriate error codes including EBADF.
185+
// So we check file flags instead which live on the file descriptor and not the underlying file.
186+
// The downside is that it costs an extra syscall, so we only do it for debug.
187+
if libc::fcntl(self.fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
188+
rtabort!("IO Safety violation: owned file descriptor already closed");
189+
}
190+
}
191+
let _ = libc::close(self.fd);
192+
}
180193
#[cfg(target_os = "hermit")]
181194
let _ = hermit_abi::close(self.fd);
182195
}

std/src/sys/pal/unix/fs.rs

+21
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,27 @@ impl Iterator for ReadDir {
870870

871871
impl Drop for Dir {
872872
fn drop(&mut self) {
873+
// ideally this would use assert_unsafe_precondition!, but that's only in core
874+
#[cfg(all(
875+
debug_assertions,
876+
not(any(
877+
target_os = "redox",
878+
target_os = "nto",
879+
target_os = "vita",
880+
target_os = "hurd",
881+
))
882+
))]
883+
{
884+
use crate::sys::os::errno;
885+
// close() can bubble up error codes from FUSE which can send semantically
886+
// inappropriate error codes including EBADF.
887+
// So we check file flags instead which live on the file descriptor and not the underlying file.
888+
// The downside is that it costs an extra syscall, so we only do it for debug.
889+
let fd = unsafe { libc::dirfd(self.0) };
890+
if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF {
891+
rtabort!("IO Safety violation: DIR*'s owned file descriptor already closed");
892+
}
893+
}
873894
let r = unsafe { libc::closedir(self.0) };
874895
assert!(
875896
r == 0 || crate::io::Error::last_os_error().is_interrupted(),

0 commit comments

Comments
 (0)