1use std::ffi::{OsStr, OsString};
2use std::fmt::Write;
3use std::str::FromStr;
4use std::time::{Duration, SystemTime};
5
6use chrono::{DateTime, Datelike, Offset, Timelike, Utc};
7use chrono_tz::Tz;
8
9use crate::*;
10
11pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
13 time.duration_since(SystemTime::UNIX_EPOCH)
14 .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported"))
15 .into()
16}
17
18impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
19pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
20 fn parse_clockid(&self, clk_id: Scalar) -> Option<TimeoutClock> {
21 let this = self.eval_context_ref();
25
26 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 }
32
33 match this.tcx.sess.target.os.as_ref() {
35 "linux" | "freebsd" | "android" => {
36 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 }
44 }
45 "macos" => {
46 if clk_id == this.eval_libc("CLOCK_UPTIME_RAW") {
50 return Some(TimeoutClock::Monotonic);
51 }
52 }
53 _ => {}
54 }
55
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 }
85 };
86
87 let tv_sec = duration.as_secs();
88 let tv_nsec = duration.subsec_nanos();
89
90 this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &tp)?;
91 this.write_int(0, dest)?;
92
93 interp_ok(())
94 }
95
96 fn gettimeofday(
97 &mut self,
98 tv_op: &OpTy<'tcx>,
99 tz_op: &OpTy<'tcx>,
100 ) -> InterpResult<'tcx, Scalar> {
101 let this = self.eval_context_mut();
102
103 this.assert_target_os_is_unix("gettimeofday");
104 this.check_no_isolation("`gettimeofday`")?;
105
106 let tv = this.deref_pointer_as(tv_op, this.libc_ty_layout("timeval"))?;
107
108 let tz = this.read_pointer(tz_op)?;
110 if !this.ptr_is_null(tz)? {
111 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
112 }
113
114 let duration = system_time_to_duration(&SystemTime::now())?;
115 let tv_sec = duration.as_secs();
116 let tv_usec = duration.subsec_micros();
117
118 this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?;
119
120 interp_ok(Scalar::from_i32(0))
121 }
122
123 fn localtime_r(
127 &mut self,
128 timep: &OpTy<'tcx>,
129 result_op: &OpTy<'tcx>,
130 ) -> InterpResult<'tcx, Pointer> {
131 let this = self.eval_context_mut();
132
133 this.assert_target_os_is_unix("localtime_r");
134 this.check_no_isolation("`localtime_r`")?;
135
136 let time_layout = this.libc_ty_layout("time_t");
137 let timep = this.deref_pointer_as(timep, time_layout)?;
138 let result = this.deref_pointer_as(result_op, this.libc_ty_layout("tm"))?;
139
140 let sec_since_epoch: i64 =
143 this.read_scalar(&timep)?.to_int(time_layout.size)?.try_into().unwrap();
144 let dt_utc: DateTime<Utc> =
145 DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp");
146
147 let tz = this.get_env_var(OsStr::new("TZ"))?.unwrap_or_else(|| OsString::from("UTC"));
149 let tz = match tz.into_string() {
150 Ok(tz) => Tz::from_str(&tz).unwrap_or(Tz::UTC),
151 _ => Tz::UTC,
152 };
153
154 let dt: DateTime<Tz> = dt_utc.with_timezone(&tz);
156
157 let tm_isdst = -1;
161 this.write_int_fields_named(
162 &[
163 ("tm_sec", dt.second().into()),
164 ("tm_min", dt.minute().into()),
165 ("tm_hour", dt.hour().into()),
166 ("tm_mday", dt.day().into()),
167 ("tm_mon", dt.month0().into()),
168 ("tm_year", dt.year().strict_sub(1900).into()),
169 ("tm_wday", dt.weekday().num_days_from_sunday().into()),
170 ("tm_yday", dt.ordinal0().into()),
171 ("tm_isdst", tm_isdst),
172 ],
173 &result,
174 )?;
175
176 if !matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
180 let offset_in_seconds = dt.offset().fix().local_minus_utc();
184 let tm_gmtoff = offset_in_seconds;
185 let mut tm_zone = String::new();
186 if offset_in_seconds < 0 {
187 tm_zone.push('-');
188 } else {
189 tm_zone.push('+');
190 }
191 let offset_hour = offset_in_seconds.abs() / 3600;
192 write!(tm_zone, "{offset_hour:02}").unwrap();
193 let offset_min = (offset_in_seconds.abs() % 3600) / 60;
194 if offset_min != 0 {
195 write!(tm_zone, "{offset_min:02}").unwrap();
196 }
197
198 tm_zone.push('\0');
200
201 let tm_zone_ptr = this.allocate_bytes_dedup(tm_zone.as_bytes())?;
203
204 this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
206 this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?;
207 }
208 interp_ok(result.ptr())
209 }
210 #[allow(non_snake_case, clippy::arithmetic_side_effects)]
211 fn GetSystemTimeAsFileTime(
212 &mut self,
213 shim_name: &str,
214 LPFILETIME_op: &OpTy<'tcx>,
215 ) -> InterpResult<'tcx> {
216 let this = self.eval_context_mut();
217
218 this.assert_target_os("windows", shim_name);
219 this.check_no_isolation(shim_name)?;
220
221 let filetime = this.deref_pointer_as(LPFILETIME_op, this.windows_ty_layout("FILETIME"))?;
222
223 let duration = this.system_time_since_windows_epoch(&SystemTime::now())?;
224 let duration_ticks = this.windows_ticks_for(duration)?;
225
226 let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
227 let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
228 this.write_int_fields(&[dwLowDateTime.into(), dwHighDateTime.into()], &filetime)?;
229
230 interp_ok(())
231 }
232
233 #[allow(non_snake_case)]
234 fn QueryPerformanceCounter(
235 &mut self,
236 lpPerformanceCount_op: &OpTy<'tcx>,
237 ) -> InterpResult<'tcx, Scalar> {
238 let this = self.eval_context_mut();
239
240 this.assert_target_os("windows", "QueryPerformanceCounter");
241
242 let duration =
245 this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch());
246 let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
247 err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
248 })?;
249 this.write_scalar(
250 Scalar::from_i64(qpc),
251 &this.deref_pointer_as(lpPerformanceCount_op, this.machine.layouts.i64)?,
252 )?;
253 interp_ok(Scalar::from_i32(-1)) }
255
256 #[allow(non_snake_case)]
257 fn QueryPerformanceFrequency(
258 &mut self,
259 lpFrequency_op: &OpTy<'tcx>,
260 ) -> InterpResult<'tcx, Scalar> {
261 let this = self.eval_context_mut();
262
263 this.assert_target_os("windows", "QueryPerformanceFrequency");
264
265 this.write_scalar(
271 Scalar::from_i64(1_000_000_000),
272 &this.deref_pointer_as(lpFrequency_op, this.machine.layouts.u64)?,
273 )?;
274 interp_ok(Scalar::from_i32(-1)) }
276
277 #[allow(non_snake_case, clippy::arithmetic_side_effects)]
278 fn system_time_since_windows_epoch(&self, time: &SystemTime) -> InterpResult<'tcx, Duration> {
279 let this = self.eval_context_ref();
280
281 let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
282 let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH");
283 let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;
284
285 interp_ok(system_time_to_duration(time)? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH))
286 }
287
288 #[allow(non_snake_case, clippy::arithmetic_side_effects)]
289 fn windows_ticks_for(&self, duration: Duration) -> InterpResult<'tcx, u64> {
290 let this = self.eval_context_ref();
291
292 let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC");
293 let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
294 let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
295
296 let ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
297 .map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;
298 interp_ok(ticks)
299 }
300
301 fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> {
302 let this = self.eval_context_ref();
303
304 this.assert_target_os("macos", "mach_absolute_time");
305
306 let duration =
309 this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch());
310 let res = u64::try_from(duration.as_nanos()).map_err(|_| {
311 err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
312 })?;
313 interp_ok(Scalar::from_u64(res))
314 }
315
316 fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
317 let this = self.eval_context_mut();
318
319 this.assert_target_os("macos", "mach_timebase_info");
320
321 let info = this.deref_pointer_as(info_op, this.libc_ty_layout("mach_timebase_info"))?;
322
323 let (numerator, denom) = (1, 1);
326 this.write_int_fields(&[numerator.into(), denom.into()], &info)?;
327
328 interp_ok(Scalar::from_i32(0)) }
330
331 fn nanosleep(&mut self, duration: &OpTy<'tcx>, rem: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
332 let this = self.eval_context_mut();
333
334 this.assert_target_os_is_unix("nanosleep");
335
336 let duration = this.deref_pointer_as(duration, this.libc_ty_layout("timespec"))?;
337 let _rem = this.read_pointer(rem)?; let duration = match this.read_timespec(&duration)? {
340 Some(duration) => duration,
341 None => {
342 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
343 }
344 };
345
346 this.block_thread(
347 BlockReason::Sleep,
348 Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)),
349 callback!(
350 @capture<'tcx> {}
351 |_this, unblock: UnblockKind| {
352 assert_eq!(unblock, UnblockKind::TimedOut);
353 interp_ok(())
354 }
355 ),
356 );
357 interp_ok(Scalar::from_i32(0))
358 }
359
360 fn clock_nanosleep(
361 &mut self,
362 clock_id: &OpTy<'tcx>,
363 flags: &OpTy<'tcx>,
364 timespec: &OpTy<'tcx>,
365 rem: &OpTy<'tcx>,
366 ) -> InterpResult<'tcx, Scalar> {
367 let this = self.eval_context_mut();
368 let clockid_t_size = this.libc_ty_layout("clockid_t").size;
369
370 let clock_id = this.read_scalar(clock_id)?.to_int(clockid_t_size)?;
371 let timespec = this.deref_pointer_as(timespec, this.libc_ty_layout("timespec"))?;
372 let flags = this.read_scalar(flags)?.to_i32()?;
373 let _rem = this.read_pointer(rem)?; if clock_id != this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)? {
377 throw_unsup_format!("clock_nanosleep: only CLOCK_MONOTONIC is supported");
378 }
379
380 let duration = match this.read_timespec(×pec)? {
381 Some(duration) => duration,
382 None => {
383 return this.set_last_error_and_return_i32(LibcError("EINVAL"));
384 }
385 };
386
387 let timeout_anchor = if flags == 0 {
388 TimeoutAnchor::Relative
391 } else if flags == this.eval_libc_i32("TIMER_ABSTIME") {
392 TimeoutAnchor::Absolute
395 } else {
396 throw_unsup_format!(
398 "`clock_nanosleep` unsupported flags {flags}, only no flags or \
399 TIMER_ABSTIME is supported"
400 );
401 };
402
403 this.block_thread(
404 BlockReason::Sleep,
405 Some((TimeoutClock::Monotonic, timeout_anchor, duration)),
406 callback!(
407 @capture<'tcx> {}
408 |_this, unblock: UnblockKind| {
409 assert_eq!(unblock, UnblockKind::TimedOut);
410 interp_ok(())
411 }
412 ),
413 );
414 interp_ok(Scalar::from_i32(0))
415 }
416
417 #[allow(non_snake_case)]
418 fn Sleep(&mut self, timeout: &OpTy<'tcx>) -> InterpResult<'tcx> {
419 let this = self.eval_context_mut();
420
421 this.assert_target_os("windows", "Sleep");
422
423 let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
424
425 let duration = Duration::from_millis(timeout_ms.into());
426
427 this.block_thread(
428 BlockReason::Sleep,
429 Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)),
430 callback!(
431 @capture<'tcx> {}
432 |_this, unblock: UnblockKind| {
433 assert_eq!(unblock, UnblockKind::TimedOut);
434 interp_ok(())
435 }
436 ),
437 );
438 interp_ok(())
439 }
440}