Skip to main content

miri/shims/
env.rs

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