Skip to content

Commit 0570fb4

Browse files
authored
Merge pull request #4499 from RalfJung/clockid
centralize clockid_t interpretation
2 parents c751579 + a20692c commit 0570fb4

File tree

4 files changed

+79
-114
lines changed

4 files changed

+79
-114
lines changed

src/tools/miri/src/concurrency/thread.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ impl Timeout {
375375
}
376376

377377
/// The clock to use for the timeout you are asking for.
378-
#[derive(Debug, Copy, Clone)]
378+
#[derive(Debug, Copy, Clone, PartialEq)]
379379
pub enum TimeoutClock {
380380
Monotonic,
381381
RealTime,

src/tools/miri/src/shims/time.rs

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,73 +17,71 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du
1717

1818
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
1919
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
20-
fn clock_gettime(
21-
&mut self,
22-
clk_id_op: &OpTy<'tcx>,
23-
tp_op: &OpTy<'tcx>,
24-
dest: &MPlaceTy<'tcx>,
25-
) -> InterpResult<'tcx> {
20+
fn parse_clockid(&self, clk_id: Scalar) -> Option<TimeoutClock> {
2621
// This clock support is deliberately minimal because a lot of clock types have fiddly
2722
// properties (is it possible for Miri to be suspended independently of the host?). If you
2823
// have a use for another clock type, please open an issue.
24+
let this = self.eval_context_ref();
2925

30-
let this = self.eval_context_mut();
31-
32-
this.assert_target_os_is_unix("clock_gettime");
33-
let clockid_t_size = this.libc_ty_layout("clockid_t").size;
34-
35-
let clk_id = this.read_scalar(clk_id_op)?.to_int(clockid_t_size)?;
36-
let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?;
37-
38-
let absolute_clocks;
39-
let mut relative_clocks;
26+
// Portable names that exist everywhere.
27+
if clk_id == this.eval_libc("CLOCK_REALTIME") {
28+
return Some(TimeoutClock::RealTime);
29+
} else if clk_id == this.eval_libc("CLOCK_MONOTONIC") {
30+
return Some(TimeoutClock::Monotonic);
31+
}
4032

33+
// Some further platform-specific names we support.
4134
match this.tcx.sess.target.os.as_ref() {
4235
"linux" | "freebsd" | "android" => {
43-
// Linux, Android, and FreeBSD have two main kinds of clocks. REALTIME clocks return the actual time since the
44-
// Unix epoch, including effects which may cause time to move backwards such as NTP.
4536
// Linux further distinguishes regular and "coarse" clocks, but the "coarse" version
46-
// is just specified to be "faster and less precise", so we implement both the same way.
47-
absolute_clocks = vec![
48-
this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?,
49-
this.eval_libc("CLOCK_REALTIME_COARSE").to_int(clockid_t_size)?,
50-
];
51-
// The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are
52-
// never allowed to go backwards. We don't need to do any additional monotonicity
53-
// enforcement because std::time::Instant already guarantees that it is monotonic.
54-
relative_clocks = vec![
55-
this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?,
56-
this.eval_libc("CLOCK_MONOTONIC_COARSE").to_int(clockid_t_size)?,
57-
];
37+
// is just specified to be "faster and less precise", so we treat it like normal
38+
// clocks.
39+
if clk_id == this.eval_libc("CLOCK_REALTIME_COARSE") {
40+
return Some(TimeoutClock::RealTime);
41+
} else if clk_id == this.eval_libc("CLOCK_MONOTONIC_COARSE") {
42+
return Some(TimeoutClock::Monotonic);
43+
}
5844
}
5945
"macos" => {
60-
absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?];
61-
relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?];
6246
// `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but
6347
// that's not really something a program running inside Miri can tell, anyway.
6448
// We need to support it because std uses it.
65-
relative_clocks.push(this.eval_libc("CLOCK_UPTIME_RAW").to_int(clockid_t_size)?);
66-
}
67-
"solaris" | "illumos" => {
68-
// The REALTIME clock returns the actual time since the Unix epoch.
69-
absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?];
70-
// MONOTONIC, in the other hand, is the high resolution, non-adjustable
71-
// clock from an arbitrary time in the past.
72-
// Note that the man page mentions HIGHRES but it is just
73-
// an alias of MONOTONIC and the libc crate does not expose it anyway.
74-
// https://docs.oracle.com/cd/E23824_01/html/821-1465/clock-gettime-3c.html
75-
relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?];
49+
if clk_id == this.eval_libc("CLOCK_UPTIME_RAW") {
50+
return Some(TimeoutClock::Monotonic);
51+
}
7652
}
77-
target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"),
53+
_ => {}
7854
}
7955

80-
let duration = if absolute_clocks.contains(&clk_id) {
81-
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
82-
system_time_to_duration(&SystemTime::now())?
83-
} else if relative_clocks.contains(&clk_id) {
84-
this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch())
85-
} else {
86-
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
56+
None
57+
}
58+
59+
fn clock_gettime(
60+
&mut self,
61+
clk_id_op: &OpTy<'tcx>,
62+
tp_op: &OpTy<'tcx>,
63+
dest: &MPlaceTy<'tcx>,
64+
) -> InterpResult<'tcx> {
65+
let this = self.eval_context_mut();
66+
67+
this.assert_target_os_is_unix("clock_gettime");
68+
69+
let clk_id = this.read_scalar(clk_id_op)?;
70+
let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?;
71+
72+
let duration = match this.parse_clockid(clk_id) {
73+
Some(TimeoutClock::RealTime) => {
74+
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
75+
system_time_to_duration(&SystemTime::now())?
76+
}
77+
Some(TimeoutClock::Monotonic) =>
78+
this.machine
79+
.monotonic_clock
80+
.now()
81+
.duration_since(this.machine.monotonic_clock.epoch()),
82+
None => {
83+
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
84+
}
8785
};
8886

8987
let tv_sec = duration.as_secs();

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

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -228,26 +228,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
228228
let abs_time_flag = flags == abs_time;
229229

230230
let clock_id_place = this.project_field(ut, FieldIdx::from_u32(2))?;
231-
let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?;
232-
let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?;
231+
let clock_id = this.read_scalar(&clock_id_place)?;
232+
let Some(timeout_clock) = this.parse_clockid(clock_id) else {
233+
throw_unsup_format!("unsupported clock")
234+
};
235+
if timeout_clock == TimeoutClock::RealTime {
236+
this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME`")?;
237+
}
233238

234239
interp_ok(Some(UmtxTime { timeout: duration, abs_time: abs_time_flag, timeout_clock }))
235240
}
236-
237-
/// Translate raw FreeBSD clockid to a Miri TimeoutClock.
238-
/// FIXME: share this code with the pthread and clock_gettime shims.
239-
fn translate_umtx_time_clock_id(&mut self, raw_id: i32) -> InterpResult<'tcx, TimeoutClock> {
240-
let this = self.eval_context_mut();
241-
242-
let timeout = if raw_id == this.eval_libc_i32("CLOCK_REALTIME") {
243-
// RealTime clock can't be used in isolation mode.
244-
this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME` timeout")?;
245-
TimeoutClock::RealTime
246-
} else if raw_id == this.eval_libc_i32("CLOCK_MONOTONIC") {
247-
TimeoutClock::Monotonic
248-
} else {
249-
throw_unsup_format!("unsupported clock id {raw_id}");
250-
};
251-
interp_ok(timeout)
252-
}
253241
}

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

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,13 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u
297297
fn condattr_get_clock_id<'tcx>(
298298
ecx: &MiriInterpCx<'tcx>,
299299
attr_ptr: &OpTy<'tcx>,
300-
) -> InterpResult<'tcx, i32> {
300+
) -> InterpResult<'tcx, Scalar> {
301301
ecx.deref_pointer_and_read(
302302
attr_ptr,
303303
condattr_clock_offset(ecx)?,
304304
ecx.libc_ty_layout("pthread_condattr_t"),
305305
ecx.machine.layouts.i32,
306-
)?
307-
.to_i32()
306+
)
308307
}
309308

310309
fn condattr_set_clock_id<'tcx>(
@@ -321,20 +320,6 @@ fn condattr_set_clock_id<'tcx>(
321320
)
322321
}
323322

324-
/// Translates the clock from what is stored in pthread_condattr_t to our enum.
325-
fn condattr_translate_clock_id<'tcx>(
326-
ecx: &MiriInterpCx<'tcx>,
327-
raw_id: i32,
328-
) -> InterpResult<'tcx, ClockId> {
329-
interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") {
330-
ClockId::Realtime
331-
} else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") {
332-
ClockId::Monotonic
333-
} else {
334-
throw_unsup_format!("unsupported clock id: {raw_id}");
335-
})
336-
}
337-
338323
// # pthread_cond_t
339324
// We store some data directly inside the type, ignoring the platform layout:
340325
// - init: u32
@@ -363,22 +348,16 @@ fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size>
363348
interp_ok(offset)
364349
}
365350

366-
#[derive(Debug, Clone, Copy)]
367-
enum ClockId {
368-
Realtime,
369-
Monotonic,
370-
}
371-
372351
#[derive(Debug, Clone)]
373352
struct PthreadCondvar {
374353
condvar_ref: CondvarRef,
375-
clock: ClockId,
354+
clock: TimeoutClock,
376355
}
377356

378357
fn cond_create<'tcx>(
379358
ecx: &mut MiriInterpCx<'tcx>,
380359
cond_ptr: &OpTy<'tcx>,
381-
clock: ClockId,
360+
clock: TimeoutClock,
382361
) -> InterpResult<'tcx, PthreadCondvar> {
383362
let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
384363
let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock };
@@ -407,7 +386,10 @@ where
407386
throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`");
408387
}
409388
// This used the static initializer. The clock there is always CLOCK_REALTIME.
410-
interp_ok(PthreadCondvar { condvar_ref: CondvarRef::new(), clock: ClockId::Realtime })
389+
interp_ok(PthreadCondvar {
390+
condvar_ref: CondvarRef::new(),
391+
clock: TimeoutClock::RealTime,
392+
})
411393
},
412394
)
413395
}
@@ -742,11 +724,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
742724
) -> InterpResult<'tcx, Scalar> {
743725
let this = self.eval_context_mut();
744726

745-
let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
746-
if clock_id == this.eval_libc_i32("CLOCK_REALTIME")
747-
|| clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")
748-
{
749-
condattr_set_clock_id(this, attr_op, clock_id)?;
727+
let clock_id = this.read_scalar(clock_id_op)?;
728+
if this.parse_clockid(clock_id).is_some() {
729+
condattr_set_clock_id(this, attr_op, clock_id.to_i32()?)?;
750730
} else {
751731
let einval = this.eval_libc_i32("EINVAL");
752732
return interp_ok(Scalar::from_i32(einval));
@@ -764,7 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
764744

765745
let clock_id = condattr_get_clock_id(this, attr_op)?;
766746
this.write_scalar(
767-
Scalar::from_i32(clock_id),
747+
clock_id,
768748
&this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?,
769749
)?;
770750

@@ -799,13 +779,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
799779
let attr = this.read_pointer(attr_op)?;
800780
// Default clock if `attr` is null, and on macOS where there is no clock attribute.
801781
let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" {
802-
this.eval_libc_i32("CLOCK_REALTIME")
782+
this.eval_libc("CLOCK_REALTIME")
803783
} else {
804784
condattr_get_clock_id(this, attr_op)?
805785
};
806-
let clock_id = condattr_translate_clock_id(this, clock_id)?;
786+
let Some(clock) = this.parse_clockid(clock_id) else {
787+
// This is UB since this situation cannot arise when using pthread_condattr_setclock.
788+
throw_ub_format!("pthread_cond_init: invalid attributes (unsupported clock)")
789+
};
807790

808-
cond_create(this, cond_op, clock_id)?;
791+
cond_create(this, cond_op, clock)?;
809792

810793
interp_ok(())
811794
}
@@ -870,18 +853,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
870853
return interp_ok(());
871854
}
872855
};
873-
let timeout_clock = match data.clock {
874-
ClockId::Realtime => {
875-
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
876-
TimeoutClock::RealTime
877-
}
878-
ClockId::Monotonic => TimeoutClock::Monotonic,
879-
};
856+
if data.clock == TimeoutClock::RealTime {
857+
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
858+
}
880859

881860
this.condvar_wait(
882861
data.condvar_ref,
883862
mutex_ref,
884-
Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
863+
Some((data.clock, TimeoutAnchor::Absolute, duration)),
885864
Scalar::from_i32(0),
886865
this.eval_libc("ETIMEDOUT"), // retval_timeout
887866
dest.clone(),

0 commit comments

Comments
 (0)