blob: 4303b04aa757f44a4597cf3a8d1be12c187c274c [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::logging::strace;
use crate::signals::*;
use crate::task::*;
use crate::types::*;
use fuchsia_zircon as zx;
/// The size of the red zone.
///
/// From the AMD64 ABI:
/// > The 128-byte area beyond the location pointed to
/// > by %rsp is considered to be reserved and shall not be modified by signal or
/// > interrupt handlers. Therefore, functions may use this area for temporary
/// > data that is not needed across function calls. In particular, leaf functions
/// > may use this area for their entire stack frame, rather than adjusting the
/// > stack pointer in the prologue and epilogue. This area is known as the red
/// > zone.
#[cfg(target_arch = "x86_64")]
const RED_ZONE_SIZE: u64 = 128;
/// The size of the syscall instruction in bytes.
#[cfg(target_arch = "x86_64")]
const SYSCALL_INSTRUCTION_SIZE_BYTES: u64 = 2;
/// A `SignalStackFrame` contains all the state that is stored on the stack prior
/// to executing a signal handler.
///
/// The ordering of the fields is significant, as it is part of the syscall ABI. In particular,
/// restorer_address must be the first field, since that is where the signal handler will return
/// after it finishes executing.
#[repr(C)]
#[cfg(target_arch = "x86_64")]
struct SignalStackFrame {
/// The address of the signal handler function.
///
/// Must be the first field, to be positioned to serve as the return address.
restorer_address: u64,
/// Information about the signal.
siginfo_bytes: [u8; std::mem::size_of::<siginfo_t>()],
/// The state of the thread at the time the signal was handled.
context: ucontext,
/// The FPU state. Must be immediately after the ucontext field, since Bionic considers this to
/// be part of the ucontext for some reason.
fpstate: _fpstate_64,
}
const SIG_STACK_SIZE: usize = std::mem::size_of::<SignalStackFrame>();
impl SignalStackFrame {
fn new(siginfo: &SignalInfo, context: ucontext, restorer_address: u64) -> SignalStackFrame {
SignalStackFrame {
context,
siginfo_bytes: siginfo.as_siginfo_bytes(),
restorer_address,
fpstate: Default::default(),
}
}
fn as_bytes(self) -> [u8; SIG_STACK_SIZE] {
unsafe { std::mem::transmute(self) }
}
fn from_bytes(bytes: [u8; SIG_STACK_SIZE]) -> SignalStackFrame {
unsafe { std::mem::transmute(bytes) }
}
}
/// Aligns the stack pointer to be 16 byte aligned, and then misaligns it by 8 bytes.
///
/// This is done because x86-64 functions expect the stack to be misaligned by 8 bytes,
/// as if the stack was 16 byte aligned and then someone used a call instruction. This
/// is due to alignment-requiring SSE instructions.
fn misalign_stack_pointer(pointer: u64) -> u64 {
pointer - (pointer % 16 + 8)
}
/// Prepares `current` state to execute the signal handler stored in `action`.
///
/// This function stores the state required to restore after the signal handler on the stack.
// TODO(lindkvist): Honor the flags in `sa_flags`.
fn dispatch_signal_handler(
current_task: &mut CurrentTask,
signal_state: &mut SignalState,
siginfo: SignalInfo,
action: sigaction_t,
) {
let signal_stack_frame = SignalStackFrame::new(
&siginfo,
ucontext {
uc_mcontext: sigcontext {
r8: current_task.registers.r8,
r9: current_task.registers.r9,
r10: current_task.registers.r10,
r11: current_task.registers.r11,
r12: current_task.registers.r12,
r13: current_task.registers.r13,
r14: current_task.registers.r14,
r15: current_task.registers.r15,
rdi: current_task.registers.rdi,
rsi: current_task.registers.rsi,
rbp: current_task.registers.rbp,
rbx: current_task.registers.rbx,
rdx: current_task.registers.rdx,
rax: current_task.registers.rax,
rcx: current_task.registers.rcx,
rsp: current_task.registers.rsp,
rip: current_task.registers.rip,
eflags: current_task.registers.rflags,
oldmask: signal_state.mask,
..Default::default()
},
uc_stack: signal_state
.alt_stack
.map(|stack| sigaltstack {
ss_sp: stack.ss_sp.ptr() as *mut c_void,
ss_flags: stack.ss_flags as i32,
ss_size: stack.ss_size,
..Default::default()
})
.unwrap_or(sigaltstack::default()),
uc_sigmask: signal_state.mask,
..Default::default()
},
action.sa_restorer.ptr() as u64,
);
// Determine which stack pointer to use for the signal handler.
// If the signal handler is executed on the main stack, adjust the stack pointer to account for
// the red zone.
// https://en.wikipedia.org/wiki/Red_zone_%28computing%29
let mut stack_pointer = if (action.sa_flags & SA_ONSTACK as u64) != 0 {
match signal_state.alt_stack {
Some(sigaltstack) => {
// Since the stack grows down, the size is added to the ss_sp when calculating the
// "bottom" of the stack.
(sigaltstack.ss_sp.ptr() + sigaltstack.ss_size) as u64
}
None => current_task.registers.rsp - RED_ZONE_SIZE,
}
} else {
current_task.registers.rsp - RED_ZONE_SIZE
};
stack_pointer -= SIG_STACK_SIZE as u64;
stack_pointer = misalign_stack_pointer(stack_pointer);
// Write the signal stack frame at the updated stack pointer.
current_task
.mm
.write_memory(UserAddress::from(stack_pointer), &signal_stack_frame.as_bytes())
.unwrap();
signal_state.set_signal_mask(action.sa_mask);
current_task.registers.rsp = stack_pointer;
current_task.registers.rdi = siginfo.signal.number() as u64;
if (action.sa_flags & SA_SIGINFO as u64) != 0 {
current_task.registers.rsi =
stack_pointer + memoffset::offset_of!(SignalStackFrame, siginfo_bytes) as u64;
current_task.registers.rdx =
stack_pointer + memoffset::offset_of!(SignalStackFrame, context) as u64;
}
current_task.registers.rip = action.sa_handler.ptr() as u64;
}
pub fn restore_from_signal_handler(current_task: &mut CurrentTask) -> Result<(), Errno> {
// The stack pointer was intentionally misaligned, so this must be done
// again to get the correct address for the stack frame.
let signal_frame_address = misalign_stack_pointer(current_task.registers.rsp);
let mut signal_stack_bytes = [0; SIG_STACK_SIZE];
current_task
.mm
.read_memory(UserAddress::from(signal_frame_address), &mut signal_stack_bytes)?;
let signal_stack_frame = SignalStackFrame::from_bytes(signal_stack_bytes);
let uctx = &signal_stack_frame.context.uc_mcontext;
// Restore the register state from before executing the signal handler.
current_task.registers = zx::sys::zx_thread_state_general_regs_t {
r8: uctx.r8,
r9: uctx.r9,
r10: uctx.r10,
r11: uctx.r11,
r12: uctx.r12,
r13: uctx.r13,
r14: uctx.r14,
r15: uctx.r15,
rax: uctx.rax,
rbx: uctx.rbx,
rcx: uctx.rcx,
rdx: uctx.rdx,
rsi: uctx.rsi,
rdi: uctx.rdi,
rbp: uctx.rbp,
rsp: uctx.rsp,
rip: uctx.rip,
rflags: uctx.eflags,
fs_base: current_task.registers.fs_base,
gs_base: current_task.registers.gs_base,
}
.into();
current_task.write().signals.mask = signal_stack_frame.context.uc_sigmask;
Ok(())
}
pub fn send_signal(task: &Task, siginfo: SignalInfo) {
let mut task_state = task.write();
task_state.signals.enqueue(siginfo.clone());
let action_is_masked = siginfo.signal.is_in_set(task_state.signals.mask);
let action = action_for_signal(&siginfo, task.thread_group.signal_actions.get(siginfo.signal));
drop(task_state);
// SIGKILL is handled without waiting to the signal to be dequeued.
if siginfo.signal == SIGKILL {
task.thread_group.exit(ExitStatus::Kill(siginfo));
return;
}
// When receiving SIGCONT or any signal that must block the process, the task state must be updated immediately.
if siginfo.signal == SIGCONT || (!action_is_masked && action == DeliveryAction::Stop) {
let stopped = siginfo.signal != SIGCONT;
task.thread_group.set_stopped(stopped, siginfo);
}
if !action_is_masked && action.must_interrupt() {
// Wake the task. Note that any potential signal handler will be executed before
// the task returns from the suspend (from the perspective of user space).
task.interrupt(InterruptionType::Signal);
}
}
pub fn force_signal(current_task: &CurrentTask, mut siginfo: SignalInfo) {
strace!(current_task, "forced signal {:?}", siginfo);
siginfo.force = true;
send_signal(current_task, siginfo)
}
/// Represents the action to take when signal is delivered.
///
/// See https://man7.org/linux/man-pages/man7/signal.7.html.
#[derive(Debug, PartialEq)]
enum DeliveryAction {
Ignore,
CallHandler,
Terminate,
CoreDump,
Stop,
Continue,
}
impl DeliveryAction {
/// Returns whether the targe task must be interrupted to execute the action.
fn must_interrupt(&self) -> bool {
match *self {
// These actions must interrupt any blocking syscalls.
DeliveryAction::CallHandler | DeliveryAction::Terminate | DeliveryAction::CoreDump => {
true
}
_ => false,
}
}
}
fn action_for_signal(siginfo: &SignalInfo, sigaction: sigaction_t) -> DeliveryAction {
match sigaction.sa_handler {
SIG_DFL => match siginfo.signal {
SIGCHLD | SIGURG | SIGWINCH => DeliveryAction::Ignore,
sig if sig.is_real_time() => DeliveryAction::Ignore,
SIGHUP | SIGINT | SIGKILL | SIGPIPE | SIGALRM | SIGTERM | SIGUSR1 | SIGUSR2
| SIGPROF | SIGVTALRM | SIGSTKFLT | SIGIO | SIGPWR => DeliveryAction::Terminate,
SIGQUIT | SIGILL | SIGABRT | SIGFPE | SIGSEGV | SIGBUS | SIGSYS | SIGTRAP | SIGXCPU
| SIGXFSZ => DeliveryAction::CoreDump,
SIGSTOP | SIGTSTP | SIGTTIN | SIGTTOU => DeliveryAction::Stop,
SIGCONT => DeliveryAction::Continue,
_ => panic!("Unknown signal"),
},
SIG_IGN => DeliveryAction::Ignore,
_ => DeliveryAction::CallHandler,
}
}
/// Adjusts a task's registers to restart a syscall once the task switches back to userspace.
///
/// This only happens if a syscall was interrupted and returned `-ERESTARTSYS`, and if the signal
/// action that interrupted the syscall had the `SA_RESTART` flag set.
fn prepare_to_restart_syscall(current_task: &mut CurrentTask, sigaction: &sigaction_t) {
if current_task.registers.rax != (-ERESTARTSYS.value()) as u64 {
// The syscall did not request to be restarted.
return;
}
if (sigaction.sa_flags & SA_RESTART as u64) == 0 {
// The signal action does not support restarts. The syscall should return -EINTR.
current_task.registers.rax = (-EINTR.value()) as u64;
return;
}
// The syscall should be restarted. Reload the original `rax` (syscall number) into `rax`,
// and backup the instruction pointer to the `syscall` instruction.
current_task.registers.rax = current_task.registers.orig_rax;
current_task.registers.rip -= SYSCALL_INSTRUCTION_SIZE_BYTES;
}
/// Dequeues and handles a pending signal for `current_task`.
pub fn dequeue_signal(current_task: &mut CurrentTask) {
let task = current_task.task_arc_clone();
let mut task_state = task.write();
let mask = task_state.signals.mask;
if let Some(siginfo) =
task_state.signals.take_next_where(|sig| !sig.signal.is_in_set(mask) || sig.force)
{
let sigaction = task.thread_group.signal_actions.get(siginfo.signal);
prepare_to_restart_syscall(current_task, &sigaction);
match action_for_signal(&siginfo, sigaction) {
DeliveryAction::CallHandler => {
dispatch_signal_handler(current_task, &mut task_state.signals, siginfo, sigaction);
}
DeliveryAction::Terminate => {
// Release the signals lock. [`ThreadGroup::exit`] sends signals to threads which
// will include this one and cause a deadlock re-acquiring the signals lock.
drop(task_state);
current_task.thread_group.exit(ExitStatus::Kill(siginfo));
}
DeliveryAction::CoreDump => {
// TODO(tbodt): Trigger crashsvc somehow
drop(task_state);
current_task.thread_group.exit(ExitStatus::CoreDump(siginfo));
}
DeliveryAction::Ignore => {}
DeliveryAction::Continue | DeliveryAction::Stop => {
// Nothing to do. Effect already happened when the signal was raised.
}
};
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::mm::{DesiredAddress, MappingOptions};
use crate::testing::*;
const SYSCALL_INSTRUCTION_ADDRESS: UserAddress = UserAddress::from(100);
const SYSCALL_NUMBER: u64 = 42;
const SYSCALL_ARGS: (u64, u64, u64, u64, u64, u64) = (20, 21, 22, 23, 24, 25);
const SA_RESTORER_ADDRESS: UserAddress = UserAddress::from(0xDEADBEEF);
const SA_HANDLER_ADDRESS: UserAddress = UserAddress::from(0x00BADDAD);
const SYSCALL2_INSTRUCTION_ADDRESS: UserAddress = UserAddress::from(200);
const SYSCALL2_NUMBER: u64 = 84;
const SYSCALL2_ARGS: (u64, u64, u64, u64, u64, u64) = (30, 31, 32, 33, 34, 35);
const SA_HANDLER2_ADDRESS: UserAddress = UserAddress::from(0xBADDAD00);
#[cfg(target_arch = "x86_64")]
#[::fuchsia::test]
fn syscall_restart_adjusts_instruction_pointer_and_rax() {
let (_kernel, mut current_task) = create_kernel_and_task_with_stack();
// Register the signal action.
current_task.thread_group.signal_actions.set(
SIGUSR1,
sigaction_t {
sa_flags: (SA_RESTORER | SA_RESTART | SA_SIGINFO) as u64,
sa_handler: SA_HANDLER_ADDRESS,
sa_restorer: SA_RESTORER_ADDRESS,
..sigaction_t::default()
},
);
// Simulate a syscall that should be restarted by setting up the register state to what it
// was after the interrupted syscall. `rax` should have the return value (-ERESTARTSYS);
// `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9`, should be the syscall arguments;
// `orig_rax` should hold the syscall number;
// and the instruction pointer should be 2 bytes after the syscall instruction.
current_task.registers.rax = -ERESTARTSYS.value() as u64;
current_task.registers.rdi = SYSCALL_ARGS.0;
current_task.registers.rsi = SYSCALL_ARGS.1;
current_task.registers.rdx = SYSCALL_ARGS.2;
current_task.registers.r10 = SYSCALL_ARGS.3;
current_task.registers.r8 = SYSCALL_ARGS.4;
current_task.registers.r9 = SYSCALL_ARGS.5;
current_task.registers.orig_rax = SYSCALL_NUMBER;
current_task.registers.rip = (SYSCALL_INSTRUCTION_ADDRESS + 2u64).ptr() as u64;
// Queue the signal that interrupted the syscall.
current_task.write().signals.enqueue(SignalInfo::new(SIGUSR1, SI_USER, SignalDetail::None));
// Process the signal.
dequeue_signal(&mut current_task);
// The instruction pointer should have changed to the signal handling address.
assert_eq!(current_task.registers.rip, SA_HANDLER_ADDRESS.ptr() as u64);
// The syscall arguments should be overwritten with signal handling args.
assert_ne!(current_task.registers.rdi, SYSCALL_ARGS.0);
assert_ne!(current_task.registers.rsi, SYSCALL_ARGS.1);
assert_ne!(current_task.registers.rdx, SYSCALL_ARGS.2);
// Now we assume that execution of the signal handler completed with a call to
// `sys_rt_sigreturn`, which would set `rax` to that syscall number.
current_task.registers.rax = __NR_rt_sigreturn as u64;
current_task.registers.rsp += 8; // The stack was popped returning from the signal handler.
restore_from_signal_handler(&mut current_task).expect("failed to restore state");
// The state of the task is now such that when switching back to userspace, the instruction
// pointer will point at the original syscall instruction, with the arguments correctly
// restored into the registers.
assert_eq!(current_task.registers.rax, SYSCALL_NUMBER);
assert_eq!(current_task.registers.rdi, SYSCALL_ARGS.0);
assert_eq!(current_task.registers.rsi, SYSCALL_ARGS.1);
assert_eq!(current_task.registers.rdx, SYSCALL_ARGS.2);
assert_eq!(current_task.registers.r10, SYSCALL_ARGS.3);
assert_eq!(current_task.registers.r8, SYSCALL_ARGS.4);
assert_eq!(current_task.registers.r9, SYSCALL_ARGS.5);
assert_eq!(current_task.registers.rip, SYSCALL_INSTRUCTION_ADDRESS.ptr() as u64);
}
#[cfg(target_arch = "x86_64")]
#[::fuchsia::test]
fn syscall_nested_restart() {
let (_kernel, mut current_task) = create_kernel_and_task_with_stack();
// Register the signal actions.
current_task.thread_group.signal_actions.set(
SIGUSR1,
sigaction_t {
sa_flags: (SA_RESTORER | SA_RESTART | SA_SIGINFO) as u64,
sa_handler: SA_HANDLER_ADDRESS,
sa_restorer: SA_RESTORER_ADDRESS,
..sigaction_t::default()
},
);
current_task.thread_group.signal_actions.set(
SIGUSR2,
sigaction_t {
sa_flags: (SA_RESTORER | SA_RESTART | SA_SIGINFO) as u64,
sa_handler: SA_HANDLER2_ADDRESS,
sa_restorer: SA_RESTORER_ADDRESS,
..sigaction_t::default()
},
);
// Simulate a syscall that should be restarted by setting up the register state to what it
// was after the interrupted syscall. `rax` should have the return value (-ERESTARTSYS);
// `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9`, should be the syscall arguments;
// `orig_rax` should hold the syscall number;
// and the instruction pointer should be 2 bytes after the syscall instruction.
current_task.registers.rax = -ERESTARTSYS.value() as u64;
current_task.registers.rdi = SYSCALL_ARGS.0;
current_task.registers.rsi = SYSCALL_ARGS.1;
current_task.registers.rdx = SYSCALL_ARGS.2;
current_task.registers.r10 = SYSCALL_ARGS.3;
current_task.registers.r8 = SYSCALL_ARGS.4;
current_task.registers.r9 = SYSCALL_ARGS.5;
current_task.registers.orig_rax = SYSCALL_NUMBER;
current_task.registers.rip = (SYSCALL_INSTRUCTION_ADDRESS + 2u64).ptr() as u64;
// Queue the signal that interrupted the syscall.
current_task.write().signals.enqueue(SignalInfo::new(SIGUSR1, SI_USER, SignalDetail::None));
// Process the signal.
dequeue_signal(&mut current_task);
// The instruction pointer should have changed to the signal handling address.
assert_eq!(current_task.registers.rip, SA_HANDLER_ADDRESS.ptr() as u64);
// The syscall arguments should be overwritten with signal handling args.
assert_ne!(current_task.registers.rdi, SYSCALL_ARGS.0);
assert_ne!(current_task.registers.rsi, SYSCALL_ARGS.1);
assert_ne!(current_task.registers.rdx, SYSCALL_ARGS.2);
// Simulate another syscall being interrupted.
current_task.registers.rax = -ERESTARTSYS.value() as u64;
current_task.registers.rdi = SYSCALL2_ARGS.0;
current_task.registers.rsi = SYSCALL2_ARGS.1;
current_task.registers.rdx = SYSCALL2_ARGS.2;
current_task.registers.r10 = SYSCALL2_ARGS.3;
current_task.registers.r8 = SYSCALL2_ARGS.4;
current_task.registers.r9 = SYSCALL2_ARGS.5;
current_task.registers.orig_rax = SYSCALL2_NUMBER;
current_task.registers.rip = (SYSCALL2_INSTRUCTION_ADDRESS + 2u64).ptr() as u64;
// Queue the signal that interrupted the syscall.
current_task.write().signals.enqueue(SignalInfo::new(SIGUSR2, SI_USER, SignalDetail::None));
// Process the signal.
dequeue_signal(&mut current_task);
// The instruction pointer should have changed to the signal handling address.
assert_eq!(current_task.registers.rip, SA_HANDLER2_ADDRESS.ptr() as u64);
// The syscall arguments should be overwritten with signal handling args.
assert_ne!(current_task.registers.rdi, SYSCALL2_ARGS.0);
assert_ne!(current_task.registers.rsi, SYSCALL2_ARGS.1);
assert_ne!(current_task.registers.rdx, SYSCALL2_ARGS.2);
// Now we assume that execution of the second signal handler completed with a call to
// `sys_rt_sigreturn`, which would set `rax` to that syscall number.
current_task.registers.rax = __NR_rt_sigreturn as u64;
current_task.registers.rsp += 8; // The stack was popped returning from the signal handler.
restore_from_signal_handler(&mut current_task).expect("failed to restore state");
// The state of the task is now such that when switching back to userspace, the instruction
// pointer will point at the original syscall instruction, with the arguments correctly
// restored into the registers.
assert_eq!(current_task.registers.rax, SYSCALL2_NUMBER);
assert_eq!(current_task.registers.rdi, SYSCALL2_ARGS.0);
assert_eq!(current_task.registers.rsi, SYSCALL2_ARGS.1);
assert_eq!(current_task.registers.rdx, SYSCALL2_ARGS.2);
assert_eq!(current_task.registers.r10, SYSCALL2_ARGS.3);
assert_eq!(current_task.registers.r8, SYSCALL2_ARGS.4);
assert_eq!(current_task.registers.r9, SYSCALL2_ARGS.5);
assert_eq!(current_task.registers.rip, SYSCALL2_INSTRUCTION_ADDRESS.ptr() as u64);
// Now we assume that execution of the first signal handler completed with a call to
// `sys_rt_sigreturn`, which would set `rax` to that syscall number.
current_task.registers.rax = __NR_rt_sigreturn as u64;
current_task.registers.rsp += 8; // The stack was popped returning from the signal handler.
restore_from_signal_handler(&mut current_task).expect("failed to restore state");
// The state of the task is now such that when switching back to userspace, the instruction
// pointer will point at the original syscall instruction, with the arguments correctly
// restored into the registers.
assert_eq!(current_task.registers.rax, SYSCALL_NUMBER);
assert_eq!(current_task.registers.rdi, SYSCALL_ARGS.0);
assert_eq!(current_task.registers.rsi, SYSCALL_ARGS.1);
assert_eq!(current_task.registers.rdx, SYSCALL_ARGS.2);
assert_eq!(current_task.registers.r10, SYSCALL_ARGS.3);
assert_eq!(current_task.registers.r8, SYSCALL_ARGS.4);
assert_eq!(current_task.registers.r9, SYSCALL_ARGS.5);
assert_eq!(current_task.registers.rip, SYSCALL_INSTRUCTION_ADDRESS.ptr() as u64);
}
#[cfg(target_arch = "x86_64")]
#[::fuchsia::test]
fn syscall_does_not_restart_if_signal_action_has_no_sa_restart_flag() {
let (_kernel, mut current_task) = create_kernel_and_task_with_stack();
// Register the signal action.
current_task.thread_group.signal_actions.set(
SIGUSR1,
sigaction_t {
sa_flags: (SA_RESTORER | SA_SIGINFO) as u64,
sa_handler: SA_HANDLER_ADDRESS,
sa_restorer: SA_RESTORER_ADDRESS,
..sigaction_t::default()
},
);
// Simulate a syscall that should be restarted by setting up the register state to what it
// was after the interrupted syscall. `rax` should have the return value (-ERESTARTSYS);
// `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9`, should be the syscall arguments;
// `orig_rax` should hold the syscall number;
// and the instruction pointer should be 2 bytes after the syscall instruction.
current_task.registers.rax = -ERESTARTSYS.value() as u64;
current_task.registers.rdi = SYSCALL_ARGS.0;
current_task.registers.rsi = SYSCALL_ARGS.1;
current_task.registers.rdx = SYSCALL_ARGS.2;
current_task.registers.r10 = SYSCALL_ARGS.3;
current_task.registers.r8 = SYSCALL_ARGS.4;
current_task.registers.r9 = SYSCALL_ARGS.5;
current_task.registers.orig_rax = SYSCALL_NUMBER;
current_task.registers.rip = (SYSCALL_INSTRUCTION_ADDRESS + 2u64).ptr() as u64;
// Queue the signal that interrupted the syscall.
current_task.write().signals.enqueue(SignalInfo::new(SIGUSR1, SI_USER, SignalDetail::None));
// Process the signal.
dequeue_signal(&mut current_task);
// The instruction pointer should have changed to the signal handling address.
assert_eq!(current_task.registers.rip, SA_HANDLER_ADDRESS.ptr() as u64);
// The syscall arguments should be overwritten with signal handling args.
assert_ne!(current_task.registers.rdi, SYSCALL_ARGS.0);
assert_ne!(current_task.registers.rsi, SYSCALL_ARGS.1);
assert_ne!(current_task.registers.rdx, SYSCALL_ARGS.2);
// Now we assume that execution of the signal handler completed with a call to
// `sys_rt_sigreturn`, which would set `rax` to that syscall number.
current_task.registers.rax = __NR_rt_sigreturn as u64;
current_task.registers.rsp += 8; // The stack was popped returning from the signal handler.
restore_from_signal_handler(&mut current_task).expect("failed to restore state");
// The state of the task is now such that when switching back to userspace, the instruction
// pointer will point at the original syscall instruction, with the arguments correctly
// restored into the registers.
assert_eq!(current_task.registers.rax, -EINTR.value() as u64);
assert_eq!(current_task.registers.rip, (SYSCALL_INSTRUCTION_ADDRESS + 2u64).ptr() as u64);
}
/// Creates a kernel and initial task, giving the task a stack.
fn create_kernel_and_task_with_stack() -> (Arc<Kernel>, CurrentTask) {
let (kernel, mut current_task) = create_kernel_and_task();
const STACK_SIZE: usize = 0x1000;
// Give the task a stack.
let stack_base = current_task
.mm
.map(
DesiredAddress::Hint(UserAddress::default()),
Arc::new(zx::Vmo::create(STACK_SIZE as u64).expect("failed to create stack VMO")),
0,
STACK_SIZE,
zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
MappingOptions::empty(),
None,
)
.expect("failed to map stack VMO");
current_task.registers.rsp = (stack_base + (STACK_SIZE - 8)).ptr() as u64;
(kernel, current_task)
}
}