Skip to main content

isla_axiomatic/
sandbox.rs

1// BSD 2-Clause License
2//
3// Copyright (c) 2020 Alasdair Armstrong
4//
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//
14// 2. Redistributions in binary form must reproduce the above copyright
15// notice, this list of conditions and the following disclaimer in the
16// documentation and/or other materials provided with the distribution.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30//! For running isla-axiomatic via the web interface, we support
31//! sandboxing the various assembler and linker commands used when
32//! building litmus tests. This is done using the
33//! [bubblewrap](https://github.com/containers/bubblewrap) tool, and
34//! controlled using the `sandbox` cargo feature.
35
36use 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}