Skip to content

Commit b67dbca

Browse files
committed
Auto merge of #1271 - RalfJung:env-clean, r=RalfJung
env shim: make sure we clean up all the memory we allocate `Machine` memory is not leak-checked, so if we forgot to deallocate part of the env shim memory, we wouldn't even notice. Thus add a dedicated memory kind that is leak-checked.
2 parents f274111 + 204c13b commit b67dbca

File tree

5 files changed

+42
-14
lines changed

5 files changed

+42
-14
lines changed

src/eval.rs

+5
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,21 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
198198

199199
// Perform the main execution.
200200
let res: InterpResult<'_, i64> = (|| {
201+
// Main loop.
201202
while ecx.step()? {
202203
ecx.process_diagnostics();
203204
}
204205
// Read the return code pointer *before* we run TLS destructors, to assert
205206
// that it was written to by the time that `start` lang item returned.
206207
let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
208+
// Global destructors.
207209
ecx.run_tls_dtors()?;
208210
Ok(return_code)
209211
})();
210212

213+
// Machine cleanup.
214+
EnvVars::cleanup(&mut ecx).unwrap();
215+
211216
// Process the result.
212217
match res {
213218
Ok(return_code) => {

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#![feature(option_expect_none, option_unwrap_none)]
33
#![feature(map_first_last)]
44
#![feature(never_type)]
5+
#![feature(or_patterns)]
6+
57
#![warn(rust_2018_idioms)]
68
#![allow(clippy::cast_lossless)]
79

src/machine.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,13 @@ pub enum MiriMemoryKind {
4848
C,
4949
/// Windows `HeapAlloc` memory.
5050
WinHeap,
51-
/// Memory for env vars and args, errno, extern statics and other parts of the machine-managed environment.
51+
/// Memory for args, errno, extern statics and other parts of the machine-managed environment.
52+
/// This memory may leak.
5253
Machine,
54+
/// Memory for env vars. Separate from `Machine` because we clean it up and leak-check it.
55+
Env,
5356
/// Globals copied from `tcx`.
57+
/// This memory may leak.
5458
Global,
5559
}
5660

@@ -498,7 +502,7 @@ impl MayLeak for MiriMemoryKind {
498502
fn may_leak(self) -> bool {
499503
use self::MiriMemoryKind::*;
500504
match self {
501-
Rust | C | WinHeap => false,
505+
Rust | C | WinHeap | Env => false,
502506
Machine | Global => true,
503507
}
504508
}

src/shims/env.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ impl<'tcx> EnvVars<'tcx> {
4646
}
4747
ecx.update_environ()
4848
}
49+
50+
pub(crate) fn cleanup<'mir>(
51+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
52+
) -> InterpResult<'tcx> {
53+
// Deallocate individual env vars.
54+
for (_name, ptr) in ecx.machine.env_vars.map.drain() {
55+
ecx.memory.deallocate(ptr, None, MiriMemoryKind::Env.into())?;
56+
}
57+
// Deallocate environ var list.
58+
let environ = ecx.machine.env_vars.environ.take().unwrap();
59+
let old_vars_ptr = ecx.read_scalar(environ.into())?.not_undef()?;
60+
ecx.memory.deallocate(ecx.force_ptr(old_vars_ptr)?, None, MiriMemoryKind::Env.into())?;
61+
Ok(())
62+
}
4963
}
5064

5165
fn alloc_env_var_as_c_str<'mir, 'tcx>(
@@ -56,7 +70,7 @@ fn alloc_env_var_as_c_str<'mir, 'tcx>(
5670
let mut name_osstring = name.to_os_string();
5771
name_osstring.push("=");
5872
name_osstring.push(value);
59-
Ok(ecx.alloc_os_str_as_c_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into()))
73+
Ok(ecx.alloc_os_str_as_c_str(name_osstring.as_os_str(), MiriMemoryKind::Env.into()))
6074
}
6175

6276
fn alloc_env_var_as_wide_str<'mir, 'tcx>(
@@ -67,7 +81,7 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>(
6781
let mut name_osstring = name.to_os_string();
6882
name_osstring.push("=");
6983
name_osstring.push(value);
70-
Ok(ecx.alloc_os_str_as_wide_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into()))
84+
Ok(ecx.alloc_os_str_as_wide_str(name_osstring.as_os_str(), MiriMemoryKind::Env.into()))
7185
}
7286

7387
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
@@ -146,7 +160,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
146160
}
147161
// Allocate environment block & Store environment variables to environment block.
148162
// Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`.
149-
// FIXME: MemoryKind should be `Machine`, blocked on https://github.com/rust-lang/rust/pull/70479.
163+
// FIXME: MemoryKind should be `Env`, blocked on https://github.com/rust-lang/rust/pull/70479.
150164
let envblock_ptr = this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::WinHeap.into());
151165
// If the function succeeds, the return value is a pointer to the environment block of the current process.
152166
Ok(envblock_ptr.into())
@@ -158,7 +172,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
158172
this.assert_target_os("windows", "FreeEnvironmentStringsW");
159173

160174
let env_block_ptr = this.read_scalar(env_block_op)?.not_undef()?;
161-
// FIXME: MemoryKind should be `Machine`, blocked on https://github.com/rust-lang/rust/pull/70479.
175+
// FIXME: MemoryKind should be `Env`, blocked on https://github.com/rust-lang/rust/pull/70479.
162176
let result = this.memory.deallocate(this.force_ptr(env_block_ptr)?, None, MiriMemoryKind::WinHeap.into());
163177
// If the function succeeds, the return value is nonzero.
164178
Ok(result.is_ok() as i32)
@@ -188,7 +202,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
188202
let var_ptr = alloc_env_var_as_c_str(&name, &value, &mut this)?;
189203
if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) {
190204
this.memory
191-
.deallocate(var, None, MiriMemoryKind::Machine.into())?;
205+
.deallocate(var, None, MiriMemoryKind::Env.into())?;
192206
}
193207
this.update_environ()?;
194208
Ok(0) // return zero on success
@@ -225,7 +239,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
225239
} else if this.is_null(value_ptr)? {
226240
// Delete environment variable `{name}`
227241
if let Some(var) = this.machine.env_vars.map.remove(&name) {
228-
this.memory.deallocate(var, None, MiriMemoryKind::Machine.into())?;
242+
this.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
229243
this.update_environ()?;
230244
}
231245
Ok(1) // return non-zero on success
@@ -234,7 +248,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
234248
let var_ptr = alloc_env_var_as_wide_str(&name, &value, &mut this)?;
235249
if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) {
236250
this.memory
237-
.deallocate(var, None, MiriMemoryKind::Machine.into())?;
251+
.deallocate(var, None, MiriMemoryKind::Env.into())?;
238252
}
239253
this.update_environ()?;
240254
Ok(1) // return non-zero on success
@@ -257,7 +271,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
257271
if let Some(old) = success {
258272
if let Some(var) = old {
259273
this.memory
260-
.deallocate(var, None, MiriMemoryKind::Machine.into())?;
274+
.deallocate(var, None, MiriMemoryKind::Env.into())?;
261275
}
262276
this.update_environ()?;
263277
Ok(0)
@@ -314,12 +328,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
314328
/// The first time it gets called, also initializes `extra.environ`.
315329
fn update_environ(&mut self) -> InterpResult<'tcx> {
316330
let this = self.eval_context_mut();
317-
// Deallocate the old environ value, if any.
331+
// Deallocate the old environ list, if any.
318332
if let Some(environ) = this.machine.env_vars.environ {
319333
let old_vars_ptr = this.read_scalar(environ.into())?.not_undef()?;
320-
this.memory.deallocate(this.force_ptr(old_vars_ptr)?, None, MiriMemoryKind::Machine.into())?;
334+
this.memory.deallocate(this.force_ptr(old_vars_ptr)?, None, MiriMemoryKind::Env.into())?;
321335
} else {
322336
// No `environ` allocated yet, let's do that.
337+
// This is memory backing an extern static, hence `Machine`, not `Env`.
323338
let layout = this.layout_of(this.tcx.types.usize)?;
324339
let place = this.allocate(layout, MiriMemoryKind::Machine.into());
325340
this.write_scalar(Scalar::from_machine_usize(0, &*this.tcx), place.into())?;
@@ -334,7 +349,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
334349
let tcx = this.tcx;
335350
let vars_layout =
336351
this.layout_of(tcx.mk_array(tcx.types.usize, u64::try_from(vars.len()).unwrap()))?;
337-
let vars_place = this.allocate(vars_layout, MiriMemoryKind::Machine.into());
352+
let vars_place = this.allocate(vars_layout, MiriMemoryKind::Env.into());
338353
for (idx, var) in vars.into_iter().enumerate() {
339354
let place = this.mplace_field(vars_place, idx)?;
340355
this.write_scalar(var, place.into())?;

src/stacked_borrows.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,10 @@ impl Stacks {
460460
// Global memory can be referenced by global pointers from `tcx`.
461461
// Thus we call `global_base_ptr` such that the global pointers get the same tag
462462
// as what we use here.
463+
// `Machine` is used for extern statics, and thus must also be listed here.
464+
// `Env` we list because we can get away with precise tracking there.
463465
// The base pointer is not unique, so the base permission is `SharedReadWrite`.
464-
MemoryKind::Machine(MiriMemoryKind::Global) | MemoryKind::Machine(MiriMemoryKind::Machine) =>
466+
MemoryKind::Machine(MiriMemoryKind::Global | MiriMemoryKind::Machine | MiriMemoryKind::Env) =>
465467
(extra.borrow_mut().global_base_ptr(id), Permission::SharedReadWrite),
466468
// Everything else we handle entirely untagged for now.
467469
// FIXME: experiment with more precise tracking.

0 commit comments

Comments
 (0)