Skip to content

Commit 0aa3fd4

Browse files
committed
Auto merge of rust-lang#3871 - Mandragorian:detect_rwlock_move, r=RalfJung
detect when pthread_rwlock_t is moved For some implementations of pthreads, the address of pthread_rwlock_t (or its fields) is used to identify the lock. That means that if the contents of a pthread_rwlock_t are moved in memory, effectively a new lock object is created, which is completely independted from the original. Thus we want to detect when when such objects are moved and show an error. see also rust-lang#3749 for more context
2 parents 797ddd2 + ff28977 commit 0aa3fd4

File tree

5 files changed

+68
-5
lines changed

5 files changed

+68
-5
lines changed

Diff for: src/tools/miri/src/concurrency/sync.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ struct Mutex {
105105

106106
declare_id!(RwLockId);
107107

108+
#[derive(Debug)]
109+
/// Additional data that may be used by shim implementations.
110+
pub struct AdditionalRwLockData {
111+
/// The address of the rwlock.
112+
pub address: u64,
113+
}
114+
108115
/// The read-write lock state.
109116
#[derive(Default, Debug)]
110117
struct RwLock {
@@ -137,6 +144,9 @@ struct RwLock {
137144
/// locks.
138145
/// This is only relevant when there is an active reader.
139146
clock_current_readers: VClock,
147+
148+
/// Additional data that can be set by shim implementations.
149+
data: Option<AdditionalRwLockData>,
140150
}
141151

142152
declare_id!(CondvarId);
@@ -343,18 +353,31 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
343353
lock_op: &OpTy<'tcx>,
344354
lock_layout: TyAndLayout<'tcx>,
345355
offset: u64,
356+
initialize_data: impl for<'a> FnOnce(
357+
&'a mut MiriInterpCx<'tcx>,
358+
)
359+
-> InterpResult<'tcx, Option<AdditionalRwLockData>>,
346360
) -> InterpResult<'tcx, RwLockId> {
347361
let this = self.eval_context_mut();
348362
this.get_or_create_id(
349363
lock_op,
350364
lock_layout,
351365
offset,
352366
|ecx| &mut ecx.machine.sync.rwlocks,
353-
|_| Ok(Default::default()),
367+
|ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }),
354368
)?
355369
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
356370
}
357371

372+
/// Retrieve the additional data stored for a rwlock.
373+
fn rwlock_get_data<'a>(&'a mut self, id: RwLockId) -> Option<&'a AdditionalRwLockData>
374+
where
375+
'tcx: 'a,
376+
{
377+
let this = self.eval_context_ref();
378+
this.machine.sync.rwlocks[id].data.as_ref()
379+
}
380+
358381
fn condvar_get_or_create_id(
359382
&mut self,
360383
lock_op: &OpTy<'tcx>,

Diff for: src/tools/miri/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ pub use crate::concurrency::{
134134
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
135135
init_once::{EvalContextExt as _, InitOnceId},
136136
sync::{
137-
AdditionalMutexData, CondvarId, EvalContextExt as _, MutexId, MutexKind, RwLockId,
138-
SynchronizationObjects,
137+
AdditionalMutexData, AdditionalRwLockData, CondvarId, EvalContextExt as _, MutexId,
138+
MutexKind, RwLockId, SynchronizationObjects,
139139
},
140140
thread::{
141141
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,

Diff for: src/tools/miri/src/shims/unix/sync.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,22 @@ fn rwlock_get_id<'tcx>(
213213
ecx: &mut MiriInterpCx<'tcx>,
214214
rwlock_op: &OpTy<'tcx>,
215215
) -> InterpResult<'tcx, RwLockId> {
216-
ecx.rwlock_get_or_create_id(
216+
let address = ecx.read_pointer(rwlock_op)?.addr().bytes();
217+
218+
let id = ecx.rwlock_get_or_create_id(
217219
rwlock_op,
218220
ecx.libc_ty_layout("pthread_rwlock_t"),
219221
rwlock_id_offset(ecx)?,
220-
)
222+
|_| Ok(Some(AdditionalRwLockData { address })),
223+
)?;
224+
225+
// Check that the rwlock has not been moved since last use.
226+
let data = ecx.rwlock_get_data(id).expect("data should be always exist for pthreads");
227+
if data.address != address {
228+
throw_ub_format!("pthread_rwlock_t can't be moved after first use")
229+
}
230+
231+
Ok(id)
221232
}
222233

223234
// pthread_condattr_t.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ignore-target-windows: No pthreads on Windows
2+
3+
fn main() {
4+
unsafe {
5+
let mut rw = libc::PTHREAD_RWLOCK_INITIALIZER;
6+
7+
libc::pthread_rwlock_rdlock(&mut rw as *mut _);
8+
9+
// Move rwlock
10+
let mut rw2 = rw;
11+
12+
libc::pthread_rwlock_unlock(&mut rw2 as *mut _); //~ ERROR: pthread_rwlock_t can't be moved after first use
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: pthread_rwlock_t can't be moved after first use
2+
--> $DIR/libx_pthread_rwlock_moved.rs:LL:CC
3+
|
4+
LL | libc::pthread_rwlock_unlock(&mut rw2 as *mut _);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_rwlock_t can't be moved after first use
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `main` at $DIR/libx_pthread_rwlock_moved.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

0 commit comments

Comments
 (0)