Skip to content
Snippets Groups Projects
Verified Commit 6dd9332a authored by Jacob Lorentzon's avatar Jacob Lorentzon
Browse files

WIP: Handle spurious signals properly.

These can occur in the following scenario:

- thread 1 has blocked signal A
- thread 2 has not blocked signal A
- signal A is sent to a process with thread 1 and thread 2
- thread 1 is simultaneously unblocking signal A, and thus jumps to the
  trampoline
- thread 2 is awoken, but thread 1 won the "fetch_andn" to clear the
  signal bit.

If the signal asm is jumped to automatically after each timer interrupt,
this scenario will also be relatively common due to that.
parent ff7eace6
No related branches found
No related tags found
No related merge requests found
...@@ -351,8 +351,32 @@ __relibc_internal_sigentry_crit_first: ...@@ -351,8 +351,32 @@ __relibc_internal_sigentry_crit_first:
__relibc_internal_sigentry_crit_second: __relibc_internal_sigentry_crit_second:
jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}] jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
7: 7:
ud2 // A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled.
// Spurious signal
// restore flags
mov rax, fs:[0] // load FS base
// TODO: Use lahf/sahf rather than pushfq/popfq?
lea rsp, [rax + {tcb_sc_off} + {sc_saved_rflags}]
popfq
// restore stack
mov rsp, fs:[{tcb_sa_off} + {sa_tmp_rsp}]
// move saved RIP away from control, allowing arch_pre to save us if interrupted.
mov rax, fs:[{tcb_sc_off} + {sc_saved_rip}]
mov fs:[{tcb_sa_off} + {sa_tmp_rip}], rax
// restore regs
mov rax, fs:[{tcb_sa_off} + {sa_tmp_rax}]
mov rdx, fs:[{tcb_sa_off} + {sa_tmp_rdx}]
// Re-enable signals. This code can be interrupted after this signal, so we need to define
// 'crit_third'.
and qword ptr fs:[{tcb_sc_off} + {sc_control}], ~1
.globl __relibc_internal_sigentry_crit_third
__relibc_internal_sigentry_crit_third:
jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
"] <= [ "] <= [
inner = sym inner_c, inner = sym inner_c,
sa_tmp_rip = const offset_of!(SigArea, tmp_rip), sa_tmp_rip = const offset_of!(SigArea, tmp_rip),
...@@ -364,6 +388,7 @@ __relibc_internal_sigentry_crit_second: ...@@ -364,6 +388,7 @@ __relibc_internal_sigentry_crit_second:
sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg),
sc_saved_rip = const offset_of!(Sigcontrol, saved_ip), sc_saved_rip = const offset_of!(Sigcontrol, saved_ip),
sc_word = const offset_of!(Sigcontrol, word), sc_word = const offset_of!(Sigcontrol, word),
sc_control = const offset_of!(Sigcontrol, control_flags),
tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch),
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
pctl_off_actions = const offset_of!(SigProcControl, actions), pctl_off_actions = const offset_of!(SigProcControl, actions),
...@@ -378,13 +403,14 @@ __relibc_internal_sigentry_crit_second: ...@@ -378,13 +403,14 @@ __relibc_internal_sigentry_crit_second:
extern "C" { extern "C" {
fn __relibc_internal_sigentry_crit_first(); fn __relibc_internal_sigentry_crit_first();
fn __relibc_internal_sigentry_crit_second(); fn __relibc_internal_sigentry_crit_second();
fn __relibc_internal_sigentry_crit_third();
} }
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) {
// It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is // It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is
// almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that // almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that
// signals are disabled in the prologue of the signal trampoline, which allows us to emulate // signals are disabled in the prologue of the signal trampoline, which allows us to emulate
// atomicity inside the critical section, consisting of one instruction at 'crit_first', and // atomicity inside the critical section, consisting of one instruction at 'crit_first', one at
// one at 'crit_second', see asm. // 'crit_second', and one at 'crit_third', see asm.
if stack.regs.rip == __relibc_internal_sigentry_crit_first as usize { if stack.regs.rip == __relibc_internal_sigentry_crit_first as usize {
// Reexecute pop rsp and jump steps. This case needs to be different from the one below, // Reexecute pop rsp and jump steps. This case needs to be different from the one below,
...@@ -397,6 +423,8 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { ...@@ -397,6 +423,8 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) {
// Almost finished, just reexecute the jump before tmp_rip is overwritten by this // Almost finished, just reexecute the jump before tmp_rip is overwritten by this
// deeper-level signal. // deeper-level signal.
stack.regs.rip = area.tmp_rip; stack.regs.rip = area.tmp_rip;
} else if stack.regs.rip == __relibc_internal_sigentry_crit_third as usize {
stack.regs.rip = area.tmp_rip;
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment