diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 179 | 
1 files changed, 37 insertions, 142 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 3670225ecbc..364fc95bf97 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -12,7 +12,6 @@  #include <linux/slab.h>  #include <linux/module.h> -#include <linux/smp_lock.h>  #include <linux/init.h>  #include <linux/sched.h>  #include <linux/fs.h> @@ -22,6 +21,7 @@  #include <linux/syscalls.h>  #include <linux/ptrace.h>  #include <linux/signal.h> +#include <linux/signalfd.h>  #include <linux/capability.h>  #include <linux/freezer.h>  #include <linux/pid_namespace.h> @@ -39,125 +39,6 @@  static struct kmem_cache *sigqueue_cachep; -/* - * In POSIX a signal is sent either to a specific thread (Linux task) - * or to the process as a whole (Linux thread group).  How the signal - * is sent determines whether it's to one thread or the whole group, - * which determines which signal mask(s) are involved in blocking it - * from being delivered until later.  When the signal is delivered, - * either it's caught or ignored by a user handler or it has a default - * effect that applies to the whole thread group (POSIX process). - * - * The possible effects an unblocked signal set to SIG_DFL can have are: - *   ignore	- Nothing Happens - *   terminate	- kill the process, i.e. all threads in the group, - * 		  similar to exit_group.  The group leader (only) reports - *		  WIFSIGNALED status to its parent. - *   coredump	- write a core dump file describing all threads using - *		  the same mm and then kill all those threads - *   stop 	- stop all the threads in the group, i.e. TASK_STOPPED state - * - * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. - * Other signals when not blocked and set to SIG_DFL behaves as follows. - * The job control signals also have other special effects. - * - *	+--------------------+------------------+ - *	|  POSIX signal      |  default action  | - *	+--------------------+------------------+ - *	|  SIGHUP            |  terminate	| - *	|  SIGINT            |	terminate	| - *	|  SIGQUIT           |	coredump 	| - *	|  SIGILL            |	coredump 	| - *	|  SIGTRAP           |	coredump 	| - *	|  SIGABRT/SIGIOT    |	coredump 	| - *	|  SIGBUS            |	coredump 	| - *	|  SIGFPE            |	coredump 	| - *	|  SIGKILL           |	terminate(+)	| - *	|  SIGUSR1           |	terminate	| - *	|  SIGSEGV           |	coredump 	| - *	|  SIGUSR2           |	terminate	| - *	|  SIGPIPE           |	terminate	| - *	|  SIGALRM           |	terminate	| - *	|  SIGTERM           |	terminate	| - *	|  SIGCHLD           |	ignore   	| - *	|  SIGCONT           |	ignore(*)	| - *	|  SIGSTOP           |	stop(*)(+)  	| - *	|  SIGTSTP           |	stop(*)  	| - *	|  SIGTTIN           |	stop(*)  	| - *	|  SIGTTOU           |	stop(*)  	| - *	|  SIGURG            |	ignore   	| - *	|  SIGXCPU           |	coredump 	| - *	|  SIGXFSZ           |	coredump 	| - *	|  SIGVTALRM         |	terminate	| - *	|  SIGPROF           |	terminate	| - *	|  SIGPOLL/SIGIO     |	terminate	| - *	|  SIGSYS/SIGUNUSED  |	coredump 	| - *	|  SIGSTKFLT         |	terminate	| - *	|  SIGWINCH          |	ignore   	| - *	|  SIGPWR            |	terminate	| - *	|  SIGRTMIN-SIGRTMAX |	terminate       | - *	+--------------------+------------------+ - *	|  non-POSIX signal  |  default action  | - *	+--------------------+------------------+ - *	|  SIGEMT            |  coredump	| - *	+--------------------+------------------+ - * - * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". - * (*) Special job control effects: - * When SIGCONT is sent, it resumes the process (all threads in the group) - * from TASK_STOPPED state and also clears any pending/queued stop signals - * (any of those marked with "stop(*)").  This happens regardless of blocking, - * catching, or ignoring SIGCONT.  When any stop signal is sent, it clears - * any pending/queued SIGCONT signals; this happens regardless of blocking, - * catching, or ignored the stop signal, though (except for SIGSTOP) the - * default action of stopping the process may happen later or never. - */ - -#ifdef SIGEMT -#define M_SIGEMT	M(SIGEMT) -#else -#define M_SIGEMT	0 -#endif - -#if SIGRTMIN > BITS_PER_LONG -#define M(sig) (1ULL << ((sig)-1)) -#else -#define M(sig) (1UL << ((sig)-1)) -#endif -#define T(sig, mask) (M(sig) & (mask)) - -#define SIG_KERNEL_ONLY_MASK (\ -	M(SIGKILL)   |  M(SIGSTOP)                                   ) - -#define SIG_KERNEL_STOP_MASK (\ -	M(SIGSTOP)   |  M(SIGTSTP)   |  M(SIGTTIN)   |  M(SIGTTOU)   ) - -#define SIG_KERNEL_COREDUMP_MASK (\ -        M(SIGQUIT)   |  M(SIGILL)    |  M(SIGTRAP)   |  M(SIGABRT)   | \ -        M(SIGFPE)    |  M(SIGSEGV)   |  M(SIGBUS)    |  M(SIGSYS)    | \ -        M(SIGXCPU)   |  M(SIGXFSZ)   |  M_SIGEMT                     ) - -#define SIG_KERNEL_IGNORE_MASK (\ -        M(SIGCONT)   |  M(SIGCHLD)   |  M(SIGWINCH)  |  M(SIGURG)    ) - -#define sig_kernel_only(sig) \ -		(((sig) < SIGRTMIN)  && T(sig, SIG_KERNEL_ONLY_MASK)) -#define sig_kernel_coredump(sig) \ -		(((sig) < SIGRTMIN)  && T(sig, SIG_KERNEL_COREDUMP_MASK)) -#define sig_kernel_ignore(sig) \ -		(((sig) < SIGRTMIN)  && T(sig, SIG_KERNEL_IGNORE_MASK)) -#define sig_kernel_stop(sig) \ -		(((sig) < SIGRTMIN)  && T(sig, SIG_KERNEL_STOP_MASK)) - -#define sig_needs_tasklist(sig)	((sig) == SIGCONT) - -#define sig_user_defined(t, signr) \ -	(((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) &&	\ -	 ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN)) - -#define sig_fatal(t, signr) \ -	(!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ -	 (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL)  static int sig_ignored(struct task_struct *t, int sig)  { @@ -233,8 +114,7 @@ void recalc_sigpending(void)  /* Given the mask, find the first available signal that should be serviced. */ -static int -next_signal(struct sigpending *pending, sigset_t *mask) +int next_signal(struct sigpending *pending, sigset_t *mask)  {  	unsigned long i, *s, *m, x;  	int sig = 0; @@ -329,6 +209,16 @@ void flush_signals(struct task_struct *t)  	spin_unlock_irqrestore(&t->sighand->siglock, flags);  } +void ignore_signals(struct task_struct *t) +{ +	int i; + +	for (i = 0; i < _NSIG; ++i) +		t->sighand->action[i].sa.sa_handler = SIG_IGN; + +	flush_signals(t); +} +  /*   * Flush all handlers for a task.   */ @@ -607,6 +497,11 @@ static int check_kill_permission(int sig, struct siginfo *info,  	int error = -EINVAL;  	if (!valid_signal(sig))  		return error; + +	error = audit_signal_info(sig, t); /* Let audit system see the signal */ +	if (error) +		return error; +  	error = -EPERM;  	if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))  	    && ((sig != SIGCONT) || @@ -616,10 +511,7 @@ static int check_kill_permission(int sig, struct siginfo *info,  	    && !capable(CAP_KILL))  		return error; -	error = security_task_kill(t, info, sig, 0); -	if (!error) -		audit_signal_info(sig, t); /* Let audit system see the signal */ -	return error; +	return security_task_kill(t, info, sig, 0);  }  /* forward decl */ @@ -740,6 +632,12 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,  	int ret = 0;  	/* +	 * Deliver the signal to listening signalfds. This must be called +	 * with the sighand lock held. +	 */ +	signalfd_notify(t, sig); + +	/*  	 * fast-pathed signals for kernel-internal things like SIGSTOP  	 * or SIGKILL.  	 */ @@ -1033,17 +931,6 @@ void zap_other_threads(struct task_struct *p)  		if (t->exit_state)  			continue; -		/* -		 * We don't want to notify the parent, since we are -		 * killed as part of a thread group due to another -		 * thread doing an execve() or similar. So set the -		 * exit signal to -1 to allow immediate reaping of -		 * the process.  But don't detach the thread group -		 * leader. -		 */ -		if (t != p->group_leader) -			t->exit_signal = -1; -  		/* SIGKILL will be handled before any pending SIGSTOP */  		sigaddset(&t->pending.signal, SIGKILL);  		signal_wake_up(t, 1); @@ -1401,6 +1288,11 @@ int send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)  		ret = 1;  		goto out;  	} +	/* +	 * Deliver the signal to listening signalfds. This must be called +	 * with the sighand lock held. +	 */ +	signalfd_notify(p, sig);  	list_add_tail(&q->list, &p->pending.list);  	sigaddset(&p->pending.signal, sig); @@ -1444,6 +1336,11 @@ send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)  		q->info.si_overrun++;  		goto out;  	}  +	/* +	 * Deliver the signal to listening signalfds. This must be called +	 * with the sighand lock held. +	 */ +	signalfd_notify(p, sig);  	/*  	 * Put this signal on the shared-pending queue. @@ -2104,6 +2001,8 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)  	/*  	 * 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. @@ -2636,9 +2535,5 @@ __attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)  void __init signals_init(void)  { -	sigqueue_cachep = -		kmem_cache_create("sigqueue", -				  sizeof(struct sigqueue), -				  __alignof__(struct sigqueue), -				  SLAB_PANIC, NULL, NULL); +	sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC);  }  | 
