From 155380523c1fe0381255556d2b0588f9a0fdcacd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Oct 2024 10:40:38 +0200 Subject: [PATCH] pthread mutex: better error in reentrant-locking-UB, also test PTHREAD_MUTEX_INITIALIZER --- src/tools/miri/src/shims/unix/sync.rs | 4 +++- ...ck.rs => libc_pthread_mutex_NULL_reentrant.rs} | 4 ++-- ...r => libc_pthread_mutex_NULL_reentrant.stderr} | 8 ++++---- ...rs => libc_pthread_mutex_default_reentrant.rs} | 9 +++++++-- ...> libc_pthread_mutex_default_reentrant.stderr} | 8 ++++---- ....rs => libc_pthread_mutex_normal_reentrant.rs} | 2 ++ ...=> libc_pthread_mutex_normal_reentrant.stderr} | 4 ++-- .../libc_pthread_mutex_staticinit_reentrant.rs | 12 ++++++++++++ ...libc_pthread_mutex_staticinit_reentrant.stderr | 15 +++++++++++++++ 9 files changed, 51 insertions(+), 15 deletions(-) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_NULL_deadlock.rs => libc_pthread_mutex_NULL_reentrant.rs} (72%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_NULL_deadlock.stderr => libc_pthread_mutex_NULL_reentrant.stderr} (68%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_default_deadlock.rs => libc_pthread_mutex_default_reentrant.rs} (52%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_default_deadlock.stderr => libc_pthread_mutex_default_reentrant.stderr} (68%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_normal_deadlock.rs => libc_pthread_mutex_normal_reentrant.rs} (81%) rename src/tools/miri/tests/fail-dep/concurrency/{libc_pthread_mutex_normal_deadlock.stderr => libc_pthread_mutex_normal_reentrant.stderr} (79%) create mode 100644 src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs create mode 100644 src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 017291f81a2..b05f340861e 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -483,7 +483,9 @@ fn pthread_mutex_lock( // Trying to acquire the same mutex again. match kind { MutexKind::Default => - throw_ub_format!("trying to acquire already locked default mutex"), + throw_ub_format!( + "trying to acquire default mutex already locked by the current thread" + ), MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs similarity index 72% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs index a79abe65328..f2df8bdca12 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs @@ -1,12 +1,12 @@ //@ignore-target: windows # No pthreads on Windows // -// Check that if we pass NULL attribute, then we get the default mutex type. +// Check that if we pass NULL attribute, then reentrant locking is UB. fn main() { unsafe { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null() as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: Undefined Behavior: trying to acquire already locked default mutex + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: already locked by the current thread } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.stderr similarity index 68% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.stderr index e9961ed413d..9455e704376 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: trying to acquire already locked default mutex - --> tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs:LL:CC +error: Undefined Behavior: trying to acquire default mutex already locked by the current thread + --> tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire already locked default mutex + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire default mutex already locked by the current thread | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs:LL:CC + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_NULL_reentrant.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs similarity index 52% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs index d9293f938b6..d2d0ffff07a 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs @@ -1,6 +1,11 @@ //@ignore-target: windows # No pthreads on Windows // -// Check that if we do not set the mutex type, it is the default. +// Check that if we do not set the mutex type, it is UB to do reentrant locking. glibc apparently +// actually exploits this, see +// : +// one must actively call pthread_mutexattr_settype to disable lock elision. This means a call to +// pthread_mutexattr_settype(PTHREAD_MUTEX_NORMAL) makes a difference even if +// PTHREAD_MUTEX_NORMAL == PTHREAD_MUTEX_DEFAULT! fn main() { unsafe { @@ -9,6 +14,6 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: Undefined Behavior: trying to acquire already locked default mutex + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: already locked by the current thread } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.stderr similarity index 68% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.stderr index a57d10753d9..a9ffbde1b65 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: trying to acquire already locked default mutex - --> tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs:LL:CC +error: Undefined Behavior: trying to acquire default mutex already locked by the current thread + --> tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire already locked default mutex + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire default mutex already locked by the current thread | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs:LL:CC + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_default_reentrant.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs similarity index 81% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs index b38582482b8..9a88639edf7 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs @@ -10,6 +10,8 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + // A "normal" mutex properly tries to acquire the lock even if its is already held + // by the current thread -- and then we deadlock. libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: deadlock: the evaluated program deadlocked } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr similarity index 79% rename from src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr index 4337475963e..f20b26297e2 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr @@ -1,11 +1,11 @@ error: deadlock: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs:LL:CC + --> tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked | = note: BACKTRACE: - = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs:LL:CC + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs new file mode 100644 index 00000000000..bd8aef787e6 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs @@ -0,0 +1,12 @@ +//@ignore-target: windows # No pthreads on Windows +// +// Check that if we use PTHREAD_MUTEX_INITIALIZER, then reentrant locking is UB. +// glibc apparently actually exploits this so we better catch it! + +fn main() { + unsafe { + let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; + assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: already locked by the current thread + } +} diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr new file mode 100644 index 00000000000..984bb07b728 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: trying to acquire default mutex already locked by the current thread + --> tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs:LL:CC + | +LL | libc::pthread_mutex_lock(&mut mutex as *mut _); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire default mutex already locked by the current thread + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_staticinit_reentrant.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error +