Skip to content

Commit 154619b

Browse files
authored
Rollup merge of rust-lang#124210 - the8472:consign-ebadf-to-the-fire, r=Mark-Simulacrum
Abort a process when FD ownership is violated When an owned FD has already been closed before it's dropped that means something else 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. Unlike the previous attempt in rust-lang#124130 this shouldn't suffer from the possibility that FUSE filesystems can return arbitrary errors.
2 parents 609c633 + 072c32d commit 154619b

File tree

6 files changed

+47
-5
lines changed

6 files changed

+47
-5
lines changed

library/core/src/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2767,7 +2767,7 @@ pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
27672767
#[unstable(feature = "core_intrinsics", issue = "none")]
27682768
#[inline(always)]
27692769
#[cfg_attr(not(bootstrap), rustc_intrinsic)] // just make it a regular fn in bootstrap
2770-
pub(crate) const fn ub_checks() -> bool {
2770+
pub const fn ub_checks() -> bool {
27712771
cfg!(debug_assertions)
27722772
}
27732773

library/core/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193
#![feature(str_split_inclusive_remainder)]
194194
#![feature(str_split_remainder)]
195195
#![feature(strict_provenance)]
196+
#![feature(ub_checks)]
196197
#![feature(unchecked_shifts)]
197198
#![feature(utf16_extra)]
198199
#![feature(utf16_extra_const)]
@@ -370,7 +371,8 @@ pub mod hint;
370371
pub mod intrinsics;
371372
pub mod mem;
372373
pub mod ptr;
373-
mod ub_checks;
374+
#[unstable(feature = "ub_checks", issue = "none")]
375+
pub mod ub_checks;
374376

375377
/* Core language traits */
376378

library/core/src/ub_checks.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ use crate::intrinsics::{self, const_eval_select};
4646
/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
4747
/// debuginfo to have a measurable compile-time impact on debug builds.
4848
#[allow_internal_unstable(const_ub_checks)] // permit this to be called in stably-const fn
49+
#[macro_export]
50+
#[unstable(feature = "ub_checks", issue = "none")]
4951
macro_rules! assert_unsafe_precondition {
5052
($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
5153
{
@@ -75,11 +77,13 @@ macro_rules! assert_unsafe_precondition {
7577
}
7678
};
7779
}
78-
pub(crate) use assert_unsafe_precondition;
80+
#[unstable(feature = "ub_checks", issue = "none")]
81+
pub use assert_unsafe_precondition;
7982

8083
/// Checking library UB is always enabled when UB-checking is done
8184
/// (and we use a reexport so that there is no unnecessary wrapper function).
82-
pub(crate) use intrinsics::ub_checks as check_library_ub;
85+
#[unstable(feature = "ub_checks", issue = "none")]
86+
pub use intrinsics::ub_checks as check_library_ub;
8387

8488
/// Determines whether we should check for language UB.
8589
///

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@
356356
#![feature(str_internals)]
357357
#![feature(strict_provenance)]
358358
#![feature(strict_provenance_atomic_ptr)]
359+
#![feature(ub_checks)]
359360
// tidy-alphabetical-end
360361
//
361362
// Library features (alloc):

library/std/src/os/fd/owned.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ 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+
crate::sys::fs::debug_assert_fd_is_open(self.fd);
181+
let _ = libc::close(self.fd);
182+
}
180183
#[cfg(target_os = "hermit")]
181184
let _ = hermit_abi::close(self.fd);
182185
}

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

+32
Original file line numberDiff line numberDiff line change
@@ -864,8 +864,40 @@ impl Iterator for ReadDir {
864864
}
865865
}
866866

867+
/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled
868+
///
869+
/// Many IO syscalls can't be fully trusted about EBADF error codes because those
870+
/// might get bubbled up from a remote FUSE server rather than the file descriptor
871+
/// in the current process being invalid.
872+
///
873+
/// So we check file flags instead which live on the file descriptor and not the underlying file.
874+
/// The downside is that it costs an extra syscall, so we only do it for debug.
875+
#[inline]
876+
pub(crate) fn debug_assert_fd_is_open(fd: RawFd) {
877+
use crate::sys::os::errno;
878+
879+
// this is similar to assert_unsafe_precondition!() but it doesn't require const
880+
if core::ub_checks::check_library_ub() {
881+
if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF {
882+
rtabort!("IO Safety violation: owned file descriptor already closed");
883+
}
884+
}
885+
}
886+
867887
impl Drop for Dir {
868888
fn drop(&mut self) {
889+
// dirfd isn't supported everywhere
890+
#[cfg(not(any(
891+
miri,
892+
target_os = "redox",
893+
target_os = "nto",
894+
target_os = "vita",
895+
target_os = "hurd",
896+
)))]
897+
{
898+
let fd = unsafe { libc::dirfd(self.0) };
899+
debug_assert_fd_is_open(fd);
900+
}
869901
let r = unsafe { libc::closedir(self.0) };
870902
assert!(
871903
r == 0 || crate::io::Error::last_os_error().is_interrupted(),

0 commit comments

Comments
 (0)