isla_axiomatic/
sandbox.rs1use std::ffi::{OsStr, OsString};
37use std::process::{Child, Command, ExitStatus, Output, Stdio};
38
39use isla_lib::config::Tool;
40
41pub struct SandboxedCommand {
42 program: OsString,
43 args: Vec<OsString>,
44 stdin: Option<Stdio>,
45 stdout: Option<Stdio>,
46 stderr: Option<Stdio>,
47}
48
49impl SandboxedCommand {
50 pub fn from_tool(tool: &Tool) -> Self {
51 let mut cmd = Self::new(&tool.executable);
52 cmd.args(&tool.options);
53 cmd
54 }
55
56 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
57 SandboxedCommand {
58 program: program.as_ref().to_os_string(),
59 args: vec![],
60 stdin: None,
61 stdout: None,
62 stderr: None,
63 }
64 }
65
66 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
67 self.args.push(arg.as_ref().to_os_string());
68 self
69 }
70
71 pub fn args<I, S>(&mut self, args: I) -> &mut Self
72 where
73 I: IntoIterator<Item = S>,
74 S: AsRef<OsStr>,
75 {
76 for arg in args.into_iter() {
77 self.args.push(arg.as_ref().to_os_string());
78 }
79 self
80 }
81
82 pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
83 self.stdin = Some(cfg.into());
84 self
85 }
86
87 pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
88 self.stdout = Some(cfg.into());
89 self
90 }
91
92 pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
93 self.stderr = Some(cfg.into());
94 self
95 }
96
97 #[cfg(feature = "sandbox")]
98 fn sandbox(&mut self) -> Command {
99 let mut bubblewrap = Command::new("bwrap");
100
101 let sandbox_lib = std::env::var("ISLA_SANDBOX").expect("No ISLA_SANDBOX in environment");
102
103 bubblewrap.args(&[OsStr::new("--ro-bind"), &self.program, &self.program]);
104 bubblewrap.args(&["--bind", "/tmp/isla", "/tmp/isla"]);
105 bubblewrap.args(&["--ro-bind", &sandbox_lib, "/lib"]);
106 bubblewrap.args(&["--symlink", "/lib", "/lib64"]);
107 bubblewrap.args(&["--symlink", "/lib", "/usr/lib64"]);
108 bubblewrap.args(&["--symlink", "/lib", "/usr/lib"]);
109 bubblewrap.arg("--unshare-all");
110 bubblewrap.arg("--");
111
112 bubblewrap.arg(&self.program);
113 bubblewrap.args(&self.args);
114
115 if let Some(stdin) = self.stdin.take() {
116 bubblewrap.stdin(stdin);
117 }
118 if let Some(stdout) = self.stdout.take() {
119 bubblewrap.stdout(stdout);
120 }
121 if let Some(stderr) = self.stderr.take() {
122 bubblewrap.stderr(stderr);
123 }
124
125 bubblewrap
126 }
127
128 #[cfg(not(feature = "sandbox"))]
129 fn sandbox(&mut self) -> Command {
130 let mut command = Command::new(&self.program);
131 command.args(&self.args);
132
133 if let Some(stdin) = self.stdin.take() {
134 command.stdin(stdin);
135 }
136 if let Some(stdout) = self.stdout.take() {
137 command.stdout(stdout);
138 }
139 if let Some(stderr) = self.stderr.take() {
140 command.stderr(stderr);
141 }
142
143 command
144 }
145
146 pub fn output(&mut self) -> std::io::Result<Output> {
147 self.sandbox().output()
148 }
149
150 pub fn spawn(&mut self) -> std::io::Result<Child> {
151 self.sandbox().spawn()
152 }
153
154 pub fn status(&mut self) -> std::io::Result<ExitStatus> {
155 self.sandbox().status()
156 }
157}