From 1c194990f82cdb6b040801afff22d114d19d2185 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sun, 5 Jul 2026 04:05:30 +0700 Subject: [PATCH] Fix crash when exiting from detached thread --- redox-rt/src/signal.rs | 1 + redox-rt/src/sys.rs | 3 ++- redox-rt/src/thread.rs | 16 ++++++++++++---- src/pthread/mod.rs | 24 +++++++++++++++++++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 71dad4ad5f..a06eaae227 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -541,6 +541,7 @@ pub struct TmpDisableSignalsGuard { active: bool, } +/// Used to prevent EINTR from appearing across all syscall pub fn tmp_disable_signals() -> TmpDisableSignalsGuard { if !crate::TLS_ACTIVATED.load(Ordering::Relaxed) { return TmpDisableSignalsGuard { active: false }; diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 4fc1e8bdde..683b3ccaea 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -527,7 +527,8 @@ pub fn posix_exit(status: i32) -> ! { &[ProcCall::Exit as u64, (status & 0xFF) as u64], ) { Ok(_) => break, - // TODO: procmgr can sometimes send EAGAIN, but why EINTR needed? + // procmgr can sometimes send EAGAIN. + // cannot disable signals so kernel might send EINTR. Err(Error { errno: EINTR } | Error { errno: EAGAIN }) => continue, Err(e) => panic!("failed to call proc mgr with Exit: {e}"), } diff --git a/redox-rt/src/thread.rs b/redox-rt/src/thread.rs index 3536257db3..853d8d6114 100644 --- a/redox-rt/src/thread.rs +++ b/redox-rt/src/thread.rs @@ -2,7 +2,7 @@ use core::mem::size_of; use syscall::Result; -use crate::{RtTcb, arch::*, proc::*, signal::tmp_disable_signals, static_proc_info}; +use crate::{RtTcb, arch::*, proc::*, static_proc_info}; /// Spawns a new context sharing the same address space as the current one (i.e. a new thread). pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result { @@ -52,12 +52,20 @@ pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result { } pub unsafe fn exit_this_thread(stack_base: *mut (), stack_size: usize) -> ! { - let _guard = tmp_disable_signals(); + let _guard = crate::signal::tmp_disable_signals(); + let thread_fd = RtTcb::current().thread_fd(); - let tcb = RtTcb::current(); // TODO: modify interface so it writes directly to the thread fd? - let status_fd = tcb.thread_fd().dup_into_upper(b"status").unwrap(); + let status_fd = thread_fd.dup_into_upper(b"status").unwrap(); + unsafe { exit_this_thread_inner(stack_base, stack_size, status_fd) } +} +/// exit_this_thread, but current thread_fd might has disappear +pub unsafe fn exit_this_thread_inner( + stack_base: *mut (), + stack_size: usize, + status_fd: FdGuard, +) -> ! { let mut buf = [0; size_of::() * 3]; plain::slice_from_mut_bytes(&mut buf) .unwrap() diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs index 4d488e37df..2811e97b02 100644 --- a/src/pthread/mod.rs +++ b/src/pthread/mod.rs @@ -7,6 +7,8 @@ use core::{ }; use alloc::collections::BTreeMap; +#[cfg(target_os = "redox")] +use redox_rt::{RtTcb, proc::FdGuard}; use crate::{ error::Errno, @@ -307,9 +309,22 @@ pub unsafe fn exit_current_thread(retval: Retval) -> ! { let stack_size = this.stack_size; if this.flags.load(Ordering::Acquire) & PthreadFlags::DETACHED.bits() != 0 { + // dealloc_thread() unmaps the tcb so extract the thread_fd now + #[cfg(target_os = "redox")] + let status_fd = { + let _guard = redox_rt::signal::tmp_disable_signals(); + let thread_fd = FdGuard::new(RtTcb::current().thread_fd().as_raw_fd()); + thread_fd.dup_into_upper(b"status").unwrap() + }; + // When detached, the thread state no longer makes any sense, and can immediately be // deallocated. unsafe { dealloc_thread(this) }; + + #[cfg(target_os = "redox")] + unsafe { + redox_rt::thread::exit_this_thread_inner(stack_base.cast(), stack_size, status_fd) + } } else { // When joinable, the return value should be made available to other threads. unsafe { this.waitval.post(retval) }; @@ -327,7 +342,14 @@ unsafe fn dealloc_thread(thread: &Pthread) { unsafe { let tcb = thread.tcb_selfref.get().read(); if !tcb.is_null() { - let _ = syscall::funmap(tcb as usize, syscall::PAGE_SIZE); + loop { + match syscall::funmap(tcb as usize, syscall::PAGE_SIZE) { + Ok(_) => break, + // might be not guarded by tmp_disable_signals() + Err(syscall::Error { errno: EINTR }) => continue, + Err(e) => log::warn!("Cannot dealloc_thread: {e:?}"), + } + } } } }