1use anyhow::{Error, format_err};
6use extended_pstate::ExtendedPstatePointer;
7use starnix_core::arch::execution::new_syscall;
8use starnix_core::ptrace::{StopState, ptrace_syscall_enter, ptrace_syscall_exit};
9use starnix_core::signals::{
10 SignalInfo, deliver_signal, dequeue_signal, prepare_to_restart_syscall,
11};
12use starnix_core::task::{CurrentTask, ExceptionResult, ExitStatus, SeccompStateValue, TaskFlags};
13use starnix_logging::{
14 CATEGORY_STARNIX, NAME_HANDLE_EXCEPTION, NAME_RESTRICTED_KICK, NAME_RUN_TASK, log_error,
15 log_syscall, log_trace, log_warn, set_current_task_info,
16};
17use starnix_registers::RestrictedState;
18
19use starnix_syscalls::SyscallResult;
20use starnix_syscalls::decls::{Syscall, SyscallDecl};
21use starnix_uapi::errno;
22use starnix_uapi::errors::Errno;
23use starnix_uapi::signals::SIGKILL;
24use zerocopy::FromZeros;
25
26mod table;
27
28pub fn enter(current_task: &mut CurrentTask) -> ExitStatus {
29 let mut exception_report = zx::sys::zx_exception_report_t::new_zeroed();
32 match RestrictedState::bind_and_map(
33 &mut current_task.thread_state.registers,
34 &mut exception_report,
35 ) {
36 Ok(restricted_state) => {
37 match run_task(current_task, restricted_state.bound_state.as_ptr(), &exception_report) {
38 Ok(ok) => ok,
39 Err(error) => {
40 log_warn!("Died unexpectedly from {error:?}! treating as SIGKILL");
41 ExitStatus::Kill(SignalInfo::kernel(SIGKILL))
42 }
43 }
44 }
45 Err(error) => {
46 log_error!("failed to map mode state vmo, {error:?}! treating as SIGKILL");
47 ExitStatus::Kill(SignalInfo::kernel(SIGKILL))
48 }
49 }
50}
51
52type RestrictedExitCallback = extern "C" fn(
53 *mut RestrictedEnterContext<'_>,
54 zx::sys::zx_restricted_reason_t,
55 *mut ExtendedPstatePointer,
56) -> bool;
57
58unsafe extern "C" {
59 #[allow(improper_ctypes)]
62 fn restricted_enter_loop(
63 options: u32,
64 restricted_exit_callback: RestrictedExitCallback,
65 restricted_exit_callback_context: *mut RestrictedEnterContext<'_>,
66 restricted_state: *mut zx::sys::zx_restricted_state_t,
67 extended_pstate_ptr_ptr: *mut ExtendedPstatePointer,
68 ) -> zx::sys::zx_status_t;
69}
70
71const RESTRICTED_ENTER_OPTIONS: u32 = 0;
72
73struct RestrictedEnterContext<'a> {
74 current_task: &'a mut CurrentTask,
75 error_context: Option<ErrorContext>,
76 exit_status: Result<ExitStatus, Error>,
77 exception_report_raw: *const zx::sys::zx_exception_report_t,
78}
79
80fn run_task(
94 current_task: &mut CurrentTask,
95 restricted_state_ptr: *mut zx::sys::zx_restricted_state_t,
96 exception_report_raw: *const zx::sys::zx_exception_report_t,
97) -> Result<ExitStatus, Error> {
98 set_current_task_info(
99 current_task.task.command(),
100 current_task.task.thread_group().read().leader_command(),
101 current_task.task.thread_group().leader,
102 current_task.tid,
103 );
104
105 fuchsia_trace::duration!(CATEGORY_STARNIX, NAME_RUN_TASK);
106
107 let error_context = None;
109
110 if let Some(exit_status) = process_completed_restricted_exit(current_task, &error_context)? {
113 return Ok(exit_status);
114 }
115
116 let mut extended_pstate_ptr = current_task.thread_state.extended_pstate.as_ptr();
119
120 let mut restricted_enter_context = RestrictedEnterContext {
121 current_task,
122 error_context,
123 exit_status: Err(errno!(ENOEXEC).into()),
124 exception_report_raw,
125 };
126
127 #[allow(
128 clippy::undocumented_unsafe_blocks,
129 reason = "Force documented unsafe blocks in Starnix"
130 )]
131 let restricted_enter_status = zx::Status::from_raw(unsafe {
132 restricted_enter_loop(
133 RESTRICTED_ENTER_OPTIONS,
134 restricted_exit_callback_c,
135 &mut restricted_enter_context,
136 restricted_state_ptr,
137 &raw mut extended_pstate_ptr,
138 )
139 });
140 if restricted_enter_status != zx::Status::OK {
141 log_error!(
144 "restricted_enter_loop failed: {}, register state: {:?}",
145 restricted_enter_status,
146 restricted_enter_context.current_task.thread_state.registers
147 );
148 }
149 restricted_enter_context.exit_status
150}
151
152extern "C" fn restricted_exit_callback_c(
153 context: *mut RestrictedEnterContext<'_>,
154 reason_code: zx::sys::zx_restricted_reason_t,
155 extended_pstate_ptr_ptr: *mut ExtendedPstatePointer,
156) -> bool {
157 let (restricted_context, extended_pstate_ptr) =
165 unsafe { (&mut *context, extended_pstate_ptr_ptr.as_mut_unchecked()) };
166 restricted_exit_callback(
167 reason_code,
168 restricted_context.current_task,
169 &mut restricted_context.error_context,
170 &mut restricted_context.exit_status,
171 extended_pstate_ptr,
172 restricted_context.exception_report_raw,
173 )
174}
175
176fn restricted_exit_callback(
177 reason_code: zx::sys::zx_restricted_reason_t,
178 current_task: &mut CurrentTask,
179 error_context: &mut Option<ErrorContext>,
180 exit_status: &mut Result<ExitStatus, Error>,
181 extended_pstate_ptr: &mut ExtendedPstatePointer,
182 exception_report_raw: *const zx::sys::zx_exception_report_t,
183) -> bool {
184 debug_assert_eq!(
185 current_task.thread_state.restart_code, None,
186 "restart_code should only ever be Some() in normal mode",
187 );
188
189 let ret = match process_restricted_exit(
190 reason_code,
191 current_task,
192 error_context,
193 exception_report_raw,
194 ) {
195 Ok(None) => {
196 *extended_pstate_ptr = current_task.thread_state.extended_pstate.as_ptr();
199
200 true
201 }
202 Ok(Some(completed_exit_status)) => {
203 *exit_status = Ok(completed_exit_status);
204 false
205 }
206 Err(error) => {
207 *exit_status = Err(error);
208 false
209 }
210 };
211
212 debug_assert_eq!(
213 current_task.thread_state.restart_code, None,
214 "restart_code should only ever be Some() in normal mode",
215 );
216
217 ret
218}
219
220fn process_restricted_exit(
221 reason_code: zx::sys::zx_restricted_reason_t,
222 current_task: &mut CurrentTask,
223 error_context: &mut Option<ErrorContext>,
224 exception_report_raw: *const zx::sys::zx_exception_report_t,
225) -> Result<Option<ExitStatus>, Error> {
226 current_task.thread_state.registers.sync_stack_ptr();
227
228 match reason_code {
229 zx::sys::ZX_RESTRICTED_REASON_SYSCALL => {
230 let syscall_decl = SyscallDecl::from_number(
231 current_task.thread_state.registers.syscall_register(),
232 current_task.thread_state.arch_width(),
233 );
234
235 if let Some(new_error_context) = execute_syscall(current_task, syscall_decl) {
236 *error_context = Some(new_error_context);
237 }
238 }
239 zx::sys::ZX_RESTRICTED_REASON_EXCEPTION => {
240 fuchsia_trace::duration!(CATEGORY_STARNIX, NAME_HANDLE_EXCEPTION);
241 let exception_report = unsafe { zx::ExceptionReport::from_raw(*exception_report_raw) };
243 let exception_result = current_task.process_exception(&exception_report);
244 process_completed_exception(current_task, exception_result, exception_report);
245 }
246 zx::sys::ZX_RESTRICTED_REASON_KICK => {
247 fuchsia_trace::instant!(
248 CATEGORY_STARNIX,
249 NAME_RESTRICTED_KICK,
250 fuchsia_trace::Scope::Thread
251 );
252 }
256 _ => {
257 return Err(format_err!("Received unexpected restricted reason code: {}", reason_code));
258 }
259 }
260
261 if let Some(exit_status) = process_completed_restricted_exit(current_task, &error_context)? {
262 return Ok(Some(exit_status));
263 }
264
265 Ok(None)
266}
267
268fn process_completed_exception(
269 current_task: &mut CurrentTask,
270 exception_result: ExceptionResult,
271 restricted_exception: zx::ExceptionReport,
272) {
273 match exception_result {
274 ExceptionResult::Handled => {}
275 ExceptionResult::Signal(signal) => {
276 let mut task_state = current_task.task.write();
277 if task_state.ptrace_on_signal_consume() {
278 task_state.set_stopped(
279 StopState::SignalDeliveryStopping,
280 Some(signal),
281 Some(¤t_task),
282 None,
283 );
284 return;
285 }
286
287 if let Some(status) = deliver_signal(
288 current_task.task.as_ref(),
289 current_task.thread_state.arch_width(),
290 task_state,
291 signal.into(),
292 &mut current_task.thread_state.registers,
293 ¤t_task.thread_state.extended_pstate,
294 Some(restricted_exception),
295 ) {
296 current_task.kill_thread_group(status);
297 }
298 }
299 }
300}
301
302#[derive(Debug)]
307pub struct ErrorContext {
308 pub syscall: Syscall,
310
311 pub error: Errno,
313}
314
315#[inline(never)] pub fn execute_syscall(
320 current_task: &mut CurrentTask,
321 syscall_decl: SyscallDecl,
322) -> Option<ErrorContext> {
323 fuchsia_trace::duration!(CATEGORY_STARNIX, syscall_decl.trace_name());
324 let syscall = new_syscall(syscall_decl, current_task);
325
326 current_task.thread_state.registers.save_registers_for_restart(syscall.decl.number);
327
328 if current_task.trace_syscalls.load(std::sync::atomic::Ordering::Relaxed) {
329 ptrace_syscall_enter(current_task);
330 }
331
332 log_syscall!(current_task, "{syscall:?}");
333
334 let _lockup_detector_guard = starnix_core::task::ThreadLockupDetector::track();
335 let result: Result<SyscallResult, Errno> =
336 if current_task.seccomp_filter_state.get() != SeccompStateValue::None {
337 if let Some(res) = current_task.run_seccomp_filters(&syscall) {
340 res
341 } else {
342 table::dispatch_syscall(current_task, &syscall)
343 }
344 } else {
345 table::dispatch_syscall(current_task, &syscall)
346 };
347
348 current_task.trigger_delayed_releaser();
349
350 let return_value = match result {
351 Ok(return_value) => {
352 log_syscall!(current_task, "-> {:#x}", return_value.value());
353 current_task.thread_state.registers.set_return_register(return_value.value());
354 None
355 }
356 Err(errno) => {
357 log_syscall!(current_task, "!-> {errno}");
358 if errno.is_restartable() {
359 current_task.thread_state.restart_code = Some(errno.code);
360 }
361 current_task.thread_state.registers.set_return_register(errno.return_value());
362 Some(ErrorContext { error: errno, syscall })
363 }
364 };
365
366 if current_task.trace_syscalls.load(std::sync::atomic::Ordering::Relaxed) {
367 ptrace_syscall_exit(current_task, return_value.is_some());
368 }
369
370 return_value
371}
372
373pub fn process_completed_restricted_exit(
377 current_task: &mut CurrentTask,
378 error_context: &Option<ErrorContext>,
379) -> Result<Option<ExitStatus>, Errno> {
380 let result;
381 loop {
382 {
384 {
385 if !current_task.is_exitted() {
386 dequeue_signal(current_task);
387 }
388 prepare_to_restart_syscall(&mut current_task.thread_state, None);
391 }
392 }
393
394 let exit_status = current_task.exit_status();
395 if let Some(exit_status) = exit_status {
396 log_trace!("exiting with status {:?}", exit_status);
397 if let Some(error_context) = error_context {
398 match exit_status {
399 ExitStatus::Exit(value) if value == 0 => {}
400 _ => {
401 log_trace!(
402 "last failing syscall before exit: {:?}, failed with {:?}",
403 error_context.syscall,
404 error_context.error
405 );
406 }
407 };
408 }
409
410 result = Some(exit_status);
411 break;
412 } else {
413 if current_task.block_if_stopped() {
416 continue;
419 }
420 result = None;
421 current_task.write().restore_signal_mask();
423 break;
424 }
425 }
426
427 if let Some(ExitStatus::CoreDump(signal_info)) = &result {
428 if current_task.flags().contains(TaskFlags::DUMP_ON_EXIT) {
429 if !signal_info.is_sent_by(¤t_task.weak_task()) {
431 debug::backtrace_request_current_thread();
436 }
437
438 if let Some(pending_report) =
439 current_task.kernel().crash_reporter.begin_crash_report(¤t_task)
440 {
441 current_task.kernel().crash_reporter.handle_core_dump(
442 ¤t_task,
443 signal_info,
444 pending_report,
445 );
446 }
447 }
448 }
449 return Ok(result);
450}