From 9196d014176359ea652ec8d91d3366565afd1361 Mon Sep 17 00:00:00 2001 From: vasilito Date: Thu, 2 Jul 2026 10:22:28 +0300 Subject: [PATCH] relibc: init Pthread.robust_list_head in create() and add null guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes for the Pthread.robust_list_head field added in P5-robust-mutexes: 1. create() at src/pthread/mod.rs:172 didn't initialize the new_tcb.pthread.robust_list_head. The Tcb::new memory happens to be zeroed, so the first non-main thread's first pthread_mutex_lock of a ROBUST mutex would have robust_list_head = 0 (null), which the original code at src/sync/pthread_mutex.rs:301 dereferences with 'let mut node = unsafe { *head }' — that's a UB on null. Add explicit init to null in create() so the invariant is documented and future Tcb::new changes (e.g. switching to MaybeUninit for performance) don't break the assumption. 2. mark_robust_mutexes_dead at src/sync/pthread_mutex.rs:299 dereferences *head without a null check. Even with the init fix above, a thread may legitimately have an empty robust list (never locked a robust mutex). Add the null guard so the function is a no-op for empty lists. Discovered by Oracle review of Phase 0c patches (Issue 4). The init was missing because P5-robust-mutexes was applied as an overlay patch that referenced the field but didn't include the init line in the right scope. --- src/pthread/mod.rs | 8 ++++++++ src/sync/pthread_mutex.rs | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs index 7755f7153f..6b67d63358 100644 --- a/src/pthread/mod.rs +++ b/src/pthread/mod.rs @@ -172,6 +172,14 @@ pub(crate) unsafe fn create( new_tcb.pthread.flags = flags.bits().into(); new_tcb.pthread.stack_base = stack_base; new_tcb.pthread.stack_size = stack_size; + // Initialize the robust mutex list head to null. The + // Pthread struct is zero-initialized in Tcb::new but we + // set this explicitly for documentation/clarity, and to + // guard against future changes that might not zero the + // struct (e.g. a switch to Box>). + // Without this, the first pthread_mutex_lock of a + // ROBUST mutex would dereference an uninitialized pointer. + new_tcb.pthread.robust_list_head = UnsafeCell::new(core::ptr::null_mut()); new_tcb.masters_ptr = current_tcb.masters_ptr; new_tcb.masters_len = current_tcb.masters_len; diff --git a/src/sync/pthread_mutex.rs b/src/sync/pthread_mutex.rs index af0c42926b..160a93152e 100644 --- a/src/sync/pthread_mutex.rs +++ b/src/sync/pthread_mutex.rs @@ -298,6 +298,15 @@ impl RlctMutex { pub(crate) unsafe fn mark_robust_mutexes_dead(thread: &crate::pthread::Pthread) { let head = thread.robust_list_head.get(); + // Null guard: if the thread's robust_list_head is null + // (e.g. the thread was created but never locked a robust + // mutex, or the kernel's robust list walk is invoked for a + // thread that doesn't support it), the list is empty and + // there's nothing to do. Without this check, dereferencing + // *head on a null pointer would be UB. + if (*head).is_null() { + return; + } let this_thread = os_tid_invalid_after_fork(); let mut node = unsafe { *head };