📄 signal.c
字号:
/* * This must be called with current->sighand->siglock held. * * This should be the path for all ptrace stops. * We always set current->last_siginfo while stopped here. * That makes it a way to test a stopped process for * being ptrace-stopped vs being job-control-stopped. * * If we actually decide not to stop at all because the tracer is gone, * we leave nostop_code in current->exit_code. */static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info){ /* * If there is a group stop in progress, * we must participate in the bookkeeping. */ if (current->signal->group_stop_count > 0) --current->signal->group_stop_count; current->last_siginfo = info; current->exit_code = exit_code; /* Let the debugger run. */ set_current_state(TASK_TRACED); spin_unlock_irq(¤t->sighand->siglock); try_to_freeze(); read_lock(&tasklist_lock); if (may_ptrace_stop()) { do_notify_parent_cldstop(current, CLD_TRAPPED); read_unlock(&tasklist_lock); schedule(); } else { /* * By the time we got the lock, our tracer went away. * Don't stop here. */ read_unlock(&tasklist_lock); set_current_state(TASK_RUNNING); current->exit_code = nostop_code; } /* * We are back. Now reacquire the siglock before touching * last_siginfo, so that we are sure to have synchronized with * any signal-sending on another CPU that wants to examine it. */ spin_lock_irq(¤t->sighand->siglock); current->last_siginfo = NULL; /* * Queued signals ignored us while we were stopped for tracing. * So check for any that we should take before resuming user mode. * This sets TIF_SIGPENDING, but never clears it. */ recalc_sigpending_tsk(current);}void ptrace_notify(int exit_code){ siginfo_t info; BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP); memset(&info, 0, sizeof info); info.si_signo = SIGTRAP; info.si_code = exit_code; info.si_pid = current->pid; info.si_uid = current->uid; /* Let the debugger run. */ spin_lock_irq(¤t->sighand->siglock); ptrace_stop(exit_code, 0, &info); spin_unlock_irq(¤t->sighand->siglock);}static voidfinish_stop(int stop_count){ /* * If there are no other threads in the group, or if there is * a group stop in progress and we are the last to stop, * report to the parent. When ptraced, every thread reports itself. */ if (stop_count == 0 || (current->ptrace & PT_PTRACED)) { read_lock(&tasklist_lock); do_notify_parent_cldstop(current, CLD_STOPPED); read_unlock(&tasklist_lock); } do { schedule(); } while (try_to_freeze()); /* * Now we don't run again until continued. */ current->exit_code = 0;}/* * This performs the stopping for SIGSTOP and other stop signals. * We have to stop all threads in the thread group. * Returns nonzero if we've actually stopped and released the siglock. * Returns zero if we didn't stop and still hold the siglock. */static int do_signal_stop(int signr){ struct signal_struct *sig = current->signal; int stop_count; if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED)) return 0; if (sig->group_stop_count > 0) { /* * There is a group stop in progress. We don't need to * start another one. */ stop_count = --sig->group_stop_count; } else { /* * There is no group stop already in progress. * We must initiate one now. */ struct task_struct *t; sig->group_exit_code = signr; stop_count = 0; for (t = next_thread(current); t != current; t = next_thread(t)) /* * Setting state to TASK_STOPPED for a group * stop is always done with the siglock held, * so this check has no races. */ if (!t->exit_state && !(t->state & (TASK_STOPPED|TASK_TRACED))) { stop_count++; signal_wake_up(t, 0); } sig->group_stop_count = stop_count; } if (stop_count == 0) sig->flags = SIGNAL_STOP_STOPPED; current->exit_code = sig->group_exit_code; __set_current_state(TASK_STOPPED); spin_unlock_irq(¤t->sighand->siglock); finish_stop(stop_count); return 1;}/* * Do appropriate magic when group_stop_count > 0. * We return nonzero if we stopped, after releasing the siglock. * We return zero if we still hold the siglock and should look * for another signal without checking group_stop_count again. */static int handle_group_stop(void){ int stop_count; if (current->signal->group_exit_task == current) { /* * Group stop is so we can do a core dump, * We are the initiating thread, so get on with it. */ current->signal->group_exit_task = NULL; return 0; } if (current->signal->flags & SIGNAL_GROUP_EXIT) /* * Group stop is so another thread can do a core dump, * or else we are racing against a death signal. * Just punt the stop so we can get the next signal. */ return 0; /* * There is a group stop in progress. We stop * without any associated signal being in our queue. */ stop_count = --current->signal->group_stop_count; if (stop_count == 0) current->signal->flags = SIGNAL_STOP_STOPPED; current->exit_code = current->signal->group_exit_code; set_current_state(TASK_STOPPED); spin_unlock_irq(¤t->sighand->siglock); finish_stop(stop_count); return 1;}int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie){ sigset_t *mask = ¤t->blocked; int signr = 0; try_to_freeze();relock: spin_lock_irq(¤t->sighand->siglock); for (;;) { struct k_sigaction *ka; if (unlikely(current->signal->group_stop_count > 0) && handle_group_stop()) goto relock; signr = dequeue_signal(current, mask, info); if (!signr) break; /* will return 0 */ if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { ptrace_signal_deliver(regs, cookie); /* Let the debugger run. */ ptrace_stop(signr, signr, info); /* We're back. Did the debugger cancel the sig? */ signr = current->exit_code; if (signr == 0) continue; current->exit_code = 0; /* Update the siginfo structure if the signal has changed. If the debugger wanted something specific in the siginfo structure then it should have updated *info via PTRACE_SETSIGINFO. */ if (signr != info->si_signo) { info->si_signo = signr; info->si_errno = 0; info->si_code = SI_USER; info->si_pid = current->parent->pid; info->si_uid = current->parent->uid; } /* If the (new) signal is now blocked, requeue it. */ if (sigismember(¤t->blocked, signr)) { specific_send_sig_info(signr, info, current); continue; } } ka = ¤t->sighand->action[signr-1]; if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */ continue; if (ka->sa.sa_handler != SIG_DFL) { /* Run the handler. */ *return_ka = *ka; if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; break; /* will return non-zero "signr" value */ } /* * Now we are doing the default action for this signal. */ if (sig_kernel_ignore(signr)) /* Default is nothing. */ continue; /* * Init of a pid space gets no signals it doesn't want from * within that pid space. It can of course get signals from * its parent pid space. */ if (current == child_reaper(current)) continue; if (sig_kernel_stop(signr)) { /* * The default action is to stop all threads in * the thread group. The job control signals * do nothing in an orphaned pgrp, but SIGSTOP * always works. Note that siglock needs to be * dropped during the call to is_orphaned_pgrp() * because of lock ordering with tasklist_lock. * This allows an intervening SIGCONT to be posted. * We need to check for that and bail out if necessary. */ if (signr != SIGSTOP) { spin_unlock_irq(¤t->sighand->siglock); /* signals can be posted during this window */ if (is_current_pgrp_orphaned()) goto relock; spin_lock_irq(¤t->sighand->siglock); } if (likely(do_signal_stop(signr))) { /* It released the siglock. */ goto relock; } /* * We didn't actually stop, due to a race * with SIGCONT or something like that. */ continue; } spin_unlock_irq(¤t->sighand->siglock); /* * Anything else is fatal, maybe with a core dump. */ current->flags |= PF_SIGNALED; if (sig_kernel_coredump(signr)) { /* * If it was able to dump core, this kills all * other threads in the group and synchronizes with * their demise. If we lost the race with another * thread getting here, it set group_exit_code * first and our do_group_exit call below will use * that value and ignore the one we pass it. */ do_coredump((long)signr, signr, regs); } /* * Death signals, no core dump. */ do_group_exit(signr); /* NOTREACHED */ } spin_unlock_irq(¤t->sighand->siglock); return signr;}EXPORT_SYMBOL(recalc_sigpending);EXPORT_SYMBOL_GPL(dequeue_signal);EXPORT_SYMBOL(flush_signals);EXPORT_SYMBOL(force_sig);EXPORT_SYMBOL(kill_proc);EXPORT_SYMBOL(ptrace_notify);EXPORT_SYMBOL(send_sig);EXPORT_SYMBOL(send_sig_info);EXPORT_SYMBOL(sigprocmask);EXPORT_SYMBOL(block_all_signals);EXPORT_SYMBOL(unblock_all_signals);/* * System call entry points. */asmlinkage long sys_restart_syscall(void){ struct restart_block *restart = ¤t_thread_info()->restart_block; return restart->fn(restart);}long do_no_restart_syscall(struct restart_block *param){ return -EINTR;}/* * We don't need to get the kernel lock - this is all local to this * particular thread.. (and that's good, because this is _heavily_ * used by various programs) *//* * This is also useful for kernel threads that want to temporarily * (or permanently) block certain signals. * * NOTE! Unlike the user-mode sys_sigprocmask(), the kernel * interface happily blocks "unblockable" signals like SIGKILL * and friends. */int sigprocmask(int how, sigset_t *set, sigset_t *oldset){ int error; spin_lock_irq(¤t->sighand->siglock); if (oldset) *oldset = current->blocked; error = 0; switch (how) { case SIG_BLOCK: sigorsets(¤t->blocked, ¤t->blocked, set); break; case SIG_UNBLOCK: signandsets(¤t->blocked, ¤t->blocked, set); break; case SIG_SETMASK: current->blocked = *set; break; default: error = -EINVAL; } recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); return error;}asmlinkage longsys_rt_sigprocmask(int how, sigset_t __user *set, sigset_t __user *oset, size_t sigsetsize){ int error = -EINVAL; sigset_t old_set, new_set; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(sigset_t)) goto out; if (set) { error = -EFAULT; if (copy_from_user(&new_set, set, sizeof(*set))) goto out; sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); error = sigprocmask(how, &new_set, &old_set); if (error) goto out; if (oset) goto set_old; } else if (oset) { spin_lock_irq(¤t->sighand->siglock); old_set = current->blocked; spin_unlock_irq(¤t->sighand->siglock); set_old: error = -EFAULT; if (copy_to_user(oset, &old_set, sizeof(*oset))) goto out; } error = 0;out: return error;}long do_sigpending(void __user *set, unsigned long sigsetsize){ long error = -EINVAL; sigset_t pending; if (sigsetsize > sizeof(sigset_t)) goto out; spin_lock_irq(¤t->sighand->siglock); sigorsets(&pending, ¤t->pending.signal, ¤t->signal->shared_pending.signal); spin_unlock_irq(¤t->sighand->siglock); /* Outside the lock because only this thread touches it. */ sigandsets(&pending, ¤t->blocked, &pending); error = -EFAULT; if (!copy_to_user(set, &pending, sigsetsize)) error = 0;out: return error;} asmlinkage longsys_rt_sigpending(sigset_t __user *set, size_t sigsetsize){ return do_sigpending(set, sigsetsize);}#ifndef HAVE_ARCH_COPY_SIGINFO_TO_USERint copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from){ int err; if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) return -EFAULT; if (from->si_code < 0) return __copy_to_user(to, from, sizeof(siginfo_t)) ? -EFAULT : 0; /* * If you change siginfo_t structure, please be sure * this code is fixed accordingly. * Please remember to update the signalfd_copyinfo() function * inside fs/signalfd.c too, in case siginfo_t changes. * It should never copy any pad contained in the structure * to avoid security leaks, but must copy the generic * 3 ints plus the relevant union member. */ err = __put_user(from->si_signo, &to->si_signo); err |= __put_user(from->si_errno, &to->si_errno); err |= __put_user((short)from->si_code, &to->si_code); switch (from->si_code & __SI_MASK) { case __SI_KILL: err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); break; case __SI_TIMER: err |= __put_user(from->si_tid, &to->si_tid); err |= __put_user(from->si_overrun, &to->si_overrun); err |= __put_user(from->si_ptr, &to->si_ptr); break; case __SI_POLL: err |= __put_user(from->si_band, &to->si_band); err |= __put_user(from->si_fd, &to->si_fd); break; case __SI_FAULT: err |= __put_user(from->si_addr, &to->si_addr);#ifdef __ARCH_SI_TRAPNO err |= __put_user(from->si_trapno, &to->si_trapno);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -