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 not(target_arch = "riscv32"),
18 not(target_arch = "riscv64")
19 )
20))]
21use gaol::profile::{Operation, PathPattern, Profile};
22use serde::{Deserialize, Serialize};
23use servo_config::opts::Opts;
24use servo_config::prefs::Preferences;
25
26use crate::event_loop::NewScriptEventLoopProcessInfo;
27use crate::serviceworker::ServiceWorkerUnprivilegedContent;
28
29#[derive(Deserialize, Serialize)]
30#[expect(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 not(target_arch = "riscv32"),
104 not(target_arch = "riscv64")
105))]
106pub fn content_process_sandbox_profile() -> Profile {
107 use std::path::PathBuf;
108
109 use embedder_traits::resources;
110
111 let mut operations = vec![Operation::FileReadAll(PathPattern::Literal(PathBuf::from(
112 "/dev/urandom",
113 )))];
114
115 operations.extend(
116 resources::sandbox_access_files()
117 .into_iter()
118 .map(|p| Operation::FileReadAll(PathPattern::Literal(p))),
119 );
120 operations.extend(
121 resources::sandbox_access_files_dirs()
122 .into_iter()
123 .map(|p| Operation::FileReadAll(PathPattern::Subpath(p))),
124 );
125
126 Profile::new(operations).expect("Failed to create sandbox profile!")
127}
128
129#[cfg(any(
130 target_os = "windows",
131 target_os = "ios",
132 target_os = "android",
133 target_env = "ohos",
134 target_arch = "arm",
135 target_arch = "riscv32",
136 target_arch = "riscv64",
137
138 all(target_arch = "aarch64", not(target_os = "macos"))
140))]
141pub fn content_process_sandbox_profile() {
142 log::error!("Sandboxed multiprocess is not supported on this platform.");
143 process::exit(1);
144}
145
146#[cfg(any(
147 target_os = "windows",
148 target_os = "android",
149 target_env = "ohos",
150 target_arch = "arm",
151 target_arch = "aarch64",
152 target_arch = "riscv32",
153 target_arch = "riscv64"
154))]
155pub fn spawn_multiprocess(
156 content: UnprivilegedContent,
157) -> Result<crate::process_manager::Process, ipc_channel::IpcError> {
158 use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
159 let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
163 .expect("Failed to create IPC one-shot server.");
164
165 let path_to_self = env::current_exe().expect("Failed to get current executor.");
166 let mut child_process = process::Command::new(path_to_self);
167 setup_common(&mut child_process, token);
168
169 #[allow(clippy::zombie_processes)]
170 let child = child_process
171 .spawn()
172 .expect("Failed to start unsandboxed child process!");
173
174 let (_receiver, sender) = server.accept().expect("Server failed to accept.");
175 sender.send(content)?;
176
177 Ok(crate::process_manager::Process::Unsandboxed(child))
178}
179
180#[cfg(all(
181 not(target_os = "windows"),
182 not(target_os = "ios"),
183 not(target_os = "android"),
184 not(target_env = "ohos"),
185 not(target_arch = "arm"),
186 not(target_arch = "aarch64"),
187 not(target_arch = "riscv32"),
188 not(target_arch = "riscv64")
189))]
190pub fn spawn_multiprocess(
191 content: UnprivilegedContent,
192) -> Result<crate::process_manager::Process, ipc_channel::IpcError> {
193 use gaol::sandbox::{self, Sandbox, SandboxMethods};
194 use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
195
196 #[allow(non_local_definitions)]
199 impl CommandMethods for gaol::sandbox::Command {
200 fn arg<T>(&mut self, arg: T)
201 where
202 T: AsRef<OsStr>,
203 {
204 self.arg(arg);
205 }
206
207 fn env<T, U>(&mut self, key: T, val: U)
208 where
209 T: AsRef<OsStr>,
210 U: AsRef<OsStr>,
211 {
212 self.env(key, val);
213 }
214 }
215
216 let (server, token) = IpcOneShotServer::<IpcSender<UnprivilegedContent>>::new()
220 .expect("Failed to create IPC one-shot server.");
221
222 let process = if content.opts().sandbox {
224 let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
225 setup_common(&mut command, token);
226
227 let profile = content_process_sandbox_profile();
228 crate::process_manager::Process::Sandboxed(
229 Sandbox::new(profile)
230 .start(&mut command)
231 .expect("Failed to start sandboxed child process!")
232 .pid as u32,
233 )
234 } else {
235 let path_to_self = env::current_exe().expect("Failed to get current executor.");
236 let mut child_process = process::Command::new(path_to_self);
237 setup_common(&mut child_process, token);
238
239 crate::process_manager::Process::Unsandboxed(
240 child_process
241 .spawn()
242 .expect("Failed to start unsandboxed child process!"),
243 )
244 };
245
246 let (_receiver, sender) = server.accept().expect("Server failed to accept.");
247 sender.send(content)?;
248
249 Ok(process)
250}
251
252#[cfg(target_os = "ios")]
253pub fn spawn_multiprocess(_content: UnprivilegedContent) -> Result<Process, Error> {
254 log::error!("Multiprocess is not supported on iOS.");
255 process::exit(1);
256}
257
258fn setup_common<C: CommandMethods>(command: &mut C, token: String) {
259 C::arg(command, "--content-process");
260 C::arg(command, token);
261
262 if let Ok(value) = env::var("RUST_BACKTRACE") {
263 C::env(command, "RUST_BACKTRACE", value);
264 }
265
266 if let Ok(value) = env::var("RUST_LOG") {
267 C::env(command, "RUST_LOG", value);
268 }
269}
270
271trait CommandMethods {
273 fn arg<T>(&mut self, arg: T)
275 where
276 T: AsRef<OsStr>;
277
278 fn env<T, U>(&mut self, key: T, val: U)
280 where
281 T: AsRef<OsStr>,
282 U: AsRef<OsStr>;
283}
284
285impl CommandMethods for process::Command {
286 fn arg<T>(&mut self, arg: T)
287 where
288 T: AsRef<OsStr>,
289 {
290 self.arg(arg);
291 }
292
293 fn env<T, U>(&mut self, key: T, val: U)
294 where
295 T: AsRef<OsStr>,
296 U: AsRef<OsStr>,
297 {
298 self.env(key, val);
299 }
300}