miri/shims/
env.rs

1use std::ffi::{OsStr, OsString};
2
3use rustc_data_structures::fx::FxHashMap;
4use rustc_target::spec::Os;
5
6use self::shims::unix::UnixEnvVars;
7use self::shims::windows::WindowsEnvVars;
8use crate::*;
9
10#[derive(Default)]
11pub enum EnvVars<'tcx> {
12    #[default]
13    Uninit,
14    Unix(UnixEnvVars<'tcx>),
15    Windows(WindowsEnvVars),
16}
17
18impl VisitProvenance for EnvVars<'_> {
19    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
20        match self {
21            EnvVars::Uninit => {}
22            EnvVars::Unix(env) => env.visit_provenance(visit),
23            EnvVars::Windows(env) => env.visit_provenance(visit),
24        }
25    }
26}
27
28impl<'tcx> EnvVars<'tcx> {
29    pub(crate) fn init(
30        ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
31        config: &MiriConfig,
32    ) -> InterpResult<'tcx> {
33        // Initialize the `env_vars` map.
34        // Skip the loop entirely if we don't want to forward anything.
35        let mut env_vars = FxHashMap::default();
36        if ecx.machine.communicate() || !config.forwarded_env_vars.is_empty() {
37            for (name, value) in &config.env {
38                let forward = ecx.machine.communicate()
39                    || config.forwarded_env_vars.iter().any(|v| **v == *name);
40                if forward {
41                    env_vars.insert(OsString::from(name), OsString::from(value));
42                }
43            }
44        }
45
46        for (name, value) in &config.set_env_vars {
47            env_vars.insert(OsString::from(name), OsString::from(value));
48        }
49
50        let env_vars = if ecx.target_os_is_unix() {
51            EnvVars::Unix(UnixEnvVars::new(ecx, env_vars)?)
52        } else if ecx.tcx.sess.target.os == Os::Windows {
53            EnvVars::Windows(WindowsEnvVars::new(ecx, env_vars)?)
54        } else {
55            // For "none" targets (i.e., without an OS).
56            EnvVars::Uninit
57        };
58        ecx.machine.env_vars = env_vars;
59
60        interp_ok(())
61    }
62
63    pub(crate) fn unix(&self) -> &UnixEnvVars<'tcx> {
64        match self {
65            EnvVars::Unix(env) => env,
66            _ => unreachable!(),
67        }
68    }
69
70    pub(crate) fn unix_mut(&mut self) -> &mut UnixEnvVars<'tcx> {
71        match self {
72            EnvVars::Unix(env) => env,
73            _ => unreachable!(),
74        }
75    }
76
77    pub(crate) fn windows(&self) -> &WindowsEnvVars {
78        match self {
79            EnvVars::Windows(env) => env,
80            _ => unreachable!(),
81        }
82    }
83
84    pub(crate) fn windows_mut(&mut self) -> &mut WindowsEnvVars {
85        match self {
86            EnvVars::Windows(env) => env,
87            _ => unreachable!(),
88        }
89    }
90}
91
92impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
93pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
94    /// Try to get an environment variable from the interpreted program's environment. This is
95    /// useful for implementing shims which are documented to read from the environment.
96    fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> {
97        let this = self.eval_context_ref();
98        match &this.machine.env_vars {
99            EnvVars::Uninit => interp_ok(None),
100            EnvVars::Unix(vars) => vars.get(this, name),
101            EnvVars::Windows(vars) => vars.get(name),
102        }
103    }
104
105    /// Get the process identifier.
106    fn get_pid(&self) -> u32 {
107        let this = self.eval_context_ref();
108        if this.machine.communicate() { std::process::id() } else { 1000 }
109    }
110
111    /// Get an "OS" thread ID for the current thread.
112    fn get_current_tid(&self) -> u32 {
113        let this = self.eval_context_ref();
114        self.get_tid(this.machine.threads.active_thread())
115    }
116
117    /// Get an "OS" thread ID for any thread.
118    fn get_tid(&self, thread: ThreadId) -> u32 {
119        let this = self.eval_context_ref();
120        let index = thread.to_u32();
121        let target_os = &this.tcx.sess.target.os;
122        if matches!(target_os, Os::Linux | Os::NetBsd) {
123            // On Linux, the main thread has PID == TID so we uphold this. NetBSD also appears
124            // to exhibit the same behavior, though I can't find a citation.
125            this.get_pid().strict_add(index)
126        } else {
127            // Other platforms do not display any relationship between PID and TID.
128            index
129        }
130    }
131}