Skip to main content

starnix_core/signals/
signal_handling.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::arch::signal_handling::{
6    RED_ZONE_SIZE, SIG_STACK_SIZE, SignalStackFrame, align_stack_pointer, restore_registers,
7};
8use crate::mm::{MemoryAccessor, MemoryAccessorExt};
9use crate::ptrace::StopState;
10use crate::signals::{KernelSignal, KernelSignalInfo, SignalDetail, SignalInfo, SignalState};
11use crate::task::{
12    ArchExtendedPstateStorage, CurrentTask, ExitStatus, Task, TaskFlags, TaskWriteGuard,
13    ThreadState, Waiter,
14};
15use starnix_logging::{log_info, log_trace, log_warn};
16use starnix_registers::{RegisterState, RegisterStorageEnum};
17use starnix_syscalls::SyscallResult;
18use starnix_types::arch::ArchWidth;
19use starnix_uapi::errors::{EINTR, ERESTART_RESTARTBLOCK, Errno};
20use starnix_uapi::resource_limits::Resource;
21use starnix_uapi::signals::{
22    SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGIO, SIGKILL,
23    SIGPIPE, SIGPROF, SIGPWR, SIGQUIT, SIGSEGV, SIGSTKFLT, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP,
24    SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ,
25    SigSet, sigaltstack_contains_pointer,
26};
27use starnix_uapi::user_address::{ArchSpecific, UserAddress};
28use starnix_uapi::{
29    SA_NODEFER, SA_ONSTACK, SA_RESETHAND, SA_SIGINFO, SIG_DFL, SIG_IGN, errno, error, sigaction_t,
30};
31
32/// Indicates where in the signal queue a signal should go.  Signals
33/// can jump the queue when being injected by tools like ptrace.
34#[derive(PartialEq)]
35enum SignalPriority {
36    First,
37    Last,
38}
39
40// `send_signal*()` calls below may fail only for real-time signals (with EAGAIN). They are
41// expected to succeed for all other signals.
42
43/// Enqueues `signal_info` at the front of `task`'s signal queue and wakes `task`.
44///
45/// Callers must not hold `ThreadGroupMutableStateLock`, as delivering `SIGKILL` or waking the
46/// thread group acquires the target thread group's write lock.
47pub fn send_signal_first(task: &Task, task_state: TaskWriteGuard<'_>, signal_info: SignalInfo) {
48    send_signal_prio(
49        task,
50        task_state,
51        signal_info.into(),
52        SignalPriority::First,
53        /* force_wake = */ true,
54    )
55    .expect("send_signal(SignalPriority::First) is not expected to fail")
56}
57
58/// Sends a standard (non-real-time) `signal_info` to `task`.
59///
60/// Callers must not hold `ThreadGroupMutableStateLock`, as delivering `SIGKILL` or waking the
61/// thread group acquires the target thread group's write lock.
62pub fn send_standard_signal(task: &Task, signal_info: SignalInfo) {
63    debug_assert!(!signal_info.signal.is_real_time());
64    let state = task.write();
65    send_signal_prio(
66        task,
67        state,
68        signal_info.into(),
69        SignalPriority::Last,
70        /* force_wake = */ false,
71    )
72    .expect("send_signal(SignalPriority::Last) is not expected to fail for standard signals.")
73}
74
75/// Sends `signal_info` to `task`.
76///
77/// May only fail for real-time signals (with `EAGAIN` when `RLIMIT_SIGPENDING` is reached) and
78/// always succeeds for standard signals.
79///
80/// Callers must not hold `ThreadGroupMutableStateLock`, as delivering `SIGKILL` or waking the
81/// thread group acquires the target thread group's write lock.
82pub fn send_signal(task: &Task, signal_info: SignalInfo) -> Result<(), Errno> {
83    let state = task.write();
84    send_signal_prio(
85        task,
86        state,
87        signal_info.into(),
88        SignalPriority::Last,
89        /* force_wake = */ false,
90    )
91}
92
93pub fn send_freeze_signal(task: &Task, waiter: Waiter) -> Result<(), Errno> {
94    let state = task.write();
95    send_signal_prio(
96        task,
97        state,
98        KernelSignalInfo::Freeze(waiter),
99        SignalPriority::First,
100        /* force_wake = */ true,
101    )
102}
103
104fn send_signal_prio(
105    task: &Task,
106    mut task_state: TaskWriteGuard<'_>,
107    kernel_signal_info: KernelSignalInfo,
108    prio: SignalPriority,
109    force_wake: bool,
110) -> Result<(), Errno> {
111    let (signal_info, signal, is_masked, was_masked, is_real_time, sigaction, action) =
112        match kernel_signal_info {
113            KernelSignalInfo::User(ref user_signal_info) => {
114                if (!task_state.is_ptraced() || user_signal_info.signal == SIGKILL)
115                    && task.thread_group().ignores_signal(user_signal_info)
116                {
117                    return Ok(());
118                }
119                if user_signal_info.signal == SIGKILL {
120                    drop(task_state);
121                    // Synchronously transition the thread group to Exiting and interrupt all
122                    // member tasks.
123                    // TODO(https://fxbug.dev/573607334): Immediately transition the thread group
124                    // to Exiting for other unblocked default-fatal signals
125                    // (DeliveryAction::Terminate when not ptraced).
126                    task.thread_group().write().force_exit_for_sigkill(user_signal_info.clone());
127                    return Ok(());
128                }
129                let signal = user_signal_info.signal;
130                let is_masked = task_state.is_signal_masked(signal);
131                let was_masked = task_state.is_signal_masked_by_saved_mask(signal);
132                let sigaction = task.get_signal_action(signal);
133                let action = action_for_signal(user_signal_info, sigaction);
134                (
135                    Some(user_signal_info.clone()),
136                    Some(signal),
137                    is_masked,
138                    was_masked,
139                    signal.is_real_time(),
140                    Some(sigaction),
141                    Some(action),
142                )
143            }
144            KernelSignalInfo::Freeze(_) => (None, None, false, false, false, None, None),
145        };
146
147    if is_real_time && prio != SignalPriority::First {
148        if task_state.pending_signal_count()
149            >= task.thread_group().get_rlimit(Resource::SIGPENDING) as usize
150        {
151            return error!(EAGAIN);
152        }
153    }
154
155    // If the signal is ignored then it doesn't need to be queued, except the following 2 cases:
156    //  1. The signal is blocked by the current or the original mask. The signal may be unmasked
157    //     later, see `SigtimedwaitTest.IgnoredUnmaskedSignal` gvisor test.
158    //  2. The task is ptraced. In this case we want to queue the signal for signal-delivery-stop.
159    let is_queued = action.is_none()
160        || action != Some(DeliveryAction::Ignore)
161        || is_masked
162        || was_masked
163        || task_state.is_ptraced();
164    if is_queued {
165        match kernel_signal_info {
166            KernelSignalInfo::User(ref signal_info) => {
167                if prio == SignalPriority::First {
168                    task_state.enqueue_signal_front(signal_info.clone());
169                } else {
170                    task_state.enqueue_signal(signal_info.clone());
171                }
172                task_state.set_flags(TaskFlags::SIGNALS_AVAILABLE, true);
173            }
174            KernelSignalInfo::Freeze(waiter) => {
175                task_state.enqueue_kernel_signal(KernelSignal::Freeze(waiter))
176            }
177        }
178    }
179
180    drop(task_state);
181    if is_queued
182        && !is_masked
183        && action.map_or_else(|| true, |action| action.must_interrupt(sigaction))
184    {
185        // Wake the task. Note that any potential signal handler will be executed before
186        // the task returns from the suspend (from the perspective of user space).
187        task.interrupt();
188    }
189
190    // Unstop the process for SIGCONT. Also unstop for forced waking, which the only caller
191    // of send_signal_first wants it to do.
192    // TODO: Give SIGSTOP similar immediate-effect treatment.
193    if signal == Some(SIGCONT) || force_wake {
194        task.thread_group().set_stopped(
195            StopState::Waking,
196            signal_info,
197            /* finalize_only = */ false,
198        );
199        task.write().set_stopped(
200            StopState::Waking,
201            /* siginfo = */ None,
202            /* current_task = */ None,
203            /* event = */ None,
204        );
205    }
206
207    Ok(())
208}
209
210/// Represents the action to take when signal is delivered.
211///
212/// See https://man7.org/linux/man-pages/man7/signal.7.html.
213#[derive(Debug, PartialEq)]
214pub enum DeliveryAction {
215    Ignore,
216    CallHandler,
217    Terminate,
218    CoreDump,
219    Stop,
220    Continue,
221}
222
223impl DeliveryAction {
224    /// Returns whether the target task must be interrupted to execute the action.
225    ///
226    /// The task is not interrupted for the Continue action, nor for a signal that is ignored by
227    /// default. A signal explicitly ignored with `SIG_IGN` does interrupt: it is only queued when a
228    /// task blocks it or is ptraced, and both of those cases need the task to wake up.
229    pub fn must_interrupt(&self, sigaction: Option<sigaction_t>) -> bool {
230        match *self {
231            Self::Continue => false,
232            Self::Ignore => sigaction.map_or(false, |sa| sa.sa_handler == SIG_IGN),
233            _ => true,
234        }
235    }
236}
237
238pub fn action_for_signal(siginfo: &SignalInfo, sigaction: sigaction_t) -> DeliveryAction {
239    let handler = if siginfo.force && sigaction.sa_handler == SIG_IGN {
240        SIG_DFL
241    } else {
242        sigaction.sa_handler
243    };
244    match handler {
245        SIG_DFL => match siginfo.signal {
246            SIGCHLD | SIGURG | SIGWINCH => DeliveryAction::Ignore,
247            sig if sig.is_real_time() => DeliveryAction::Ignore,
248            SIGHUP | SIGINT | SIGKILL | SIGPIPE | SIGALRM | SIGTERM | SIGUSR1 | SIGUSR2
249            | SIGPROF | SIGVTALRM | SIGSTKFLT | SIGIO | SIGPWR => DeliveryAction::Terminate,
250            SIGQUIT | SIGILL | SIGABRT | SIGFPE | SIGSEGV | SIGBUS | SIGSYS | SIGTRAP | SIGXCPU
251            | SIGXFSZ => DeliveryAction::CoreDump,
252            SIGSTOP | SIGTSTP | SIGTTIN | SIGTTOU => DeliveryAction::Stop,
253            SIGCONT => DeliveryAction::Continue,
254            _ => panic!("Unknown signal"),
255        },
256        SIG_IGN => DeliveryAction::Ignore,
257        _ => DeliveryAction::CallHandler,
258    }
259}
260
261/// Dequeues and handles a pending signal for `current_task`.
262pub fn dequeue_signal(current_task: &mut CurrentTask) {
263    let &mut CurrentTask { ref task, ref mut thread_state, .. } = current_task;
264    if !task.should_check_for_pending_signals() {
265        return;
266    }
267
268    let mut task_state = task.write();
269    // This code is occasionally executed as the task is stopping. Stopping /
270    // stopped threads should not get signals.
271    if task.load_stopped().is_stopping_or_stopped() {
272        return;
273    }
274
275    // If there is a kernel signal needs to handle, deliver the signal right away.
276    let kernel_signal = task_state.take_kernel_signal();
277    let siginfo = if kernel_signal.is_some() { None } else { task_state.take_any_signal() };
278    prepare_to_restart_syscall(
279        thread_state,
280        siginfo.as_ref().map(|siginfo| task.thread_group().signal_actions.get(siginfo.signal)),
281    );
282
283    if let Some(ref siginfo) = siginfo {
284        if task_state.ptrace_on_signal_consume() && siginfo.signal != SIGKILL {
285            // Indicate we will be stopping for ptrace at the next opportunity.
286            // Whether you actually deliver the signal is now up to ptrace, so
287            // we can return.
288            task_state.set_stopped(
289                StopState::SignalDeliveryStopping,
290                Some(siginfo.clone()),
291                None,
292                None,
293            );
294            return;
295        }
296    }
297
298    // A syscall may have been waiting with a temporary mask which should be used to dequeue the
299    // signal, but after the signal has been dequeued the old mask should be restored.
300    task_state.restore_signal_mask();
301
302    if let Some(kernel_signal) = kernel_signal {
303        let KernelSignal::Freeze(waiter) = kernel_signal;
304        drop(task_state);
305
306        waiter.freeze(current_task);
307    } else if let Some(ref siginfo) = siginfo {
308        if let SignalDetail::Timer { timer } = &siginfo.detail {
309            timer.on_signal_delivered();
310        }
311        if let Some(status) = deliver_signal(
312            &task,
313            current_task.thread_state.arch_width(),
314            task_state,
315            siginfo.clone(),
316            &mut current_task.thread_state.registers,
317            &current_task.thread_state.extended_pstate,
318            None,
319        ) {
320            current_task.kill_thread_group(status);
321        }
322    };
323}
324
325fn deliver_signal(
326    task: &Task,
327    arch_width: ArchWidth,
328    mut task_state: TaskWriteGuard<'_>,
329    mut siginfo: SignalInfo,
330    registers: &mut RegisterState<RegisterStorageEnum>,
331    extended_pstate: &ArchExtendedPstateStorage,
332    restricted_exception: Option<zx::ExceptionReport>,
333) -> Option<ExitStatus> {
334    loop {
335        // Ignore unforced signals that `init` (PID 1) does not handle, such as a `SIGSTOP` that
336        // was queued for signal-delivery-stop while `init` was ptraced and later dequeued after
337        // the tracer detached.
338        // Note: This differs from Linux, which only filters catchable signals at delivery time
339        // (because it does not retain whether a queued `SIGSTOP` was forced) and therefore stops
340        // `init` if a `SIGSTOP` queued while ptraced is dequeued after detach.
341        if task.thread_group().ignores_signal(&siginfo) {
342            return None;
343        }
344        let signal = siginfo.signal;
345        let mut sigaction = task.thread_group().signal_actions.get(signal);
346        let masked_signals = task_state.signal_mask();
347        // From the `signal.7` man page:
348        //
349        // If a process is ignoring a signal that is generated as a consequence of
350        // a hardware exception (e.g., SIGSEGV, SIGBUS, SIGFPE, SIGILL), or is
351        // blocking the signal, the behavior is undefined, unless the signal was
352        // generated by kill(2), sigqueue(3), or raise(3).
353        //
354        // Linux behaviour is to unmask the signal and reset its disposition to
355        // `SIG_DFL`.
356        if siginfo.force && (masked_signals.has_signal(signal) || sigaction.sa_handler == SIG_IGN) {
357            task_state.set_signal_mask(masked_signals & !SigSet::from(signal));
358            sigaction = sigaction_t::default();
359            task.thread_group().signal_actions.set(signal, sigaction);
360        }
361
362        let action = action_for_signal(&siginfo, sigaction);
363        log_trace!("handling signal {:?} with action {:?}", siginfo, action);
364        match action {
365            DeliveryAction::Ignore => {}
366            DeliveryAction::CallHandler => {
367                // Reset the signal handler before dispatching if `SA_RESETHAND` was set, so the
368                // disposition is restored to `SIG_DFL` even if frame setup or handler entry faults.
369                if sigaction.sa_flags & (SA_RESETHAND as u64) != 0 {
370                    let new_sigaction = sigaction_t {
371                        sa_handler: SIG_DFL,
372                        sa_flags: sigaction.sa_flags & !(SA_RESETHAND as u64),
373                        ..sigaction
374                    };
375                    task.thread_group().signal_actions.set(signal, new_sigaction);
376                }
377                let core_dump_during_shutdown = task.kernel().is_shutting_down()
378                    && action_for_signal(&siginfo, sigaction_t::default())
379                        == DeliveryAction::CoreDump;
380                if let Err(err) = dispatch_signal_handler(
381                    task,
382                    arch_width,
383                    registers,
384                    extended_pstate,
385                    task_state.signals_mut(),
386                    siginfo,
387                    sigaction,
388                ) {
389                    log_warn!("failed to deliver signal {:?}: {:?}", signal, err);
390                    if signal == SIGSEGV {
391                        task.thread_group().signal_actions.set(SIGSEGV, sigaction_t::default());
392                    }
393                    siginfo = SignalInfo::forced(SIGSEGV);
394                    continue;
395                }
396                if core_dump_during_shutdown {
397                    task_state.set_flags(TaskFlags::IN_CORE_DUMP_SIGNAL_HANDLER, true);
398                }
399            }
400            DeliveryAction::Terminate => {
401                // Release the signals lock. [`ThreadGroup::exit`] sends signals to threads which
402                // will include this one and cause a deadlock re-acquiring the signals lock.
403                drop(task_state);
404                return Some(ExitStatus::Kill(siginfo));
405            }
406            DeliveryAction::CoreDump => {
407                task_state.set_flags(TaskFlags::DUMP_ON_EXIT, true);
408                drop(task_state);
409                if let Some(exception) = restricted_exception {
410                    log_info!(
411                        registers:?=registers,
412                        exception:?=exception;
413                        // LINT.IfChange(restricted_mode_core_dump_tefmo)
414                        "Restricted mode exception caused core dump",
415                        // LINT.ThenChange(//tools/testing/tefmocheck/string_in_log_check.go:restricted_mode_core_dump_tefmo)
416                    );
417                    if let SignalDetail::SigFault { addr } = siginfo.detail {
418                        if let Ok(mm) = task.mm() {
419                            mm.log_memory_map(task, UserAddress::from(addr));
420                        }
421                    }
422                }
423                return Some(ExitStatus::CoreDump(siginfo));
424            }
425            DeliveryAction::Stop => {
426                drop(task_state);
427                task.thread_group().set_stopped(StopState::GroupStopping, Some(siginfo), false);
428            }
429            DeliveryAction::Continue => {
430                // Nothing to do. Effect already happened when the signal was raised.
431            }
432        };
433        break;
434    }
435    None
436}
437
438/// Delivers a synchronous kernel signal ([`SignalInfo`]) immediately, unmasking it and resetting
439/// its disposition to [`SIG_DFL`] if it is currently blocked or [`SIG_IGN`].
440///
441/// If `restricted_exception` is [`Some`], the [`zx::ExceptionReport`] and task memory map are
442/// logged when the signal results in a core dump.
443pub fn force_signal(
444    current_task: &mut CurrentTask,
445    mut siginfo: SignalInfo,
446    restricted_exception: Option<zx::ExceptionReport>,
447) {
448    siginfo.force = true;
449    let mut task_state = current_task.task.write();
450    if task_state.ptrace_on_signal_consume() && siginfo.signal != SIGKILL {
451        task_state.set_stopped(
452            StopState::SignalDeliveryStopping,
453            Some(siginfo),
454            Some(current_task),
455            /* event = */ None,
456        );
457        return;
458    }
459
460    if let Some(status) = deliver_signal(
461        &current_task.task,
462        current_task.thread_state.arch_width(),
463        task_state,
464        siginfo,
465        &mut current_task.thread_state.registers,
466        &current_task.thread_state.extended_pstate,
467        restricted_exception,
468    ) {
469        current_task.kill_thread_group(status);
470    }
471}
472
473/// Prepares `current` state to execute the signal handler stored in `action`.
474///
475/// This function stores the state required to restore after the signal handler on the stack.
476fn dispatch_signal_handler(
477    task: &Task,
478    arch_width: ArchWidth,
479    registers: &mut RegisterState<RegisterStorageEnum>,
480    extended_pstate: &ArchExtendedPstateStorage,
481    signal_state: &mut SignalState,
482    siginfo: SignalInfo,
483    action: sigaction_t,
484) -> Result<(), Errno> {
485    let stack_pointer_register = registers.stack_pointer_register();
486    let on_sigaltstack = signal_state.alt_stack.is_some_and(|sigaltstack| {
487        sigaltstack_contains_pointer(&sigaltstack, stack_pointer_register)
488    });
489    let main_stack = stack_pointer_register.checked_sub(RED_ZONE_SIZE);
490    let stack_bottom = if (action.sa_flags & SA_ONSTACK as u64) != 0 {
491        match signal_state.alt_stack {
492            Some(sigaltstack) => {
493                // Only install the sigaltstack if the stack pointer is not already in it.
494                if on_sigaltstack {
495                    main_stack
496                } else {
497                    // Since the stack grows down, the size is added to the ss_sp when
498                    // calculating the "bottom" of the stack.
499                    // Use the main stack if sigaltstack overflows.
500                    sigaltstack
501                        .ss_sp
502                        .addr
503                        .checked_add(sigaltstack.ss_size)
504                        .map(|sp| sp as u64)
505                        .or(main_stack)
506                }
507            }
508            None => main_stack,
509        }
510    } else {
511        main_stack
512    }
513    .ok_or_else(|| errno!(EINVAL))?;
514
515    let stack_pointer =
516        align_stack_pointer(stack_bottom.checked_sub(SIG_STACK_SIZE as u64).ok_or_else(|| {
517            errno!(
518                EINVAL,
519                format!(
520                    "Subtracting SIG_STACK_SIZE ({}) from stack bottom ({}) overflowed",
521                    SIG_STACK_SIZE, stack_bottom
522                )
523            )
524        })?);
525
526    // Check that if the stack pointer is inside altstack, the entire signal stack is inside
527    // altstack.
528    if let Some(alt_stack) = signal_state.alt_stack {
529        if sigaltstack_contains_pointer(&alt_stack, stack_pointer)
530            != (on_sigaltstack || sigaltstack_contains_pointer(&alt_stack, stack_bottom))
531        {
532            return error!(EINVAL);
533        }
534    }
535
536    let signal_stack_frame = SignalStackFrame::new(
537        task,
538        arch_width,
539        registers,
540        extended_pstate,
541        signal_state,
542        &siginfo,
543        action,
544        UserAddress::from(stack_pointer),
545    )?;
546
547    // Write the signal stack frame at the updated stack pointer.
548    task.write_memory(UserAddress::from(stack_pointer), signal_stack_frame.as_bytes())?;
549
550    let mut mask: SigSet = action.sa_mask.into();
551    if action.sa_flags & (SA_NODEFER as u64) == 0 {
552        mask = mask | siginfo.signal.into();
553    }
554
555    // Preserve the existing mask when handling a nested signal
556    signal_state.set_mask(mask | signal_state.mask());
557
558    registers.set_stack_pointer_register(stack_pointer);
559    registers.set_arg0_register(siginfo.signal.number() as u64);
560    if (action.sa_flags & SA_SIGINFO as u64) != 0 {
561        registers.set_arg1_register(
562            stack_pointer + memoffset::offset_of!(SignalStackFrame, siginfo_bytes) as u64,
563        );
564        registers.set_arg2_register(
565            stack_pointer + memoffset::offset_of!(SignalStackFrame, context) as u64,
566        );
567    }
568    registers.set_instruction_pointer_register(action.sa_handler.addr);
569    registers.reset_flags(); // TODO(https://fxbug.dev/413070731): Verify and update the logic in resetting the flags.
570
571    Ok(())
572}
573
574pub fn restore_from_signal_handler(current_task: &mut CurrentTask) -> Result<(), Errno> {
575    // Read the signal stack frame from memory.
576    let signal_frame_address = UserAddress::from(align_stack_pointer(
577        current_task.thread_state.registers.stack_pointer_register(),
578    ));
579    let signal_stack_bytes =
580        current_task.read_memory_to_array::<SIG_STACK_SIZE>(signal_frame_address)?;
581
582    // Grab the registers state from the stack frame.
583    let signal_stack_frame = SignalStackFrame::from_bytes(signal_stack_bytes);
584    restore_registers(current_task, &signal_stack_frame, signal_frame_address)?;
585
586    // Restore the stored signal mask.
587    let mut task_state = current_task.write();
588    task_state.set_signal_mask(signal_stack_frame.get_signal_mask(current_task.is_arch32()));
589    task_state.set_flags(TaskFlags::IN_CORE_DUMP_SIGNAL_HANDLER, false);
590
591    Ok(())
592}
593
594/// Maybe adjust a task's registers to restart a syscall once the task switches back to userspace,
595/// based on whether the task previously had a syscall return with an error code indicating that a
596/// restart was required.
597pub fn prepare_to_restart_syscall(
598    thread_state: &mut ThreadState<RegisterStorageEnum>,
599    sigaction: Option<sigaction_t>,
600) {
601    // Taking the value ensures each syscall is only considered for restart once.
602    let Some(err) = thread_state.restart_code.take() else {
603        // Don't interact with register state at all unless other kernel code indicates that we may
604        // need to restart.
605        return;
606    };
607
608    if err.should_restart(sigaction) {
609        // This error code is returned for system calls that need restart_syscall() to adjust time
610        // related arguments when the syscall is restarted. Other syscall restarts can be dispatched
611        // directly to the original syscall implementation.
612        if err == ERESTART_RESTARTBLOCK {
613            thread_state.registers.prepare_for_custom_restart();
614        } else {
615            thread_state.registers.restore_original_return_register();
616        }
617
618        // TODO(https://fxbug.dev/388051291) figure out whether Linux relies on registers here
619        thread_state.registers.rewind_syscall_instruction();
620    } else {
621        thread_state.registers.set_return_register(EINTR.return_value());
622    }
623}
624
625pub fn sys_restart_syscall(current_task: &mut CurrentTask) -> Result<SyscallResult, Errno> {
626    match current_task.thread_state.syscall_restart_func.take() {
627        Some(f) => f(current_task),
628        None => {
629            // This may indicate a bug where a syscall returns ERESTART_RESTARTBLOCK without
630            // setting a restart func. But it can also be triggered by userspace, e.g. by directly
631            // calling restart_syscall or injecting an ERESTART_RESTARTBLOCK error through ptrace.
632            log_warn!("restart_syscall called, but nothing to restart");
633            error!(EINTR)
634        }
635    }
636}
637
638/// Test utilities for signal handling.
639#[cfg(test)]
640pub(crate) mod testing {
641    use super::*;
642    use crate::testing::AutoReleasableTask;
643    use std::ops::DerefMut as _;
644
645    pub(crate) fn dequeue_signal_for_test(current_task: &mut AutoReleasableTask) {
646        dequeue_signal(current_task.deref_mut());
647    }
648}