From df0cf669f9f5afae7b0261d0dfb16b3b440d05b3 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 28 Jun 2025 11:09:08 -0700 Subject: [PATCH] Deprecate `unshare` and add `unshare_unsafe`. As pointed out in #1479, `unshare` can lead to threads observing dangling file descriptors. Deprecate it, and add a new `unshare_unsafe` that's unsafe, to reflect this. Fixes #1479. --- src/backend/libc/thread/syscalls.rs | 4 ++-- src/backend/linux_raw/thread/syscalls.rs | 4 ++-- src/thread/setns.rs | 25 +++++++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index 9198a7fbe..e79909590 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -343,8 +343,8 @@ pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result io::Result<()> { - unsafe { ret(c::unshare(flags.bits() as i32)) } +pub(crate) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { + ret(c::unshare(flags.bits() as i32)) } #[cfg(linux_kernel)] diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 352e06150..0888c29db 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -372,8 +372,8 @@ pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result io::Result<()> { - unsafe { ret(syscall_readonly!(__NR_unshare, flags)) } +pub(crate) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { + ret(syscall_readonly!(__NR_unshare, flags)) } #[inline] diff --git a/src/thread/setns.rs b/src/thread/setns.rs index 0eaee2f2a..9ec1205a9 100644 --- a/src/thread/setns.rs +++ b/src/thread/setns.rs @@ -1,3 +1,10 @@ +//! Thread-specific namespace functions. +//! +//! # Safety +//! +//! The `unshare` function can cause threads to use different file descriptor tables. +#![allow(unsafe_code)] + use bitflags::bitflags; use linux_raw_sys::general::{ CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, @@ -127,13 +134,29 @@ pub fn move_into_thread_name_spaces( syscalls::setns(fd, allowed_types.bits() as c_int).map(|_r| ()) } +/// `unshare(flags)`—Deprecated in favor of [`unshare_unsafe`]. +/// +/// This function should be unsafe; see the safety comment on `unshare_unsafe`. +#[deprecated(since = "1.1.0", note = "Use `unshare_unsafe`")] +pub fn unshare(flags: UnshareFlags) -> io::Result<()> { + // SAFETY: This is not actually safe. This function is deprecated and users + // should use `unshare_unsafe` instead. + unsafe { syscalls::unshare(flags) } +} + /// `unshare(flags)`—Disassociate parts of the current thread's execution /// context with other threads. /// +/// # Safety +/// +/// When using `UnshareFlags::FILES`, this function can cause one thread to be +/// unable to use file descriptors created on a different thread. Callers must +/// ensure that threads never observe file descriptors from unshared tables. +/// /// # References /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/unshare.2.html -pub fn unshare(flags: UnshareFlags) -> io::Result<()> { +pub unsafe fn unshare_unsafe(flags: UnshareFlags) -> io::Result<()> { syscalls::unshare(flags) }