Skip to content

Commit c3f2701

Browse files
committed
Auto merge of rust-lang#3560 - RalfJung:sync-check-id, r=RalfJung
sync: better error in invalid synchronization primitive ID `@devnexen` this should fix the ICE in your PR (but it won't fix the code, it will just report proper UB instead).
2 parents 37537d1 + 9503c41 commit c3f2701

File tree

4 files changed

+67
-9
lines changed

4 files changed

+67
-9
lines changed

src/tools/miri/src/concurrency/sync.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
305305
let this = self.eval_context_mut();
306306
let next_index = this.machine.threads.sync.mutexes.next_index();
307307
if let Some(old) = existing(this, next_index)? {
308+
if this.machine.threads.sync.mutexes.get(old).is_none() {
309+
throw_ub_format!("mutex has invalid ID");
310+
}
308311
Ok(old)
309312
} else {
310313
let new_index = this.machine.threads.sync.mutexes.push(Default::default());
@@ -399,6 +402,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
399402
let this = self.eval_context_mut();
400403
let next_index = this.machine.threads.sync.rwlocks.next_index();
401404
if let Some(old) = existing(this, next_index)? {
405+
if this.machine.threads.sync.rwlocks.get(old).is_none() {
406+
throw_ub_format!("rwlock has invalid ID");
407+
}
402408
Ok(old)
403409
} else {
404410
let new_index = this.machine.threads.sync.rwlocks.push(Default::default());
@@ -563,6 +569,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
563569
let this = self.eval_context_mut();
564570
let next_index = this.machine.threads.sync.condvars.next_index();
565571
if let Some(old) = existing(this, next_index)? {
572+
if this.machine.threads.sync.condvars.get(old).is_none() {
573+
throw_ub_format!("condvar has invalid ID");
574+
}
566575
Ok(old)
567576
} else {
568577
let new_index = this.machine.threads.sync.condvars.push(Default::default());

src/tools/miri/src/shims/unix/sync.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
6565
// (need to avoid this because it is set by static initializer macros)
6666
// bytes 4-7: mutex id as u32 or 0 if id is not assigned yet.
6767
// bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32
68-
// (the kind has to be at its offset for compatibility with static initializer macros)
68+
// (the kind has to be at this particular offset for compatibility with Linux's static initializer
69+
// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
6970

7071
fn mutex_get_id<'mir, 'tcx: 'mir>(
7172
ecx: &mut MiriInterpCx<'mir, 'tcx>,
@@ -123,11 +124,13 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
123124
// (need to avoid this because it is set by static initializer macros)
124125
// bytes 4-7: rwlock id as u32 or 0 if id is not assigned yet.
125126

127+
const RWLOCK_ID_OFFSET: u64 = 4;
128+
126129
fn rwlock_get_id<'mir, 'tcx: 'mir>(
127130
ecx: &mut MiriInterpCx<'mir, 'tcx>,
128131
rwlock_op: &OpTy<'tcx, Provenance>,
129132
) -> InterpResult<'tcx, RwLockId> {
130-
ecx.rwlock_get_or_create_id(rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), 4)
133+
ecx.rwlock_get_or_create_id(rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), RWLOCK_ID_OFFSET)
131134
}
132135

133136
// pthread_condattr_t
@@ -136,13 +139,15 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>(
136139
// store an i32 in the first four bytes equal to the corresponding libc clock id constant
137140
// (e.g. CLOCK_REALTIME).
138141

142+
const CONDATTR_CLOCK_OFFSET: u64 = 0;
143+
139144
fn condattr_get_clock_id<'mir, 'tcx: 'mir>(
140145
ecx: &MiriInterpCx<'mir, 'tcx>,
141146
attr_op: &OpTy<'tcx, Provenance>,
142147
) -> InterpResult<'tcx, i32> {
143148
ecx.deref_pointer_and_read(
144149
attr_op,
145-
0,
150+
CONDATTR_CLOCK_OFFSET,
146151
ecx.libc_ty_layout("pthread_condattr_t"),
147152
ecx.machine.layouts.i32,
148153
)?
@@ -156,7 +161,7 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
156161
) -> InterpResult<'tcx, ()> {
157162
ecx.deref_pointer_and_write(
158163
attr_op,
159-
0,
164+
CONDATTR_CLOCK_OFFSET,
160165
Scalar::from_i32(clock_id),
161166
ecx.libc_ty_layout("pthread_condattr_t"),
162167
ecx.machine.layouts.i32,
@@ -172,11 +177,14 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
172177
// bytes 4-7: the conditional variable id as u32 or 0 if id is not assigned yet.
173178
// bytes 8-11: the clock id constant as i32
174179

180+
const CONDVAR_ID_OFFSET: u64 = 4;
181+
const CONDVAR_CLOCK_OFFSET: u64 = 8;
182+
175183
fn cond_get_id<'mir, 'tcx: 'mir>(
176184
ecx: &mut MiriInterpCx<'mir, 'tcx>,
177185
cond_op: &OpTy<'tcx, Provenance>,
178186
) -> InterpResult<'tcx, CondvarId> {
179-
ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), 4)
187+
ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), CONDVAR_ID_OFFSET)
180188
}
181189

182190
fn cond_reset_id<'mir, 'tcx: 'mir>(
@@ -185,7 +193,7 @@ fn cond_reset_id<'mir, 'tcx: 'mir>(
185193
) -> InterpResult<'tcx, ()> {
186194
ecx.deref_pointer_and_write(
187195
cond_op,
188-
4,
196+
CONDVAR_ID_OFFSET,
189197
Scalar::from_i32(0),
190198
ecx.libc_ty_layout("pthread_cond_t"),
191199
ecx.machine.layouts.u32,
@@ -198,7 +206,7 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>(
198206
) -> InterpResult<'tcx, i32> {
199207
ecx.deref_pointer_and_read(
200208
cond_op,
201-
8,
209+
CONDVAR_CLOCK_OFFSET,
202210
ecx.libc_ty_layout("pthread_cond_t"),
203211
ecx.machine.layouts.i32,
204212
)?
@@ -212,7 +220,7 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>(
212220
) -> InterpResult<'tcx, ()> {
213221
ecx.deref_pointer_and_write(
214222
cond_op,
215-
8,
223+
CONDVAR_CLOCK_OFFSET,
216224
Scalar::from_i32(clock_id),
217225
ecx.libc_ty_layout("pthread_cond_t"),
218226
ecx.machine.layouts.i32,
@@ -719,6 +727,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
719727
) -> InterpResult<'tcx, Scalar<Provenance>> {
720728
let this = self.eval_context_mut();
721729

730+
// Does not exist on macOS!
731+
if !matches!(&*this.tcx.sess.target.os, "linux") {
732+
throw_unsup_format!(
733+
"`pthread_condattr_init` is not supported on {}",
734+
this.tcx.sess.target.os
735+
);
736+
}
737+
722738
let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
723739
if clock_id == this.eval_libc_i32("CLOCK_REALTIME")
724740
|| clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")
@@ -739,6 +755,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
739755
) -> InterpResult<'tcx, Scalar<Provenance>> {
740756
let this = self.eval_context_mut();
741757

758+
// Does not exist on macOS!
759+
if !matches!(&*this.tcx.sess.target.os, "linux") {
760+
throw_unsup_format!(
761+
"`pthread_condattr_init` is not supported on {}",
762+
this.tcx.sess.target.os
763+
);
764+
}
765+
742766
let clock_id = condattr_get_clock_id(this, attr_op)?;
743767
this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?;
744768

src/tools/miri/tests/pass-dep/shims/libc-rsfs.stdout

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/tools/miri/tests/pass-dep/shims/pthread-sync.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ fn main() {
1919
check_rwlock_write();
2020
check_rwlock_read_no_deadlock();
2121
check_cond();
22+
check_condattr();
2223
}
2324

2425
fn test_mutex_libc_init_recursive() {
@@ -261,6 +262,31 @@ fn check_cond() {
261262
}
262263
}
263264

265+
fn check_condattr() {
266+
unsafe {
267+
// Just smoke-testing that these functions can be called.
268+
let mut attr: MaybeUninit<libc::pthread_condattr_t> = MaybeUninit::uninit();
269+
assert_eq!(libc::pthread_condattr_init(attr.as_mut_ptr()), 0);
270+
271+
#[cfg(not(target_os = "macos"))] // setclock-getclock do not exist on macOS
272+
{
273+
let clock_id = libc::CLOCK_MONOTONIC;
274+
assert_eq!(libc::pthread_condattr_setclock(attr.as_mut_ptr(), clock_id), 0);
275+
let mut check_clock_id = MaybeUninit::<libc::clockid_t>::uninit();
276+
assert_eq!(
277+
libc::pthread_condattr_getclock(attr.as_mut_ptr(), check_clock_id.as_mut_ptr()),
278+
0
279+
);
280+
assert_eq!(check_clock_id.assume_init(), clock_id);
281+
}
282+
283+
let mut cond: MaybeUninit<libc::pthread_cond_t> = MaybeUninit::uninit();
284+
assert_eq!(libc::pthread_cond_init(cond.as_mut_ptr(), attr.as_ptr()), 0);
285+
assert_eq!(libc::pthread_condattr_destroy(attr.as_mut_ptr()), 0);
286+
assert_eq!(libc::pthread_cond_destroy(cond.as_mut_ptr()), 0);
287+
}
288+
}
289+
264290
// std::sync::RwLock does not even used pthread_rwlock any more.
265291
// Do some smoke testing of the API surface.
266292
fn test_rwlock_libc_static_initializer() {

0 commit comments

Comments
 (0)