Skip to content

Commit acad27f

Browse files
committed
process/signal: Signal fixes
1 parent b9f3c19 commit acad27f

9 files changed

Lines changed: 630 additions & 184 deletions

File tree

kernel/src/arch/sched.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ use super::internal;
22
use crate::{
33
memory::{VirtAddr, stack::KernelStack},
44
posix::errno::EResult,
5-
process::{signal::SignalSet, task::Task},
5+
process::task::Task,
66
};
77
use core::{fmt::Debug, mem::MaybeUninit};
88

9-
pub use internal::sched::Context;
9+
pub use internal::sched::{Context, SyscallRestart};
1010
assert_trait_impl!(TaskContext, Debug);
1111
assert_trait_impl!(Context, Default);
1212
assert_trait_impl!(Context, Clone);
@@ -97,12 +97,9 @@ pub unsafe fn jump_to_context(context: *mut Context) {
9797
/// When the handler returns, execution continues via the restorer which calls sigreturn.
9898
pub fn setup_signal_frame(
9999
context: &mut Context,
100-
handler: usize,
101-
signal: u32,
102-
mask: SignalSet,
103-
restorer: usize,
100+
delivery: &crate::process::signal::SignalDelivery,
104101
) {
105-
internal::sched::setup_signal_frame(context, handler, signal, mask, restorer);
102+
internal::sched::setup_signal_frame(context, delivery);
106103
}
107104

108105
/// Restores the original context from a signal frame on the user stack.
@@ -117,9 +114,25 @@ pub fn restore_signal_frame(context: &mut Context) {
117114
#[doc(hidden)]
118115
#[allow(unused)]
119116
mod api {
120-
use super::Context;
117+
use super::{Context, SyscallRestart};
121118

122119
fn set_return(ctx: &mut Context, val: usize, err: usize) {
123120
ctx.set_return(val, err);
124121
}
122+
123+
fn sp(ctx: &Context) -> usize {
124+
ctx.sp()
125+
}
126+
127+
fn return_error(ctx: &Context) -> usize {
128+
ctx.syscall_error()
129+
}
130+
131+
fn snapshot_syscall(ctx: &Context) -> SyscallRestart {
132+
ctx.snapshot_syscall()
133+
}
134+
135+
fn restart_syscall(ctx: &mut Context, restart: &SyscallRestart) {
136+
ctx.restart_syscall(restart);
137+
}
125138
}

kernel/src/arch/x86_64/irq.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ pub unsafe extern "C" fn amd64_syscall_stub() {
117117
extern "C" fn syscall_handler(frame: *mut Context) {
118118
unsafe {
119119
let frame = frame.as_mut().unwrap();
120+
let restart = frame.snapshot_syscall();
120121

121122
crate::syscall::dispatch(frame);
122123

123124
// Check for pending signals before returning to userspace.
124-
crate::process::signal::deliver_pending_signals(frame);
125+
signal::deliver_pending_signals(frame, Some(restart));
125126
}
126127
}
127128

@@ -257,8 +258,20 @@ unsafe extern "C" fn idt_handler(context: *mut Context) {
257258
}
258259
};
259260

261+
let code = match sig {
262+
Signal::SigIll => crate::uapi::signal::ILL_ILLOPC,
263+
Signal::SigBus => crate::uapi::signal::BUS_ADRALN,
264+
Signal::SigSegv => crate::uapi::signal::SEGV_MAPERR,
265+
_ => 0,
266+
};
267+
let info = signal::SigInfoData {
268+
code: code as i32,
269+
addr: context.rip as usize,
270+
..Default::default()
271+
};
272+
260273
let task = Scheduler::get_current();
261-
signal::send_signal_to_thread(&task, sig);
274+
signal::send_signal_info_to_thread(&task, sig, info);
262275
}
263276
// Kernel exceptions are fatal.
264277
0x00..0x20 => {
@@ -294,7 +307,7 @@ unsafe extern "C" fn idt_handler(context: *mut Context) {
294307

295308
// Check for pending signals before returning to userspace.
296309
if from_user {
297-
signal::deliver_pending_signals(context);
310+
signal::deliver_pending_signals(context, None);
298311
}
299312
}
300313

kernel/src/arch/x86_64/sched.rs

Lines changed: 146 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use crate::{
1313
posix::errno::EResult,
1414
process::{
1515
Process, State,
16-
signal::{Signal, SignalSet},
16+
signal::{Signal, SignalDelivery, SignalSet},
1717
task::Task,
1818
},
1919
sched::Scheduler,
20+
uapi::signal::{SA_ONSTACK, siginfo_t},
2021
};
2122
use alloc::boxed::Box;
2223
use core::{
@@ -75,6 +76,10 @@ impl Context {
7576
self.rax as usize
7677
}
7778

79+
pub fn syscall_error(&self) -> usize {
80+
self.rdx as usize
81+
}
82+
7883
pub fn arg0(&self) -> usize {
7984
self.rdi as usize
8085
}
@@ -103,6 +108,36 @@ impl Context {
103108
self.rax = val as _;
104109
self.rdx = err as _;
105110
}
111+
112+
pub fn sp(&self) -> usize {
113+
self.rsp as usize
114+
}
115+
116+
pub fn ip(&self) -> usize {
117+
self.rip as usize
118+
}
119+
120+
pub fn snapshot_syscall(&self) -> SyscallRestart {
121+
SyscallRestart {
122+
nr: self.rax as usize,
123+
arg2: self.rdx as usize,
124+
}
125+
}
126+
127+
pub fn restart_syscall(&mut self, restart: &SyscallRestart) {
128+
self.rip -= 2; // Length of the `syscall` instruction.
129+
self.rax = restart.nr as u64;
130+
self.rdx = restart.arg2 as u64;
131+
}
132+
}
133+
134+
/// Registers captured before a syscall is dispatched so that an interrupted
135+
/// syscall can be transparently restarted afterwards.
136+
/// The fields are whichever registers [`Context::set_return`] would clobber.
137+
#[derive(Clone, Copy, Debug)]
138+
pub struct SyscallRestart {
139+
nr: usize,
140+
arg2: usize,
106141
}
107142

108143
impl core::fmt::Debug for Context {
@@ -379,57 +414,132 @@ pub(in crate::arch) unsafe fn jump_to_context(context: *mut Context) -> ! {
379414
}
380415
}
381416

382-
/// Signal frame placed on the user stack when delivering a signal.
383-
/// The restorer function pops this frame via sigreturn.
417+
/// Saved state for `sigreturn`, written to the user stack below the rest of the signal frame.
384418
#[repr(C)]
385419
#[derive(Clone, Copy)]
386420
struct SignalFrame {
387-
/// The saved signal mask to restore after handler returns.
388421
saved_mask: u64,
389-
/// The saved user context to restore after handler returns.
390422
saved_context: Context,
391-
/// The signal number.
392-
signal_number: u32,
423+
}
424+
425+
#[repr(C)]
426+
#[derive(Clone, Copy, Default)]
427+
struct UserStack {
428+
ss_sp: u64,
429+
ss_size: u64,
430+
ss_flags: i32,
393431
_pad: u32,
394-
/// Return address: points to the restorer trampoline.
395-
restorer_ret: u64,
396432
}
397433

398-
pub(in crate::arch) fn setup_signal_frame(
399-
context: &mut Context,
400-
handler: usize,
401-
signal: u32,
402-
mask: SignalSet,
403-
restorer: usize,
404-
) {
405-
let frame_sp = (context.rsp as usize - size_of::<SignalFrame>()) & !0xF;
434+
#[repr(C)]
435+
#[derive(Clone, Copy, Default)]
436+
struct Mcontext {
437+
oldmask: u64,
438+
gregs: [u64; 16],
439+
pc: u64,
440+
pr: u64,
441+
sr: u64,
442+
gbr: u64,
443+
mach: u64,
444+
macl: u64,
445+
fpregs: [u64; 16],
446+
xfpregs: [u64; 16],
447+
fpscr: u32,
448+
fpul: u32,
449+
ownedfp: u32,
450+
}
451+
452+
#[repr(C)]
453+
#[derive(Clone, Copy)]
454+
struct Ucontext {
455+
uc_link: u64,
456+
uc_stack: UserStack,
457+
uc_mcontext: Mcontext,
458+
uc_sigmask: u64,
459+
}
460+
461+
pub(in crate::arch) fn setup_signal_frame(context: &mut Context, delivery: &SignalDelivery) {
462+
let altstack = delivery.altstack;
463+
let on_altstack = altstack.contains(context.rsp as usize);
464+
let use_altstack = delivery.flags & SA_ONSTACK != 0 && altstack.is_enabled() && !on_altstack;
465+
466+
// Top of the alternate stack if requested, otherwise below the 128-byte red zone
467+
// of the interrupted stack (System V AMD64 ABI).
468+
let base = if use_altstack {
469+
altstack.sp + altstack.size
470+
} else {
471+
context.rsp as usize - 128
472+
};
473+
474+
let align = |x: usize| x & !0xF;
475+
476+
let info_addr = align(base - size_of::<siginfo_t>());
477+
let uc_addr = align(info_addr - size_of::<Ucontext>());
478+
let sf_addr = align(uc_addr - size_of::<SignalFrame>());
479+
let ret_addr = sf_addr - 8;
480+
481+
let mut gregs = [0u64; 16];
482+
gregs[0] = context.rax;
483+
gregs[1] = context.rbx;
484+
gregs[2] = context.rcx;
485+
gregs[3] = context.rdx;
486+
gregs[4] = context.rsi;
487+
gregs[5] = context.rdi;
488+
gregs[6] = context.rbp;
489+
gregs[7] = context.rsp;
490+
gregs[8] = context.r8;
491+
gregs[9] = context.r9;
492+
gregs[10] = context.r10;
493+
gregs[11] = context.r11;
494+
gregs[12] = context.r12;
495+
gregs[13] = context.r13;
496+
gregs[14] = context.r14;
497+
gregs[15] = context.r15;
498+
499+
let ucontext = Ucontext {
500+
uc_link: 0,
501+
uc_stack: UserStack {
502+
ss_sp: altstack.sp as u64,
503+
ss_size: altstack.size as u64,
504+
ss_flags: altstack.flags,
505+
_pad: 0,
506+
},
507+
uc_mcontext: Mcontext {
508+
oldmask: delivery.old_mask.as_raw(),
509+
gregs,
510+
pc: context.rip,
511+
..Default::default()
512+
},
513+
uc_sigmask: delivery.old_mask.as_raw(),
514+
};
406515

407516
let frame = SignalFrame {
408-
saved_mask: mask.as_raw(),
517+
saved_mask: delivery.old_mask.as_raw(),
409518
saved_context: *context,
410-
signal_number: signal,
411-
_pad: 0,
412-
restorer_ret: restorer as u64,
413519
};
414520

415-
let mut ptr = UserPtr::<SignalFrame>::new(VirtAddr::new(frame_sp));
416-
if ptr.write(frame).is_none() {
417-
Process::exit(State::Signaled(Signal::SigSegv));
418-
}
419-
420-
// Place the restorer return address BELOW the signal frame so the
421-
// handler's stack grows downward away from the frame, not into it.
422-
// Also ensures RSP % 16 == 8 at handler entry (x86_64 ABI).
423-
let ret_sp = frame_sp - 8;
424-
let mut ret_ptr = UserPtr::<u64>::new(VirtAddr::new(ret_sp));
425-
if ret_ptr.write(restorer as u64).is_none() {
521+
let ok = UserPtr::<siginfo_t>::new(VirtAddr::new(info_addr))
522+
.write(delivery.info)
523+
.is_some()
524+
&& UserPtr::<Ucontext>::new(VirtAddr::new(uc_addr))
525+
.write(ucontext)
526+
.is_some()
527+
&& UserPtr::<SignalFrame>::new(VirtAddr::new(sf_addr))
528+
.write(frame)
529+
.is_some()
530+
&& UserPtr::<u64>::new(VirtAddr::new(ret_addr))
531+
.write(delivery.restorer as u64)
532+
.is_some();
533+
if !ok {
426534
Process::exit(State::Signaled(Signal::SigSegv));
427535
}
428536

429-
// Modify context to jump to the handler.
430-
context.rip = handler as u64;
431-
context.rdi = signal as u64; // First argument: signal number.
432-
context.rsp = ret_sp as u64;
537+
// Hand control to the handler.
538+
context.rip = delivery.handler as u64;
539+
context.rdi = delivery.signal as u64;
540+
context.rsi = info_addr as u64;
541+
context.rdx = uc_addr as u64;
542+
context.rsp = ret_addr as u64;
433543
context.rflags &= !consts::RFLAGS_DF;
434544
}
435545

kernel/src/memory/virt/fault.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
use crate::memory::virt::VmFlags;
12
use crate::{
23
arch,
3-
memory::{MemoryObject, PagedMemoryObject, VirtAddr, pmm::KernelAlloc, virt::VmFlags},
4-
process::signal::Signal,
4+
memory::{
5+
MemoryObject, PagedMemoryObject, VirtAddr,
6+
pmm::KernelAlloc,
7+
},
8+
process::signal::{self, SigInfoData, Signal},
59
sched::Scheduler,
610
};
711
use alloc::sync::Arc;
@@ -114,7 +118,37 @@ fn signal_or_panic(info: &PageFaultInfo) -> bool {
114118
if info.caused_by_user {
115119
// Force SIGSEGV to the faulting user process. Using force_signal ensures the signal
116120
// cannot be masked or caught in a loop (handler is reset to SIG_DFL).
117-
crate::process::signal::force_signal_to_thread(&task, Signal::SigSegv);
121+
let code = if info.page_was_present {
122+
crate::uapi::signal::SEGV_ACCERR
123+
} else {
124+
crate::uapi::signal::SEGV_MAPERR
125+
};
126+
let sig_info = SigInfoData {
127+
code: code as i32,
128+
addr: info.addr.value(),
129+
..Default::default()
130+
};
131+
let proc = task.get_process();
132+
warn!(
133+
"segfault in {} (pid {}): {} {} page at {:#x} (IP {:#x})",
134+
proc.get_name(),
135+
proc.get_pid(),
136+
if info.caused_by_write {
137+
"write to"
138+
} else if info.caused_by_fetch {
139+
"execute on"
140+
} else {
141+
"read from"
142+
},
143+
if info.page_was_present {
144+
"present"
145+
} else {
146+
"non-present"
147+
},
148+
info.addr.value(),
149+
info.ip.value(),
150+
);
151+
signal::force_signal_to_thread(&task, Signal::SigSegv, sig_info);
118152
return true; // Will be delivered on return to userspace.
119153
}
120154

kernel/src/process/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,15 @@ impl Process {
378378
}
379379
}
380380

381-
signal::notify_parent_of_child_state_change(&proc, false);
381+
let (cld_code, cld_status) = match *proc.status.lock() {
382+
State::Exited(code) => (uapi::signal::CLD_EXITED as i32, code as i32),
383+
State::Signaled(sig) if sig.default_action() == signal::DefaultAction::CoreDump => {
384+
(uapi::signal::CLD_DUMPED as i32, sig as i32)
385+
}
386+
State::Signaled(sig) => (uapi::signal::CLD_KILLED as i32, sig as i32),
387+
_ => (uapi::signal::CLD_EXITED as i32, 0),
388+
};
389+
signal::notify_parent_of_child_state_change(&proc, cld_code, cld_status);
382390

383391
drop(proc);
384392
drop(task);

0 commit comments

Comments
 (0)