relibc: init Pthread.robust_list_head in create() and add null guard

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.
This commit is contained in:
2026-07-02 10:22:28 +03:00
parent 36f95af890
commit 9196d01417
2 changed files with 17 additions and 0 deletions
+8
View File
@@ -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<MaybeUninit<Pthread>>).
// 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;
+9
View File
@@ -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 };