Skip to content

Commit e4b2973

Browse files
committed
Use the interpreted program's TZ variable in localtime_r
1 parent f26bd28 commit e4b2973

File tree

6 files changed

+141
-19
lines changed

6 files changed

+141
-19
lines changed

Cargo.lock

Lines changed: 81 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ aes = { version = "0.8.3", features = ["hazmat"] }
2525
measureme = "11"
2626
ctrlc = "3.2.5"
2727
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
28+
chrono-tz = "0.4"
2829
directories = "5"
2930

3031
# Copied from `compiler/rustc/Cargo.toml`.

src/shims/env.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::ffi::OsString;
1+
use std::ffi::{OsStr, OsString};
22

33
use rustc_data_structures::fx::FxHashMap;
44

@@ -99,4 +99,15 @@ impl<'tcx> EnvVars<'tcx> {
9999
}
100100

101101
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
102-
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {}
102+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
103+
/// Try to get an environment variable from the interpreted program's environment. This is
104+
/// useful for implementing shims which are documented to read from the environment.
105+
fn get_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> {
106+
let this = self.eval_context_ref();
107+
match &this.machine.env_vars {
108+
EnvVars::Uninit => return Ok(None),
109+
EnvVars::Unix(vars) => vars.get(this, name),
110+
EnvVars::Windows(vars) => vars.get(name),
111+
}
112+
}
113+
}

src/shims/time.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use std::ffi::OsString;
1+
use std::ffi::{OsStr, OsString};
22
use std::fmt::Write;
3+
use std::str::FromStr;
34
use std::time::{Duration, SystemTime};
45

5-
use chrono::{DateTime, Datelike, Local, Timelike, Utc};
6+
use chrono::{DateTime, Datelike, Offset, Timelike, Utc};
7+
use chrono_tz::Tz;
68

79
use crate::concurrency::thread::MachineCallback;
810
use crate::*;
@@ -136,8 +138,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
136138
.unwrap();
137139
let dt_utc: DateTime<Utc> =
138140
DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp");
141+
142+
// Figure out what time zone is in use
143+
let tz = this.get_var(OsStr::new("TZ"))?.unwrap_or_else(|| OsString::from("UTC"));
144+
let tz = match tz.into_string() {
145+
Ok(tz) => Tz::from_str(&tz).unwrap_or(Tz::Zulu),
146+
_ => Tz::Zulu,
147+
};
148+
139149
// Convert that to local time, then return the broken-down time value.
140-
let dt: DateTime<Local> = DateTime::from(dt_utc);
150+
let dt: DateTime<Tz> = dt_utc.with_timezone(&tz);
141151

142152
// This value is always set to -1, because there is no way to know if dst is in effect with
143153
// chrono crate yet.
@@ -146,17 +156,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
146156

147157
// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
148158
// This may not be consistent with libc::localtime_r's result.
149-
let offset_in_second = Local::now().offset().local_minus_utc();
150-
let tm_gmtoff = offset_in_second;
159+
let offset_in_seconds = Utc::now().with_timezone(&tz).offset().fix().local_minus_utc();
160+
let tm_gmtoff = offset_in_seconds;
151161
let mut tm_zone = String::new();
152-
if offset_in_second < 0 {
162+
if offset_in_seconds < 0 {
153163
tm_zone.push('-');
154164
} else {
155165
tm_zone.push('+');
156166
}
157-
let offset_hour = offset_in_second.abs() / 3600;
167+
let offset_hour = offset_in_seconds.abs() / 3600;
158168
write!(tm_zone, "{:02}", offset_hour).unwrap();
159-
let offset_min = (offset_in_second.abs() % 3600) / 60;
169+
let offset_min = (offset_in_seconds.abs() % 3600) / 60;
160170
if offset_min != 0 {
161171
write!(tm_zone, "{:02}", offset_min).unwrap();
162172
}

src/shims/unix/env.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,27 @@ impl<'tcx> UnixEnvVars<'tcx> {
7070
pub(crate) fn environ(&self) -> Pointer<Option<Provenance>> {
7171
self.environ.ptr()
7272
}
73+
74+
/// Implementation detail for [`InterpCx::get_var`]. This basically does `getenv`, complete
75+
/// with the reads of the environment, but returns an [`OsString`] instead of a pointer.
76+
pub(crate) fn get<'mir>(
77+
&self,
78+
ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
79+
name: &OsStr,
80+
) -> InterpResult<'tcx, Option<OsString>> {
81+
// We don't care about the value as we have the `map` to keep track of everything,
82+
// but we do want to do this read so it shows up as a data race.
83+
let _vars_ptr = ecx.read_pointer(&self.environ)?;
84+
let Some(var_ptr) = self.map.get(name) else {
85+
return Ok(None);
86+
};
87+
// The offset is used to strip the "{name}=" part of the string.
88+
let var_ptr = var_ptr.offset(
89+
Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()),
90+
ecx,
91+
)?;
92+
ecx.read_os_str_from_c_str(var_ptr).map(|s| Some(s.to_owned()))
93+
}
7394
}
7495

7596
fn alloc_env_var<'mir, 'tcx>(

src/shims/windows/env.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::env;
2-
use std::ffi::OsString;
2+
use std::ffi::{OsStr, OsString};
33
use std::io::ErrorKind;
44

55
use rustc_data_structures::fx::FxHashMap;
@@ -9,7 +9,7 @@ use helpers::windows_check_buffer_size;
99

1010
#[derive(Default)]
1111
pub struct WindowsEnvVars {
12-
/// Stores the environment varialbles.
12+
/// Stores the environment variables.
1313
map: FxHashMap<OsString, OsString>,
1414
}
1515

@@ -26,6 +26,11 @@ impl WindowsEnvVars {
2626
) -> InterpResult<'tcx, Self> {
2727
Ok(Self { map: env_vars })
2828
}
29+
30+
/// Implementation detail for [`InterpCx::get_var`].
31+
pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> {
32+
Ok(self.map.get(name).cloned())
33+
}
2934
}
3035

3136
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}

0 commit comments

Comments
 (0)