1#![recursion_limit = "256"]
6
7use anyhow::{Error, format_err};
8use extended_pstate::ExtendedPstatePointer;
9use starnix_core::arch::execution::new_syscall;
10use starnix_core::signals::{SignalInfo, dequeue_signal, force_signal, prepare_to_restart_syscall};
11use starnix_core::task::{CurrentTask, ExceptionResult, ExitStatus, SeccompStateValue, TaskFlags};
12use starnix_logging::{
13 CATEGORY_STARNIX, NAME_HANDLE_EXCEPTION, NAME_RESTRICTED_KICK, NAME_RUN_TASK, log_error,
14 log_syscall, log_trace, log_warn, set_current_task_info,
15};
16use starnix_registers::RestrictedState;
17
18use starnix_syscalls::SyscallResult;
19use starnix_syscalls::decls::{Syscall, SyscallDecl};
20use starnix_uapi::errno;
21use starnix_uapi::errors::Errno;
22use starnix_uapi::signals::{SIGKILL, SIGSEGV};
23use zerocopy::FromZeros;
24
25mod table;
26
27pub fn enter(current_task: &mut CurrentTask) -> ExitStatus {
28 let mut exception_report = zx::sys::zx_exception_report_t::new_zeroed();
31 match RestrictedState::bind_and_map(
32 &mut current_task.thread_state.registers,
33 &mut exception_report,
34 ) {
35 Ok(restricted_state) => {
36 match run_task(current_task, restricted_state.bound_state.as_ptr(), &exception_report) {
37 Ok(ok) => ok,
38 Err(error) => {
39 log_warn!("Died unexpectedly from {error:?}! treating as SIGKILL");
40 ExitStatus::Kill(SignalInfo::kernel(SIGKILL))
41 }
42 }
43 }
44 Err(error) => {
45 log_error!("failed to map mode state vmo, {error:?}! treating as SIGKILL");
46 ExitStatus::Kill(SignalInfo::kernel(SIGKILL))
47 }
48 }
49}
50
51unsafe extern "C" {
52 #[allow(improper_ctypes)]
68 fn restricted_enter_loop(
69 options: u32,
70 restricted_exit_callback_context: *mut RestrictedEnterContext<'_>,
71 restricted_state: *mut zx::sys::zx_restricted_state_t,
72 extended_pstate_ptr_ptr: *mut ExtendedPstatePointer,
73 ) -> zx::sys::zx_status_t;
74}
75
76const RESTRICTED_ENTER_OPTIONS: u32 = 0;
77
78struct RestrictedEnterContext<'a> {
79 current_task: &'a mut CurrentTask,
80 error_context: Option<ErrorContext>,
81 exit_status: Result<ExitStatus, Error>,
82 exception_report_raw: *const zx::sys::zx_exception_report_t,
83}
84
85fn run_task(
99 current_task: &mut CurrentTask,
100 restricted_state_ptr: *mut zx::sys::zx_restricted_state_t,
101 exception_report_raw: *const zx::sys::zx_exception_report_t,
102) -> Result<ExitStatus, Error> {
103 set_current_task_info(
104 current_task.task.command(),
105 current_task.task.thread_group().read().leader_command(),
106 current_task.task.pid.id,
107 current_task.tid.id,
108 );
109
110 fuchsia_trace::duration!(CATEGORY_STARNIX, NAME_RUN_TASK);
111
112 let error_context = None;
114
115 if let Some(exit_status) = process_completed_restricted_exit(current_task, &error_context)? {
118 return Ok(exit_status);
119 }
120
121 let mut restricted_enter_context = RestrictedEnterContext {
122 current_task,
123 error_context,
124 exit_status: Err(errno!(ENOEXEC).into()),
125 exception_report_raw,
126 };
127
128 loop {
129 let mut extended_pstate_ptr =
130 restricted_enter_context.current_task.thread_state.extended_pstate.as_ptr();
131 let restricted_enter_status = zx::Status::ok(unsafe {
140 restricted_enter_loop(
141 RESTRICTED_ENTER_OPTIONS,
142 &raw mut restricted_enter_context,
143 restricted_state_ptr,
144 &raw mut extended_pstate_ptr,
145 )
146 });
147 match restricted_enter_status {
148 Ok(()) => break,
149 Err(zx::Status::BAD_STATE) => {
150 force_signal(
155 restricted_enter_context.current_task,
156 SignalInfo::forced(SIGSEGV),
157 None,
158 );
159 if let Some(exit_status) = process_completed_restricted_exit(
160 restricted_enter_context.current_task,
161 &restricted_enter_context.error_context,
162 )? {
163 return Ok(exit_status);
164 }
165 }
166 Err(status) => {
167 log_error!(
170 "restricted_enter_loop failed: {}, register state: {:?}",
171 status,
172 restricted_enter_context.current_task.thread_state.registers
173 );
174 break;
175 }
176 }
177 }
178 restricted_enter_context.exit_status
179}
180
181#[allow(private_interfaces)]
191#[unsafe(no_mangle)]
192pub unsafe extern "C" fn restricted_exit_callback_c(
193 context: *mut RestrictedEnterContext<'_>,
194 reason_code: zx::sys::zx_restricted_reason_t,
195 extended_pstate_ptr_ptr: *mut ExtendedPstatePointer,
196) -> bool {
197 let (restricted_context, extended_pstate_ptr) =
205 unsafe { (&mut *context, extended_pstate_ptr_ptr.as_mut_unchecked()) };
206 restricted_exit_callback(
207 reason_code,
208 restricted_context.current_task,
209 &mut restricted_context.error_context,
210 &mut restricted_context.exit_status,
211 extended_pstate_ptr,
212 restricted_context.exception_report_raw,
213 )
214}
215
216fn restricted_exit_callback(
217 reason_code: zx::sys::zx_restricted_reason_t,
218 current_task: &mut CurrentTask,
219 error_context: &mut Option<ErrorContext>,
220 exit_status: &mut Result<ExitStatus, Error>,
221 extended_pstate_ptr: &mut ExtendedPstatePointer,
222 exception_report_raw: *const zx::sys::zx_exception_report_t,
223) -> bool {
224 debug_assert_eq!(
225 current_task.thread_state.restart_code, None,
226 "restart_code should only ever be Some() in normal mode",
227 );
228
229 let ret = match process_restricted_exit(
230 reason_code,
231 current_task,
232 error_context,
233 exception_report_raw,
234 ) {
235 Ok(None) => {
236 *extended_pstate_ptr = current_task.thread_state.extended_pstate.as_ptr();
239
240 true
241 }
242 Ok(Some(completed_exit_status)) => {
243 *exit_status = Ok(completed_exit_status);
244 false
245 }
246 Err(error) => {
247 *exit_status = Err(error);
248 false
249 }
250 };
251
252 debug_assert_eq!(
253 current_task.thread_state.restart_code, None,
254 "restart_code should only ever be Some() in normal mode",
255 );
256
257 ret
258}
259
260fn process_restricted_exit(
261 reason_code: zx::sys::zx_restricted_reason_t,
262 current_task: &mut CurrentTask,
263 error_context: &mut Option<ErrorContext>,
264 exception_report_raw: *const zx::sys::zx_exception_report_t,
265) -> Result<Option<ExitStatus>, Error> {
266 #[cfg(target_arch = "aarch64")]
267 current_task.thread_state.registers.sync_stack_ptr();
268
269 match reason_code {
270 zx::sys::ZX_RESTRICTED_REASON_SYSCALL => {
271 execute_syscall(current_task, error_context);
272 }
273 zx::sys::ZX_RESTRICTED_REASON_EXCEPTION => {
274 if !current_task.is_exitted() {
275 fuchsia_trace::duration!(CATEGORY_STARNIX, NAME_HANDLE_EXCEPTION);
276 let exception_report =
278 unsafe { zx::ExceptionReport::from_raw(*exception_report_raw) };
279 let exception_result = current_task.process_exception(&exception_report);
280 process_completed_exception(current_task, exception_result, exception_report);
281 }
282 }
283 zx::sys::ZX_RESTRICTED_REASON_KICK => {
284 fuchsia_trace::instant!(
285 CATEGORY_STARNIX,
286 NAME_RESTRICTED_KICK,
287 fuchsia_trace::Scope::Thread
288 );
289 }
293 _ => {
294 return Err(format_err!("Received unexpected restricted reason code: {}", reason_code));
295 }
296 }
297
298 if !has_pending_exit_work(current_task) {
301 return Ok(None);
302 }
303
304 if let Some(exit_status) = process_completed_restricted_exit(current_task, &error_context)? {
305 return Ok(Some(exit_status));
306 }
307
308 Ok(None)
309}
310
311fn process_completed_exception(
312 current_task: &mut CurrentTask,
313 exception_result: ExceptionResult,
314 restricted_exception: zx::ExceptionReport,
315) {
316 match exception_result {
317 ExceptionResult::Handled => {}
318 ExceptionResult::Signal(signal) => {
319 force_signal(current_task, signal, Some(restricted_exception));
320 }
321 }
322}
323
324#[derive(Debug)]
329pub struct ErrorContext {
330 pub syscall: Syscall,
332
333 pub error: Errno,
335}
336
337#[inline(never)] pub fn execute_syscall(current_task: &mut CurrentTask, error_context: &mut Option<ErrorContext>) {
350 current_task.thread_state.registers.save_registers_for_restart();
351
352 current_task.ptrace().on_syscall_enter();
353 if current_task.is_exitted() {
356 return;
357 }
358
359 let syscall_decl = SyscallDecl::from_number(
360 current_task.thread_state.registers.syscall_register(),
361 current_task.thread_state.arch_width(),
362 );
363
364 let _syscall_trace = starnix_core::task::begin_syscall_trace(|| syscall_decl.trace_name());
365 let syscall = new_syscall(syscall_decl, current_task);
366
367 log_syscall!(current_task, "{syscall:?}");
368
369 let _lockup_detector_guard = starnix_core::task::ThreadLockupDetector::track();
370 let result: Result<SyscallResult, Errno> =
371 if current_task.seccomp_filter_state.get() != SeccompStateValue::None {
372 if let Some(res) = current_task.run_seccomp_filters(&syscall) {
375 res
376 } else {
377 table::dispatch_syscall(current_task, &syscall)
378 }
379 } else {
380 table::dispatch_syscall(current_task, &syscall)
381 };
382
383 current_task.trigger_delayed_releaser();
384
385 let is_error = match result {
386 Ok(return_value) => {
387 log_syscall!(current_task, "-> {:#x}", return_value.value());
388 current_task.thread_state.registers.set_return_register(return_value.value());
389 false
390 }
391 Err(errno) => {
392 log_syscall!(current_task, "!-> {errno}");
393 if errno.is_restartable() {
394 current_task.thread_state.restart_code = Some(errno.code);
395 }
396 current_task.thread_state.registers.set_return_register(errno.return_value());
397 *error_context = Some(ErrorContext { error: errno, syscall });
398 true
399 }
400 };
401
402 current_task.ptrace().on_syscall_exit(is_error);
403}
404
405#[inline(always)]
423fn has_pending_exit_work(current_task: &CurrentTask) -> bool {
424 current_task.thread_state.restart_code.is_some()
425 || current_task.is_exitted()
426 || current_task.should_check_for_pending_signals()
427 || current_task.load_stopped().is_stopping_or_stopped()
428 || current_task.thread_group().load_stopped().is_stopping_or_stopped()
429}
430
431pub fn process_completed_restricted_exit(
438 current_task: &mut CurrentTask,
439 error_context: &Option<ErrorContext>,
440) -> Result<Option<ExitStatus>, Errno> {
441 let result;
442 loop {
443 {
445 {
446 if !current_task.is_exitted() {
447 dequeue_signal(current_task);
448 }
449 prepare_to_restart_syscall(&mut current_task.thread_state, None);
452 }
453 }
454
455 let exit_status = current_task.exit_status();
456 if let Some(exit_status) = exit_status {
457 log_trace!("exiting with status {:?}", exit_status);
458 if let Some(error_context) = error_context {
459 match exit_status {
460 ExitStatus::Exit(value) if value == 0 => {}
461 _ => {
462 log_trace!(
463 "last failing syscall before exit: {:?}, failed with {:?}",
464 error_context.syscall,
465 error_context.error
466 );
467 }
468 };
469 }
470
471 result = Some(exit_status);
472 break;
473 } else {
474 if current_task.block_if_stopped() {
477 continue;
480 }
481 if current_task.flags().contains(TaskFlags::TEMPORARY_SIGNAL_MASK) {
483 current_task.write().restore_signal_mask();
484 if current_task.should_check_for_pending_signals() {
485 continue;
486 }
487 }
488
489 result = None;
490 break;
491 }
492 }
493
494 if let Some(ExitStatus::CoreDump(signal_info)) = &result {
495 if current_task.flags().contains(TaskFlags::DUMP_ON_EXIT) {
496 if !signal_info.is_sent_by(¤t_task.weak_task()) {
498 debug::backtrace_request_current_thread();
503 }
504
505 if let Some(pending_report) =
506 current_task.kernel().crash_reporter.begin_crash_report(¤t_task)
507 {
508 current_task.kernel().crash_reporter.handle_core_dump(
509 ¤t_task,
510 signal_info,
511 pending_report,
512 );
513 }
514 }
515 }
516 return Ok(result);
517}
518
519#[cfg(all(test, target_arch = "aarch64"))]
520mod tests {
521 use super::*;
522 use core::arch::asm;
523 use extended_pstate::{
524 ExtendedAarch32PstateState, ExtendedPstatePointer, ExtendedPstateState,
525 restore_extended_pstate, save_extended_aarch32_pstate, save_extended_pstate,
526 };
527
528 unsafe extern "C" {
529 fn zx_vmar_root_self() -> zx::sys::zx_handle_t;
530 }
531
532 unsafe fn call_restricted_enter_loop_and_save_pstate(
546 seed_pstate_ptr: *mut ExtendedPstatePointer,
547 restricted_state: *mut zx::sys::zx_restricted_state_t,
548 in_pstate_ptr: *mut ExtendedPstatePointer,
549 out_pstate_ptr: *mut ExtendedPstatePointer,
550 ) -> zx::sys::zx_status_t {
551 let status: zx::sys::zx_status_t;
552 unsafe {
562 asm!(
563 "mov x0, x20",
564 "bl {restore_fn}",
565 "mov w0, #-1",
566 "mov x1, xzr",
567 "mov x2, x21",
568 "mov x3, x22",
569 "bl {enter_loop_fn}",
570 "mov w24, w0",
571 "mov x0, x23",
572 "bl {save_fn}",
573 "msr fpcr, xzr",
574 "msr fpsr, xzr",
575 restore_fn = sym restore_extended_pstate,
576 enter_loop_fn = sym restricted_enter_loop,
577 save_fn = sym save_extended_pstate,
578 in("x20") seed_pstate_ptr,
579 in("x21") restricted_state,
580 in("x22") in_pstate_ptr,
581 in("x23") out_pstate_ptr,
582 out("w24") status,
583 clobber_abi("C"),
584 out("v8") _, out("v9") _, out("v10") _, out("v11") _,
585 out("v12") _, out("v13") _, out("v14") _, out("v15") _,
586 );
587 }
588 status
589 }
590
591 #[test]
592 fn test_restricted_enter_loop_restores_aarch64_and_aarch32_pstate() {
593 assert_eq!(std::mem::offset_of!(zx::sys::zx_restricted_state_t, cpsr), 34 * 8);
594
595 let mut qregs64 = [0u128; 32];
596 for (i, q) in qregs64.iter_mut().enumerate() {
597 *q = ((i as u128 + 1) << 64) | (0x0102_0304_0506_0708_u128 + i as u128);
598 }
599 let fpcr64: u32 = 1 << 24;
601 let fpsr64: u32 = 0x15;
602 let mut pregs64 = [0u16; 16];
603 for (i, p) in pregs64.iter_mut().enumerate() {
604 *p = 0x5a00 | (i as u16 + 1);
605 }
606 let ffr64: u16 = 0x00ff;
607
608 let mut state64 = ExtendedPstateState::default();
609 state64.set_arm64_state(&qregs64, fpsr64, fpcr64);
610 if state64.has_arm64_sve() {
611 state64.set_arm64_sve_state(&pregs64, ffr64);
612 }
613
614 let mut zero_seed = ExtendedPstateState::default();
615 let mut zero_seed_ptr = ExtendedPstatePointer { extended_pstate: &raw mut zero_seed };
616 let mut in_pstate64_ptr = ExtendedPstatePointer { extended_pstate: &raw mut state64 };
617 let mut saved_after_64 = ExtendedPstateState::default();
618 let mut saved_after_64_ptr =
619 ExtendedPstatePointer { extended_pstate: &raw mut saved_after_64 };
620
621 let mut restricted_state = zx::sys::zx_restricted_state_t::default();
622 restricted_state.cpsr = 0;
623
624 let status = unsafe {
627 call_restricted_enter_loop_and_save_pstate(
628 &raw mut zero_seed_ptr,
629 &raw mut restricted_state,
630 &raw mut in_pstate64_ptr,
631 &raw mut saved_after_64_ptr,
632 )
633 };
634 assert_eq!(status, zx::sys::ZX_ERR_INVALID_ARGS);
635 assert_eq!(saved_after_64.get_arm64_qregs(), &qregs64);
636 assert_eq!(saved_after_64.get_arm64_fpcr(), fpcr64);
637 assert_eq!(saved_after_64.get_arm64_fpsr(), fpsr64);
638 if state64.has_arm64_sve() {
639 assert_eq!(saved_after_64.get_arm64_p_regs(), &pregs64);
640 assert_eq!(saved_after_64.get_arm64_ffr(), ffr64);
641 }
642
643 let root_vmar = unsafe { zx::Unowned::<zx::Vmar>::from_raw_handle(zx_vmar_root_self()) };
650 let page_size = zx::system_get_page_size() as usize;
651 let (sub_vmar, _sub_vmar_base) = root_vmar
652 .allocate(
653 0,
654 2 * page_size,
655 zx::VmarFlags::CAN_MAP_READ
656 | zx::VmarFlags::CAN_MAP_WRITE
657 | zx::VmarFlags::CAN_MAP_SPECIFIC,
658 )
659 .expect("allocate 2-page VMAR");
660 let vmo = zx::Vmo::create(page_size as u64).expect("create 1-page VMO");
661 let page_start = sub_vmar
662 .map(
663 0,
664 &vmo,
665 0,
666 page_size,
667 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE | zx::VmarFlags::SPECIFIC,
668 )
669 .expect("map first page");
670 let page_end = page_start + page_size;
671 let state32_addr = page_end - std::mem::size_of::<ExtendedAarch32PstateState>();
672 let state32_ptr: *mut ExtendedAarch32PstateState =
673 std::ptr::without_provenance_mut(state32_addr);
674 assert!(state32_ptr.is_aligned());
675
676 let mut qregs32 = [0u128; 16];
677 for (i, q) in qregs32.iter_mut().enumerate() {
678 *q = 0xfedc_ba98_7654_3210_u128 ^ ((i as u128 + 1) * 0x1111_2222_3333_4444_u128);
679 }
680 let fpcr32: u32 = (1 << 25) | (1 << 22);
682 let fpsr32: u32 = 0x0a;
683
684 let mut init32_from_64 = ExtendedPstateState::with_sve(false);
685 let mut init32_qregs = [0u128; 32];
686 init32_qregs[..16].copy_from_slice(&qregs32);
687 init32_from_64.set_arm64_state(&init32_qregs, fpsr32, fpcr32);
688 let mut init32_from_64_ptr =
689 ExtendedPstatePointer { extended_pstate: &raw mut init32_from_64 };
690 let mut in_pstate32_ptr = ExtendedPstatePointer { extended_aarch32_pstate: state32_ptr };
691
692 unsafe {
695 state32_ptr.write(ExtendedAarch32PstateState::default());
696 asm!(
697 "mov x0, x20",
698 "bl {restore_fn}",
699 "mov x0, x21",
700 "bl {save_aarch32_fn}",
701 "msr fpcr, xzr",
702 "msr fpsr, xzr",
703 restore_fn = sym restore_extended_pstate,
704 save_aarch32_fn = sym save_extended_aarch32_pstate,
705 in("x20") &raw mut init32_from_64_ptr,
706 in("x21") &raw mut in_pstate32_ptr,
707 clobber_abi("C"),
708 out("v8") _, out("v9") _, out("v10") _, out("v11") _,
709 out("v12") _, out("v13") _, out("v14") _, out("v15") _,
710 );
711 let state32_ref = &*state32_ptr;
712 assert_eq!(state32_ref.get_arm32_qregs(), &qregs32);
713 assert_eq!(state32_ref.get_arm32_fpcr(), fpcr32);
714 assert_eq!(state32_ref.get_arm32_fpsr(), fpsr32);
715 }
716
717 restricted_state.cpsr = zx::sys::ZX_REG_CPSR_ARCH_32_MASK as u32;
718 let mut saved_after_32 = ExtendedPstateState::default();
719 let mut saved_after_32_ptr =
720 ExtendedPstatePointer { extended_pstate: &raw mut saved_after_32 };
721
722 let status = unsafe {
729 call_restricted_enter_loop_and_save_pstate(
730 &raw mut in_pstate64_ptr,
731 &raw mut restricted_state,
732 &raw mut in_pstate32_ptr,
733 &raw mut saved_after_32_ptr,
734 )
735 };
736 assert_eq!(status, zx::sys::ZX_ERR_INVALID_ARGS);
737 assert_eq!(&saved_after_32.get_arm64_qregs()[..16], &qregs32);
738 assert_eq!(&saved_after_32.get_arm64_qregs()[16..], &qregs64[16..]);
739 assert_eq!(saved_after_32.get_arm64_fpcr(), fpcr32);
740 assert_eq!(saved_after_32.get_arm64_fpsr(), fpsr32);
741
742 unsafe {
744 sub_vmar.destroy().expect("destroy 2-page VMAR");
745 }
746 }
747}