1#[path = "exec/shim/src/layout.rs"]
18#[allow(dead_code)]
19pub mod layout;
20
21use crate::sys::errno;
22
23#[derive(Debug)]
25pub struct Mailbox {
26 pub fd: i32,
28}
29
30impl Mailbox {
31 pub fn create() -> Result<Mailbox, String> {
40 let fd = unsafe {
41 libc::syscall(
42 libc::SYS_memfd_create,
43 c"proot-mailbox".as_ptr(),
44 libc::MFD_CLOEXEC,
45 )
46 };
47 if fd < 0 {
48 return Err(format!("memfd_create: {}", crate::sys::strerror(errno())));
49 }
50 let fd = fd as i32;
51 if unsafe { libc::ftruncate(fd, layout::MAIL_SIZE as libc::off_t) } < 0 {
52 let e = errno();
53 unsafe { libc::close(fd) };
54 return Err(format!("sizing the mailbox: {}", crate::sys::strerror(e)));
55 }
56 let local = unsafe {
57 libc::mmap(
58 std::ptr::null_mut(),
59 layout::MAIL_SIZE,
60 libc::PROT_READ | libc::PROT_WRITE,
61 libc::MAP_SHARED,
62 fd,
63 0,
64 )
65 };
66 if local == libc::MAP_FAILED {
67 let e = errno();
68 unsafe { libc::close(fd) };
69 return Err(format!("mapping the mailbox: {}", crate::sys::strerror(e)));
70 }
71 crate::tracee::mem::set_window(
72 &[layout::MAIL64, layout::MAIL32],
73 layout::MAIL_SIZE,
74 local as *mut u8,
75 );
76 Ok(Mailbox { fd })
77 }
78}
79
80pub unsafe fn exec_trampoline(
94 mail: i32,
95 shim: *const libc::c_char,
96 argv: *const *const libc::c_char,
97 envp: *const *const libc::c_char,
98) -> ! {
99 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
100 unsafe {
101 libc::fcntl(mail, libc::F_SETFD, 0);
102 if map_gates() && exec_through_gate(shim, argv, envp) == -(libc::ENOENT as i64) {
103 libc::_exit(127);
104 }
105 }
106 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
107 let _ = (mail, shim, argv, envp);
108 unsafe { libc::_exit(126) }
109}
110
111#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
117fn map_gates() -> bool {
118 unsafe {
119 let page = libc::mmap(
120 layout::GATE_ADDR as *mut libc::c_void,
121 layout::PAGE,
122 libc::PROT_READ | libc::PROT_WRITE,
123 libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED_NOREPLACE,
124 -1,
125 0,
126 );
127 if page as usize != layout::GATE_ADDR {
128 return false;
129 }
130 for at in [layout::ALLOW_GATE, layout::NOTIFY_GATE] {
131 std::ptr::copy_nonoverlapping(layout::GATE_CODE.as_ptr(), at as *mut u8, layout::GATE_CODE.len());
132 }
133 #[cfg(target_arch = "aarch64")]
134 sync_code(layout::GATE_ADDR, layout::PAGE);
135 libc::mprotect(page, layout::PAGE, libc::PROT_READ | libc::PROT_EXEC) == 0
136 }
137}
138
139#[cfg(target_arch = "aarch64")]
147fn sync_code(start: usize, len: usize) {
148 let ctr: usize;
149 unsafe { std::arch::asm!("mrs {}, ctr_el0", out(reg) ctr, options(nomem, nostack)) };
150 let dline = 4 << ((ctr >> 16) & 0xf);
151 let iline = 4 << (ctr & 0xf);
152 let mut a = start & !(dline - 1);
153 while a < start + len {
154 unsafe { std::arch::asm!("dc cvau, {}", in(reg) a, options(nostack)) };
155 a += dline;
156 }
157 unsafe { std::arch::asm!("dsb ish", options(nostack)) };
158 let mut a = start & !(iline - 1);
159 while a < start + len {
160 unsafe { std::arch::asm!("ic ivau, {}", in(reg) a, options(nostack)) };
161 a += iline;
162 }
163 unsafe { std::arch::asm!("dsb ish", "isb", options(nostack)) };
164}
165
166#[cfg(target_arch = "x86_64")]
179unsafe fn exec_through_gate(
180 path: *const libc::c_char,
181 argv: *const *const libc::c_char,
182 envp: *const *const libc::c_char,
183) -> i64 {
184 let ret: i64;
185 unsafe {
186 std::arch::asm!(
187 "lea r12, [rip + 2f]",
188 "jmp {at}",
189 "2:",
190 at = in(reg) layout::ALLOW_GATE,
191 inlateout("rax") libc::SYS_execve => ret,
192 in("rdi") path,
193 in("rsi") argv,
194 in("rdx") envp,
195 out("r12") _,
196 lateout("rcx") _,
197 lateout("r11") _,
198 options(nostack),
199 );
200 }
201 ret
202}
203
204#[cfg(target_arch = "aarch64")]
217unsafe fn exec_through_gate(
218 path: *const libc::c_char,
219 argv: *const *const libc::c_char,
220 envp: *const *const libc::c_char,
221) -> i64 {
222 let ret: i64;
223 unsafe {
224 std::arch::asm!(
225 "adr x16, 2f",
226 "br {at}",
227 "2:",
228 at = in(reg) layout::ALLOW_GATE,
229 inlateout("x0") path => ret,
230 in("x1") argv,
231 in("x2") envp,
232 in("x8") libc::SYS_execve,
233 out("x16") _,
234 options(nostack),
235 );
236 }
237 ret
238}
239
240#[cfg(test)]
241mod tests {
242 use super::layout::*;
243
244 #[test]
246 fn slots_fill_the_mailbox() {
247 assert_eq!(slot_addr(0), MAIL_ADDR + PAGE);
248 assert_eq!(slot_addr(SLOTS - 1) + SLOT_SIZE, MAIL_ADDR + MAIL_SIZE);
249 }
250
251 #[test]
255 fn served_path_syscalls_are_trapped() {
256 use crate::abi::Abi;
257 use crate::syscall::{NO_PTRACE_SYSCALLS, NOTIFY_SYSCALLS, nr};
258 let registers_only = [nr::CLONE, nr::FORK, nr::VFORK, nr::FCHDIR, nr::CLONE3];
259 type Trapped = fn(i64) -> bool;
260 let tables: [(Abi, Trapped); 4] = [
261 (Abi::X86_64, trapped_x86_64),
262 (Abi::I386, trapped_i386),
263 (Abi::Aarch64, trapped_aarch64),
264 (Abi::Arm, trapped_arm),
265 ];
266 for (abi, trapped) in tables {
267 for nr in abi.numbers_for(NOTIFY_SYSCALLS) {
268 assert!(trapped(nr), "{abi:?} {nr} is served but not staged");
269 }
270 for nr in abi.numbers_for(NO_PTRACE_SYSCALLS) {
271 let canonical = abi.to_canonical(nr).unwrap().0;
272 assert_eq!(trapped(nr), !registers_only.contains(&canonical), "{abi:?} {nr}");
273 }
274 }
275 for nr in [nr::GETUID, nr::FCHOWN, nr::SETUID] {
276 assert!(!trapped_x86_64(nr), "{nr}");
277 }
278 }
279
280 #[test]
283 fn staging_names_the_right_arguments() {
284 assert_eq!(staging_x86_64(262).unwrap()[0], (1, Stage::Path));
285 assert_eq!(
286 staging_x86_64(262).unwrap()[1],
287 (2, Stage::Out(Len::Fixed(144), Produced::Whole))
288 );
289 assert_eq!(
290 staging_x86_64(332).unwrap()[1],
291 (4, Stage::Out(Len::Fixed(256), Produced::Whole))
292 );
293 assert_eq!(
294 staging_x86_64(266).unwrap(),
295 &[(0, Stage::Path), (2, Stage::Path)]
296 );
297 assert_eq!(
298 staging_x86_64(267).unwrap()[1],
299 (2, Stage::Out(Len::Arg(3), Produced::Ret))
300 );
301 assert_eq!(
302 staging_x86_64(79).unwrap()[0],
303 (0, Stage::Out(Len::Arg(1), Produced::Ret))
304 );
305 assert!(staging_x86_64(0).is_none());
306 assert_eq!(
307 staging_i386(195).unwrap()[1],
308 (1, Stage::Out(Len::Fixed(96), Produced::Whole))
309 );
310 assert_eq!(
311 staging_i386(106).unwrap()[1],
312 (1, Stage::Out(Len::Fixed(64), Produced::Whole))
313 );
314 assert_eq!(
315 staging_i386(268).unwrap()[1],
316 (2, Stage::Out(Len::Fixed(84), Produced::Whole))
317 );
318 assert_eq!(
319 staging_i386(80).unwrap()[0],
320 (1, Stage::Out(Len::Elems(0, 2), Produced::RetElems(2)))
321 );
322 assert_eq!(staging_i386(304).unwrap(), &[(0, Stage::Path), (2, Stage::Path)]);
323 assert!(trapped_i386(11) && trapped_i386(174) && !trapped_i386(120));
324 assert_eq!(
325 staging_aarch64(79).unwrap()[1],
326 (2, Stage::Out(Len::Fixed(128), Produced::Whole))
327 );
328 assert_eq!(
329 staging_aarch64(36).unwrap(),
330 &[(0, Stage::Path), (2, Stage::Path)]
331 );
332 assert!(trapped_aarch64(221) && trapped_aarch64(134) && !trapped_aarch64(220));
333 assert_eq!(
334 staging_arm(327).unwrap()[1],
335 (2, Stage::Out(Len::Fixed(104), Produced::Whole))
336 );
337 assert_eq!(
338 staging_arm(266).unwrap()[1],
339 (2, Stage::Out(Len::Fixed(84), Produced::Whole))
340 );
341 assert!(trapped_arm(387) && trapped_arm(174) && !trapped_arm(120));
342 }
343}