# 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 = &current->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: ![](https://i.imgur.com/gRaUejW.png) ![](https://i.imgur.com/6Zpls5e.png)