1use std::ffi::OsStr;
6use std::{env, process};
7
8#[cfg(any(
9 target_os = "macos",
10 all(
11 not(target_os = "windows"),
12 not(target_os = "ios"),
13 not(target_os = "android"),
14 not(target_env = "ohos"),
15 not(target_arch = "arm"),
16 not(target_arch = "aarch64")
17 )
18))]
19use gaol::profile::{Operation, PathPattern, Profile};
20use ipc_channel::Error;
21use serde::{Deserialize, Serialize};
22use servo_config::opts::Opts;
23use servo_config::prefs::Preferences;
24
25use crate::event_loop::NewScriptEventLoopProcessInfo;
26use crate::process_manager::Process;
27use crate::serviceworker::ServiceWorkerUnprivilegedContent;
28
29#[derive(Deserialize, Serialize)]
30#[allow(clippy::large_enum_variant)]
31pub enum UnprivilegedContent {
32 ScriptEventLoop(NewScriptEventLoopProcessInfo),
33 ServiceWorker(ServiceWorkerUnprivilegedContent),
34}
35
36impl UnprivilegedContent {
37 pub fn opts(&self) -> Opts {
38 match self {
39 UnprivilegedContent::ScriptEventLoop(content) => content.opts.clone(),
40 UnprivilegedContent::ServiceWorker(content) => content.opts.clone(),
41 }
42 }
43
44 pub fn prefs(&self) -> &Preferences {
45 match self {
46 UnprivilegedContent::ScriptEventLoop(content) => &content.prefs,
47 UnprivilegedContent::ServiceWorker(content) => &content.prefs,
48 }
49 }
50}
51
52#[cfg(target_os = "macos")]
54pub fn content_process_sandbox_profile() -> Profile {
55 use std::path::PathBuf;
56
57 use embedder_traits::resources;
58 use gaol::platform;
59
60 let mut operations = vec![
61 Operation::FileReadAll(PathPattern::Literal(PathBuf::from("/dev/urandom"))),
62 Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/Library/Fonts"))),
63 Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/System/Library/Fonts"))),
64 Operation::FileReadAll(PathPattern::Subpath(PathBuf::from(
65 "/System/Library/Frameworks/ApplicationServices.framework",
66 ))),
67 Operation::FileReadAll(PathPattern::Subpath(PathBuf::from(
68 "/System/Library/Frameworks/CoreGraphics.framework",
69 ))),
70 Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/"))),
71 Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/Library"))),
72 Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/System"))),
73 Operation::FileReadMetadata(PathPattern::Literal(PathBuf::from("/etc"))),
74 Operation::SystemInfoRead,
75 Operation::PlatformSpecific(platform::macos::Operation::MachLookup(
76 b"com.apple.FontServer".to_vec(),
77 )),
78 ];
79
80 operations.extend(
81 resources::sandbox_access_files()
82 .into_iter()
83 .map(|p| Operation::FileReadAll(PathPattern::Literal(p))),
84 );
85 operations.extend(
86 resources::sandbox_access_files_dirs()
87 .into_iter()
88 .map(|p| Operation::FileReadAll(PathPattern::Subpath(p))),
89 );
90
91 Profile::new(operations).expect("Failed to create sandbox profile!")
92}
93
94#[cfg(all(
96 not(target_os = "macos"),
97 not(target_os = "windows"),
98 not(target_os = "ios"),
99 not(target_os = "android"),
100 not(target_env = "ohos"),
101 not(target_arch = "arm"),
102 not(target_arch = "aarch64")
103))]
104pub fn content_process_sandbox_profile() -> Profile {
105 use std::path::PathBuf;
106
107 use embedder_traits::resources;
108
109 let mut operations = vec![Operation::FileReadAll(PathPattern::Literal(PathBuf::from(
110 "/dev/urandom",
111 )))];
112
113 operations.extend(
114 resources::sandbox_access_files()
115 .into_iter()
116 .map(|p| Operation::FileReadAll(PathPattern::Literal(p))),
117 );
118 operations.extend(
119 resources::sandbox_access_files_dirs()
120 .into_iter()
121 .map(|p| Operation::FileReadAll(PathPattern::Subpath(p))),
122 );
123
124 Profile::new(operations).expect("Failed to create sandbox profile!")
125}
126
127#[cfg(any(
128 target_os = "windows",
129 target_os = "ios",
130 target_os = "android",
131 target_env = "ohos",
132 target_arch = "arm",
133
134 all(target_arch = "aarch64", not(target_os = "macos"))
136))]
137pub fn content_process_sandbox_profile() {
138 log::error!("Sandboxed multiprocess is not supported on this platform.");
139 process::exit(1);
140}
141
142#[cfg(any(
143 target_os = "windows",
144 target_os = "android",
145 target_env = "ohos",
146 target_arch = "arm",
147 target_arch = "aarch64"
148))]
149pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<Process, Error> {
150 use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
151 let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
155 .expect("Failed to create IPC one-shot server.");
156
157 let path_to_self = env::current_exe().expect("Failed to get current executor.");
158 let mut child_process = process::Command::new(path_to_self);
159 setup_common(&mut child_process, token);
160
161 #[allow(clippy::zombie_processes)]
162 let child = child_process
163 .spawn()
164 .expect("Failed to start unsandboxed child process!");
165
166 let (_receiver, sender) = server.accept().expect("Server failed to accept.");
167 sender.send(content)?;
168
169 Ok(Process::Unsandboxed(child))
170}
171
172#[cfg(all(
173 not(target_os = "windows"),
174 not(target_os = "ios"),
175 not(target_os = "android"),
176 not(target_env = "ohos"),
177 not(target_arch = "arm"),
178 not(target_arch = "aarch64")
179))]
180pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<Process, Error> {
181 use gaol::sandbox::{self, Sandbox, SandboxMethods};
182 use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
183
184 #[allow(non_local_definitions)]
187 impl CommandMethods for gaol::sandbox::Command {
188 fn arg<T>(&mut self, arg: T)
189 where
190 T: AsRef<OsStr>,
191 {
192 self.arg(arg);
193 }
194
195 fn env<T, U>(&mut self, key: T, val: U)
196 where
197 T: AsRef<OsStr>,
198 U: AsRef<OsStr>,
199 {
200 self.env(key, val);
201 }
202 }
203
204 let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
208 .expect("Failed to create IPC one-shot server.");
209
210 let process = if content.opts().sandbox {
212 let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
213 setup_common(&mut command, token);
214
215 let profile = content_process_sandbox_profile();
216 Process::Sandboxed(
217 Sandbox::new(profile)
218 .start(&mut command)
219 .expect("Failed to start sandboxed child process!")
220 .pid as u32,
221 )
222 } else {
223 let path_to_self = env::current_exe().expect("Failed to get current executor.");
224 let mut child_process = process::Command::new(path_to_self);
225 setup_common(&mut child_process, token);
226
227 Process::Unsandboxed(
228 child_process
229 .spawn()
230 .expect("Failed to start unsandboxed child process!"),
231 )
232 };
233
234 let (_receiver, sender) = server.accept().expect("Server failed to accept.");
235 sender.send(content)?;
236
237 Ok(process)
238}
239
240#[cfg(target_os = "ios")]
241pub fn spawn_multiprocess(_content: UnprivilegedContent) -> Result<Process, Error> {
242 log::error!("Multiprocess is not supported on iOS.");
243 process::exit(1);
244}
245
246fn setup_common<C: CommandMethods>(command: &mut C, token: String) {
247 C::arg(command, "--content-process");
248 C::arg(command, token);
249
250 if let Ok(value) = env::var("RUST_BACKTRACE") {
251 C::env(command, "RUST_BACKTRACE", value);
252 }
253
254 if let Ok(value) = env::var("RUST_LOG") {
255 C::env(command, "RUST_LOG", value);
256 }
257}
258
259trait CommandMethods {
261 fn arg<T>(&mut self, arg: T)
263 where
264 T: AsRef<OsStr>;
265
266 fn env<T, U>(&mut self, key: T, val: U)
268 where
269 T: AsRef<OsStr>,
270 U: AsRef<OsStr>;
271}
272
273impl CommandMethods for process::Command {
274 fn arg<T>(&mut self, arg: T)
275 where
276 T: AsRef<OsStr>,
277 {
278 self.arg(arg);
279 }
280
281 fn env<T, U>(&mut self, key: T, val: U)
282 where
283 T: AsRef<OsStr>,
284 U: AsRef<OsStr>,
285 {
286 self.env(key, val);
287 }
288}