]> git.lizzy.rs Git - rust.git/blob - src/shims/env.rs
8f946a2b5eb10a9538c193723de0c053af06c8d1
[rust.git] / src / shims / env.rs
1 use std::collections::HashMap;
2
3 use rustc::ty::layout::{Size, Align};
4 use rustc_mir::interpret::{Pointer, Memory};
5 use crate::stacked_borrows::Tag;
6 use crate::*;
7
8 #[derive(Default)]
9 pub struct EnvVars {
10     /// Stores pointers to the environment variables. These variables must be stored as
11     /// null-terminated C strings with the `"{name}={value}"` format.
12     map: HashMap<Vec<u8>, Pointer<Tag>>,
13 }
14
15 impl EnvVars {
16     pub(crate) fn init<'mir, 'tcx>(
17         ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
18     ) {
19         if ecx.machine.communicate {
20             for (name, value) in std::env::vars() {
21                 let var_ptr = alloc_env_var(name.as_bytes(), value.as_bytes(), ecx.memory_mut());
22                 ecx.machine.env_vars.map.insert(name.into_bytes(), var_ptr);
23             }
24         }
25     }
26 }
27
28 fn alloc_env_var<'mir, 'tcx>(
29     name: &[u8],
30     value: &[u8],
31     memory: &mut Memory<'mir, 'tcx, Evaluator<'tcx>>,
32 ) -> Pointer<Tag> {
33     let bytes = [name, b"=", value].concat();
34     let tcx = {memory.tcx.tcx};
35     let length = bytes.len() as u64;
36     // `+1` for the null terminator.
37     let ptr = memory.allocate(
38         Size::from_bytes(length + 1),
39         Align::from_bytes(1).unwrap(),
40         MiriMemoryKind::Env.into(),
41     );
42     // We just allocated these, so the write cannot fail.
43     let alloc = memory.get_mut(ptr.alloc_id).unwrap();
44     alloc.write_bytes(&tcx, ptr, &bytes).unwrap();
45     let trailing_zero_ptr = ptr.offset(
46         Size::from_bytes(length),
47         &tcx,
48     ).unwrap();
49     alloc.write_bytes(&tcx, trailing_zero_ptr, &[0]).unwrap();
50     ptr
51 }
52
53 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
54 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
55     fn getenv(
56         &mut self,
57         name_op: OpTy<'tcx, Tag>,
58     ) -> InterpResult<'tcx, Scalar<Tag>> {
59         let this = self.eval_context_mut();
60
61         let name_ptr = this.read_scalar(name_op)?.not_undef()?;
62         let name = this.memory().read_c_str(name_ptr)?;
63         Ok(match this.machine.env_vars.map.get(name) {
64             // The offset is used to strip the "{name}=" part of the string.
65             Some(var_ptr) => Scalar::Ptr(var_ptr.offset(Size::from_bytes(name.len() as u64 + 1), this)?),
66             None => Scalar::ptr_null(&*this.tcx),
67         })
68     }
69
70     fn setenv(
71         &mut self,
72         name_op: OpTy<'tcx, Tag>,
73         value_op: OpTy<'tcx, Tag>,
74     ) -> InterpResult<'tcx, i32> {
75         let this = self.eval_context_mut();
76
77         let name_ptr = this.read_scalar(name_op)?.not_undef()?;
78         let value_ptr = this.read_scalar(value_op)?.not_undef()?;
79         let value = this.memory().read_c_str(value_ptr)?;
80         let mut new = None;
81         if !this.is_null(name_ptr)? {
82             let name = this.memory().read_c_str(name_ptr)?;
83             if !name.is_empty() && !name.contains(&b'=') {
84                 new = Some((name.to_owned(), value.to_owned()));
85             }
86         }
87         if let Some((name, value)) = new {
88             let var_ptr = alloc_env_var(&name, &value, this.memory_mut());
89             if let Some(var) = this.machine.env_vars.map.insert(name.to_owned(), var_ptr) {
90                 this.memory_mut().deallocate(var, None, MiriMemoryKind::Env.into())?;
91             }
92             Ok(0)
93         } else {
94             Ok(-1)
95         }
96     }
97
98     fn unsetenv(
99         &mut self,
100         name_op: OpTy<'tcx, Tag>,
101     ) -> InterpResult<'tcx, i32> {
102         let this = self.eval_context_mut();
103
104         let name_ptr = this.read_scalar(name_op)?.not_undef()?;
105         let mut success = None;
106         if !this.is_null(name_ptr)? {
107             let name = this.memory().read_c_str(name_ptr)?.to_owned();
108             if !name.is_empty() && !name.contains(&b'=') {
109                 success = Some(this.machine.env_vars.map.remove(&name));
110             }
111         }
112         if let Some(old) = success {
113             if let Some(var) = old {
114                 this.memory_mut().deallocate(var, None, MiriMemoryKind::Env.into())?;
115             }
116             Ok(0)
117         } else {
118             Ok(-1)
119         }
120     }
121 }