Skip to content

Commit 160ebaa

Browse files
committed
add OS-specific handling to src/shims/env.rs
1 parent fc8f88e commit 160ebaa

File tree

1 file changed

+46
-9
lines changed

1 file changed

+46
-9
lines changed

src/shims/env.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ pub struct EnvVars<'tcx> {
2323
impl<'tcx> EnvVars<'tcx> {
2424
pub(crate) fn init<'mir>(
2525
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
26-
excluded_env_vars: Vec<String>,
26+
mut excluded_env_vars: Vec<String>,
2727
) -> InterpResult<'tcx> {
28+
if ecx.tcx.sess.target.target.target_os == "windows" {
29+
// Exclude `TERM` var to avoid terminfo trying to open the termcap file.
30+
excluded_env_vars.push("TERM".to_owned());
31+
}
2832
if ecx.machine.communicate {
2933
for (name, value) in env::vars() {
3034
if !excluded_env_vars.contains(&name) {
@@ -49,19 +53,41 @@ fn alloc_env_var_as_target_str<'mir, 'tcx>(
4953
Ok(ecx.alloc_os_str_as_target_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into())?)
5054
}
5155

56+
fn alloc_env_var_as_c_str<'mir, 'tcx>(
57+
name: &OsStr,
58+
value: &OsStr,
59+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
60+
) -> InterpResult<'tcx, Pointer<Tag>> {
61+
let mut name_osstring = name.to_os_string();
62+
name_osstring.push("=");
63+
name_osstring.push(value);
64+
Ok(ecx.alloc_os_str_as_c_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into()))
65+
}
66+
67+
fn alloc_env_var_as_wide_str<'mir, 'tcx>(
68+
name: &OsStr,
69+
value: &OsStr,
70+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
71+
) -> InterpResult<'tcx, Pointer<Tag>> {
72+
let mut name_osstring = name.to_os_string();
73+
name_osstring.push("=");
74+
name_osstring.push(value);
75+
Ok(ecx.alloc_os_str_as_wide_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into()))
76+
}
77+
5278
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
5379
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
5480
fn getenv(&mut self, name_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar<Tag>> {
5581
let this = self.eval_context_mut();
56-
let target_os = this.tcx.sess.target.target.target_os.as_str();
57-
assert!(target_os == "linux" || target_os == "macos", "`{}` is only available for the UNIX target family");
82+
let target_os = &this.tcx.sess.target.target.target_os;
83+
assert!(target_os == "linux" || target_os == "macos", "`getenv` is only available for the UNIX target family");
5884

5985
let name_ptr = this.read_scalar(name_op)?.not_undef()?;
6086
let name = this.read_os_str_from_c_str(name_ptr)?;
6187
Ok(match this.machine.env_vars.map.get(name) {
62-
// The offset is used to strip the "{name}=" part of the string.
6388
Some(var_ptr) => {
64-
Scalar::from(var_ptr.offset(Size::from_bytes(name.len()) + Size::from_bytes(1), this)?)
89+
// The offset is used to strip the "{name}=" part of the string.
90+
Scalar::from(var_ptr.offset(Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), this)?)
6591
}
6692
None => Scalar::ptr_null(&*this.tcx),
6793
})
@@ -73,32 +99,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
7399
value_op: OpTy<'tcx, Tag>,
74100
) -> InterpResult<'tcx, i32> {
75101
let mut this = self.eval_context_mut();
102+
let target_os = &this.tcx.sess.target.target.target_os;
103+
assert!(target_os == "linux" || target_os == "macos", "`setenv` is only available for the UNIX target family");
76104

77105
let name_ptr = this.read_scalar(name_op)?.not_undef()?;
78106
let value_ptr = this.read_scalar(value_op)?.not_undef()?;
79-
let value = this.read_os_str_from_target_str(value_ptr)?;
107+
80108
let mut new = None;
81109
if !this.is_null(name_ptr)? {
82-
let name = this.read_os_str_from_target_str(name_ptr)?;
110+
let name = this.read_os_str_from_c_str(name_ptr)?;
83111
if !name.is_empty() && !name.to_string_lossy().contains('=') {
112+
let value = this.read_os_str_from_c_str(value_ptr)?;
84113
new = Some((name.to_owned(), value.to_owned()));
85114
}
86115
}
87116
if let Some((name, value)) = new {
88-
let var_ptr = alloc_env_var_as_target_str(&name, &value, &mut this)?;
117+
let var_ptr = alloc_env_var_as_c_str(&name, &value, &mut this)?;
89118
if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) {
90119
this.memory
91120
.deallocate(var, None, MiriMemoryKind::Machine.into())?;
92121
}
93122
this.update_environ()?;
94-
Ok(0)
123+
Ok(0) // return zero on success
95124
} else {
125+
// name argument is a null pointer, points to an empty string, or points to a string containing an '=' character.
126+
let einval = this.eval_libc("EINVAL")?;
127+
this.set_last_error(einval)?;
96128
Ok(-1)
97129
}
98130
}
99131

100132
fn unsetenv(&mut self, name_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
101133
let this = self.eval_context_mut();
134+
let target_os = &this.tcx.sess.target.target.target_os;
135+
assert!(target_os == "linux" || target_os == "macos", "`unsetenv` is only available for the UNIX target family");
102136

103137
let name_ptr = this.read_scalar(name_op)?.not_undef()?;
104138
let mut success = None;
@@ -116,6 +150,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
116150
this.update_environ()?;
117151
Ok(0)
118152
} else {
153+
// name argument is a null pointer, points to an empty string, or points to a string containing an '=' character.
154+
let einval = this.eval_libc("EINVAL")?;
155+
this.set_last_error(einval)?;
119156
Ok(-1)
120157
}
121158
}

0 commit comments

Comments
 (0)