# Signal
###### tags: `Linux` `Signal`
* 其他篇筆記:
* https://hackmd.io/@LJP/SkrXX8OSc
# Kernel
## 發生 signal
### `arch_do_signal_or_restart()`
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L864
```c
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
{
struct ksignal ksig;
// 取得 ksig 結構
if (has_signal && get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */
handle_signal(&ksig, regs);
return;
}
/* Did we come from a system call? */
if (syscall_get_nr(current, regs) != -1) {
/* Restart the system call - no handlers present */
switch (syscall_get_error(current, regs)) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->ax = regs->orig_ax;
regs->ip -= 2;
break;
case -ERESTART_RESTARTBLOCK:
regs->ax = get_nr_restart_syscall(regs);
regs->ip -= 2;
break;
}
}
/*
* If there's no signal to deliver, we just put the saved sigmask
* back.
*/
restore_saved_sigmask();
}
```
### `handle_signal()`
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L786
```c
static void
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
bool stepping, failed;
struct fpu *fpu = ¤t->thread.fpu;
if (v8086_mode(regs))
save_v86_state((struct kernel_vm86_regs *) regs, VM86_SIGNAL);
/* Are we from a system call? */
if (syscall_get_nr(current, regs) != -1) {
/* If so, check system call restarting.. */
switch (syscall_get_error(current, regs)) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->ax = -EINTR;
break;
case -ERESTARTSYS:
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
regs->ax = -EINTR;
break;
}
fallthrough;
case -ERESTARTNOINTR:
regs->ax = regs->orig_ax;
regs->ip -= 2;
break;
}
}
/*
* If TF is set due to a debugger (TIF_FORCED_TF), clear TF now
* so that register information in the sigcontext is correct and
* then notify the tracer before entering the signal handler.
*/
stepping = test_thread_flag(TIF_SINGLESTEP);
if (stepping)
user_disable_single_step(current);
failed = (setup_rt_frame(ksig, regs) < 0);
if (!failed) {
/*
* Clear the direction flag as per the ABI for function entry.
*
* Clear RF when entering the signal handler, because
* it might disable possible debug exception from the
* signal handler.
*
* Clear TF for the case when it wasn't set by debugger to
* avoid the recursive send_sigtrap() in SIGTRAP handler.
*/
regs->flags &= ~(X86_EFLAGS_DF|X86_EFLAGS_RF|X86_EFLAGS_TF);
/*
* Ensure the signal handler starts with the new fpu state.
*/
fpu__clear_user_states(fpu);
}
signal_setup_done(failed, ksig, stepping);
}
```
### `setup_rt_frame()`
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L763
```c
// ksig 結構決定等等的 rt_sigframe 內部要放什麼
static int
setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
{
int usig = ksig->sig;
sigset_t *set = sigmask_to_save();
compat_sigset_t *cset = (compat_sigset_t *) set;
/* Perform fixup for the pre-signal frame. */
rseq_signal_deliver(ksig, regs);
/* Set up the stack frame */
if (is_ia32_frame(ksig)) {
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
return ia32_setup_rt_frame(usig, ksig, cset, regs);
else
return ia32_setup_frame(usig, ksig, cset, regs);
} else if (is_x32_frame(ksig)) {
return x32_setup_rt_frame(ksig, cset, regs);
} else {
return __setup_rt_frame(ksig->sig, ksig, set, regs);
}
}
```
### `__setup_rt_frame()`
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L460
```c
static int __setup_rt_frame(int sig, struct ksignal *ksig,
sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
void __user *fp = NULL;
unsigned long uc_flags;
/* x86-64 should always use SA_RESTORER. */
if (!(ksig->ka.sa.sa_flags & SA_RESTORER))
return -EFAULT;
frame = get_sigframe(&ksig->ka, regs, sizeof(struct rt_sigframe), &fp);
uc_flags = frame_uc_flags(regs);
if (!user_access_begin(frame, sizeof(*frame)))
return -EFAULT;
/* Create the ucontext. */
unsafe_put_user(uc_flags, &frame->uc.uc_flags, Efault);
unsafe_put_user(0, &frame->uc.uc_link, Efault);
unsafe_save_altstack(&frame->uc.uc_stack, regs->sp, Efault);
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
// 設定 pretcode
unsafe_put_user(ksig->ka.sa.sa_restorer, &frame->pretcode, Efault);
unsafe_put_sigcontext(&frame->uc.uc_mcontext, fp, regs, set, Efault);
unsafe_put_sigmask(set, frame, Efault);
user_access_end();
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
if (copy_siginfo_to_user(&frame->info, &ksig->info))
return -EFAULT;
}
/* Set up registers for signal handler */
regs->di = sig;
/* In case the signal handler was declared without prototypes */
regs->ax = 0;
/* This also works for non SA_SIGINFO handlers because they expect the
next argument after the signal number on the stack. */
regs->si = (unsigned long)&frame->info;
regs->dx = (unsigned long)&frame->uc;
regs->ip = (unsigned long) ksig->ka.sa.sa_handler;
regs->sp = (unsigned long)frame;
/*
* Set up the CS and SS registers to run signal handlers in
* 64-bit mode, even if the handler happens to be interrupting
* 32-bit or 16-bit code.
*
* SS is subtle. In 64-bit mode, we don't need any particular
* SS descriptor, but we do need SS to be valid. It's possible
* that the old SS is entirely bogus -- this can happen if the
* signal we're trying to deliver is #GP or #SS caused by a bad
* SS value. We also have a compatibility issue here: DOSEMU
* relies on the contents of the SS register indicating the
* SS value at the time of the signal, even though that code in
* DOSEMU predates sigreturn's ability to restore SS. (DOSEMU
* avoids relying on sigreturn to restore SS; instead it uses
* a trampoline.) So we do our best: if the old SS was valid,
* we keep it. Otherwise we replace it.
*/
regs->cs = __USER_CS;
if (unlikely(regs->ss != __USER_DS))
force_valid_ss(regs);
return 0;
Efault:
user_access_end();
return -EFAULT;
}
```
## signal handler 返回 (呼叫 sigreturn)
### syscall rt_sigreturn
* https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L657
```c
SYSCALL_DEFINE0(rt_sigreturn)
{
struct pt_regs *regs = current_pt_regs();
struct rt_sigframe __user *frame;
sigset_t set;
unsigned long uc_flags;
frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
if (!access_ok(frame, sizeof(*frame)))
goto badframe;
if (__get_user(*(__u64 *)&set, (__u64 __user *)&frame->uc.uc_sigmask))
goto badframe;
if (__get_user(uc_flags, &frame->uc.uc_flags))
goto badframe;
set_current_blocked(&set);
if (!restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
goto badframe;
if (restore_altstack(&frame->uc.uc_stack))
goto badframe;
return regs->ax;
badframe:
signal_fault(regs, frame, "rt_sigreturn");
return 0;
}
```
# Libc
## `signal`
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/posix/signal.c#L29
```c
/* Set the handler for the signal SIG to HANDLER,
returning the old handler, or SIG_ERR on error. */
__sighandler_t
__bsd_signal (int sig, __sighandler_t handler)
{
struct sigaction act, oact;
/* Check signal extents to protect __sigismember. */
if (handler == SIG_ERR || sig < 1 || sig >= NSIG
|| __is_internal_signal (sig))
{
__set_errno (EINVAL);
return SIG_ERR;
}
act.sa_handler = handler;
__sigemptyset (&act.sa_mask);
__sigaddset (&act.sa_mask, sig);
act.sa_flags = __sigismember (&_sigintr, sig) ? 0 : SA_RESTART;
if (__sigaction (sig, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
weak_alias (__bsd_signal, bsd_signal)
weak_alias (__bsd_signal, signal)
weak_alias (__bsd_signal, ssignal)
```
## `__sigaction`
* https://elixir.bootlin.com/glibc/glibc-2.31/source/nptl/sigaction.c#L22
```c
int
__sigaction (int sig, const struct sigaction *act, struct sigaction *oact)
{
if (sig <= 0 || sig >= NSIG || __is_internal_signal (sig))
{
__set_errno (EINVAL);
return -1;
}
return __libc_sigaction (sig, act, oact);
}
libc_hidden_weak (__sigaction)
weak_alias (__sigaction, sigaction)
```
## `__libc_sigaction`
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/unix/sysv/linux/sigaction.c#L42
```c
/* If ACT is not NULL, change the action for SIG to *ACT.
If OACT is not NULL, put the old action for SIG in *OACT. */
int
__libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact)
{
int result;
struct kernel_sigaction kact, koact;
if (act)
{
kact.k_sa_handler = act->sa_handler;
memcpy (&kact.sa_mask, &act->sa_mask, sizeof (sigset_t));
kact.sa_flags = act->sa_flags;
SET_SA_RESTORER (&kact, act);
}
/* XXX The size argument hopefully will have to be changed to the
real size of the user-level sigset_t. */
result = INLINE_SYSCALL_CALL (rt_sigaction, sig,
act ? &kact : NULL,
oact ? &koact : NULL, STUB (act, _NSIG / 8));
if (oact && result >= 0)
{
oact->sa_handler = koact.k_sa_handler;
memcpy (&oact->sa_mask, &koact.sa_mask, sizeof (sigset_t));
oact->sa_flags = koact.sa_flags;
RESET_SA_RESTORER (oact, &koact);
}
return result;
}
libc_hidden_def (__libc_sigaction)
```
* `SET_SA_RESTORER` 設定了 sa_restorer
## `SET_SA_RESTORER`
* https://elixir.bootlin.com/glibc/glibc-2.31/source/sysdeps/unix/sysv/linux/x86_64/sigaction.c#L24
```c
extern void restore_rt (void) asm ("__restore_rt") attribute_hidden;
#define SET_SA_RESTORER(kact, act) \
(kact)->sa_flags = (act)->sa_flags | SA_RESTORER; \
(kact)->sa_restorer = &restore_rt
```
* `restore_rt` 被改標成 `__restore_rt`, 繼續看以下 macro 展開
```c
/* The return code for realtime-signals. */
RESTORE (restore_rt, __NR_rt_sigreturn)
```
* `RESTORE` 也是在同一檔案
```c
#define RESTORE(name, syscall) RESTORE2 (name, syscall)
# define RESTORE2(name, syscall) \
asm \
( \
/* `nop' for debuggers assuming `call' should not disalign the code. */ \
" nop\n" \
".align 16\n" \
".LSTART_" #name ":\n" \
" .type __" #name ",@function\n" \
"__" #name ":\n" \
" movq $" #syscall ", %rax\n" \
" syscall\n" \
".LEND_" #name ":\n" \
".section .eh_frame,\"a\",@progbits\n" \
".LSTARTFRAME_" #name ":\n" \
" .long .LENDCIE_" #name "-.LSTARTCIE_" #name "\n" \
".LSTARTCIE_" #name ":\n" \
" .long 0\n" /* CIE ID */ \
" .byte 1\n" /* Version number */ \
" .string \"zRS\"\n" /* NUL-terminated augmentation string */ \
" .uleb128 1\n" /* Code alignment factor */ \
" .sleb128 -8\n" /* Data alignment factor */ \
" .uleb128 16\n" /* Return address register column (rip) */ \
/* Augmentation value length */ \
" .uleb128 .LENDAUGMNT_" #name "-.LSTARTAUGMNT_" #name "\n" \
".LSTARTAUGMNT_" #name ":\n" \
" .byte 0x1b\n" /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */ \
".LENDAUGMNT_" #name ":\n" \
" .align " LP_SIZE "\n" \
".LENDCIE_" #name ":\n" \
" .long .LENDFDE_" #name "-.LSTARTFDE_" #name "\n" /* FDE len */ \
".LSTARTFDE_" #name ":\n" \
" .long .LSTARTFDE_" #name "-.LSTARTFRAME_" #name "\n" /* CIE */ \
/* `LSTART_' is subtracted 1 as debuggers assume a `call' here. */ \
" .long (.LSTART_" #name "-1)-.\n" /* PC-relative start addr. */ \
" .long .LEND_" #name "-(.LSTART_" #name "-1)\n" \
" .uleb128 0\n" /* FDE augmentation length */ \
do_cfa_expr \
do_expr (8 /* r8 */, oR8) \
do_expr (9 /* r9 */, oR9) \
do_expr (10 /* r10 */, oR10) \
do_expr (11 /* r11 */, oR11) \
do_expr (12 /* r12 */, oR12) \
do_expr (13 /* r13 */, oR13) \
do_expr (14 /* r14 */, oR14) \
do_expr (15 /* r15 */, oR15) \
do_expr (5 /* rdi */, oRDI) \
do_expr (4 /* rsi */, oRSI) \
do_expr (6 /* rbp */, oRBP) \
do_expr (3 /* rbx */, oRBX) \
do_expr (1 /* rdx */, oRDX) \
do_expr (0 /* rax */, oRAX) \
do_expr (2 /* rcx */, oRCX) \
do_expr (7 /* rsp */, oRSP) \
do_expr (16 /* rip */, oRIP) \
/* libgcc-4.1.1 has only `DWARF_FRAME_REGISTERS == 17'. */ \
/* do_expr (49 |* rflags *|, oEFL) */ \
/* `cs'/`ds'/`fs' are unaligned and a different size. */ \
/* gas: Error: register save offset not a multiple of 8 */ \
" .align " LP_SIZE "\n" \
".LENDFDE_" #name ":\n" \
" .previous\n" \
);
```
* 展開後:
```c
asm ( " nop\n" ".align 16\n" ".LSTART_" "restore_rt" ":\n" " .type __" "restore_rt" ",@function\n" "__" "restore_rt" ":\n" " movq $" "__NR_rt_sigreturn" ", %rax\n" " syscall\n" ".LEND_" "restore_rt" ":\n" ".section .eh_frame,\"a\",@progbits\n" ".LSTARTFRAME_" "restore_rt" ":\n" " .long .LENDCIE_" "restore_rt" "-.LSTARTCIE_" "restore_rt" "\n" ".LSTARTCIE_" "restore_rt" ":\n" " .long 0\n" " .byte 1\n" " .string \"zRS\"\n" " .uleb128 1\n" " .sleb128 -8\n" " .uleb128 16\n" " .uleb128 .LENDAUGMNT_" "restore_rt" "-.LSTARTAUGMNT_" "restore_rt" "\n" ".LSTARTAUGMNT_" "restore_rt" ":\n" " .byte 0x1b\n" ".LENDAUGMNT_" "restore_rt" ":\n" " .align " LP_SIZE "\n" ".LENDCIE_" "restore_rt" ":\n" " .long .LENDFDE_" "restore_rt" "-.LSTARTFDE_" "restore_rt" "\n" ".LSTARTFDE_" "restore_rt" ":\n" " .long .LSTARTFDE_" "restore_rt" "-.LSTARTFRAME_" "restore_rt" "\n" " .long (.LSTART_" "restore_rt" "-1)-.\n" " .long .LEND_" "restore_rt" "-(.LSTART_" "restore_rt" "-1)\n" " .uleb128 0\n" do_cfa_expr do_expr (8 , oR8) do_expr (9 , oR9) do_expr (10 , oR10) do_expr (11 , oR11) do_expr (12 , oR12) do_expr (13 , oR13) do_expr (14 , oR14) do_expr (15 , oR15) do_expr (5 , oRDI) do_expr (4 , oRSI) do_expr (6 , oRBP) do_expr (3 , oRBX) do_expr (1 , oRDX) do_expr (0 , oRAX) do_expr (2 , oRCX) do_expr (7 , oRSP) do_expr (16 , oRIP) " .align " LP_SIZE "\n" ".LENDFDE_" "restore_rt" ":\n" " .previous\n" );
```
* 整理一番:
```c
asm (
" nop\n"
".align 16\n"
".LSTART_restore_rt:\n"
" .type __restore_rt,@function\n"
"__restore_rt:\n"
" movq $__NR_rt_sigreturn, %rax\n"
" syscall\n"
".LEND_restore_rt:\n"
".section .eh_frame,\"a\",@progbits\n"
".LSTARTFRAME_restore_rt:\n"
" .long .LENDCIE_restore_rt-.LSTARTCIE_restore_rt\n"
".LSTARTCIE_restore_rt:\n"
" .long 0\n"
" .byte 1\n"
" .string \"zRS\"\n"
" .uleb128 1\n"
" .sleb128 -8\n"
" .uleb128 16\n"
" .uleb128 .LENDAUGMNT_restore_rt-.LSTARTAUGMNT_restore_rt\n"
".LSTARTAUGMNT_restore_rt:\n"
" .byte 0x1b\n"
".LENDAUGMNT_restore_rt:\n"
" .align " LP_SIZE "\n"
".LENDCIE_restore_rt:\n"
" .long .LENDFDE_restore_rt-.LSTARTFDE_restore_rt\n"
".LSTARTFDE_restore_rt:\n"
" .long .LSTARTFDE_restore_rt-.LSTARTFRAME_restore_rt\n"
" .long (.LSTART_" "restore_rt" "-1)-.\n"
" .long .LEND_restore_rt-(.LSTART_restore_rt-1)\n"
" .uleb128 0\n"
do_cfa_expr
do_expr (8 , oR8)
do_expr (9 , oR9)
do_expr (10 , oR10)
do_expr (11 , oR11)
do_expr (12 , oR12)
do_expr (13 , oR13)
do_expr (14 , oR14)
do_expr (15 , oR15)
do_expr (5 , oRDI)
do_expr (4 , oRSI)
do_expr (6 , oRBP)
do_expr (3 , oRBX)
do_expr (1 , oRDX)
do_expr (0 , oRAX)
do_expr (2 , oRCX)
do_expr (7 , oRSP)
do_expr (16 , oRIP)
" .align " LP_SIZE "\n"
".LENDFDE_" "restore_rt" ":\n"
" .previous\n"
);
```
* 能夠看見關於 `__restore_rt` 的定義:
```c
"__restore_rt:\n"
" movq $__NR_rt_sigreturn, %rax\n"
" syscall\n"
```
* 呼叫 rt_sigreturn syscall:

