2673 lines · 69.3 KB · C
RawDownload
/* *  linux/kernel/signal.c * *  Copyright (C) 1991, 1992  Linus Torvalds * *  1997-11-02  Modified for POSIX.1b signals by Richard Henderson * *  2003-06-02  Jim Houston - Concurrent Computer Corp. *		Changes to use preallocated sigqueue structures *		to allow signals to be sent reliably. */ #include <linux/config.h>#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>#include <linux/tty.h>#include <linux/binfmts.h>#include <linux/security.h>#include <linux/syscalls.h>#include <linux/ptrace.h>#include <linux/posix-timers.h>#include <linux/signal.h>#include <linux/audit.h>#include <asm/param.h>#include <asm/uaccess.h>#include <asm/unistd.h>#include <asm/siginfo.h> /* * SLAB caches for signal bits. */ static kmem_cache_t *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_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){	void __user * handler; 	/*	 * Tracers always want to know about signals..	 */	if (t->ptrace & PT_PTRACED)		return 0; 	/*	 * Blocked signals are never ignored, since the	 * signal handler may change by the time it is	 * unblocked.	 */	if (sigismember(&t->blocked, sig))		return 0; 	/* Is it explicitly or implicitly ignored? */	handler = t->sighand->action[sig-1].sa.sa_handler;	return   handler == SIG_IGN ||		(handler == SIG_DFL && sig_kernel_ignore(sig));} /* * Re-calculate pending state from the set of locally pending * signals, globally pending signals, and blocked signals. */static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked){	unsigned long ready;	long i; 	switch (_NSIG_WORDS) {	default:		for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;)			ready |= signal->sig[i] &~ blocked->sig[i];		break; 	case 4: ready  = signal->sig[3] &~ blocked->sig[3];		ready |= signal->sig[2] &~ blocked->sig[2];		ready |= signal->sig[1] &~ blocked->sig[1];		ready |= signal->sig[0] &~ blocked->sig[0];		break; 	case 2: ready  = signal->sig[1] &~ blocked->sig[1];		ready |= signal->sig[0] &~ blocked->sig[0];		break; 	case 1: ready  = signal->sig[0] &~ blocked->sig[0];	}	return ready !=	0;} #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) fastcall void recalc_sigpending_tsk(struct task_struct *t){	if (t->signal->group_stop_count > 0 ||	    (freezing(t)) ||	    PENDING(&t->pending, &t->blocked) ||	    PENDING(&t->signal->shared_pending, &t->blocked))		set_tsk_thread_flag(t, TIF_SIGPENDING);	else		clear_tsk_thread_flag(t, TIF_SIGPENDING);} void recalc_sigpending(void){	recalc_sigpending_tsk(current);} /* Given the mask, find the first available signal that should be serviced. */ static intnext_signal(struct sigpending *pending, sigset_t *mask){	unsigned long i, *s, *m, x;	int sig = 0;		s = pending->signal.sig;	m = mask->sig;	switch (_NSIG_WORDS) {	default:		for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)			if ((x = *s &~ *m) != 0) {				sig = ffz(~x) + i*_NSIG_BPW + 1;				break;			}		break; 	case 2: if ((x = s[0] &~ m[0]) != 0)			sig = 1;		else if ((x = s[1] &~ m[1]) != 0)			sig = _NSIG_BPW + 1;		else			break;		sig += ffz(~x);		break; 	case 1: if ((x = *s &~ *m) != 0)			sig = ffz(~x) + 1;		break;	}		return sig;} static struct sigqueue *__sigqueue_alloc(struct task_struct *t, unsigned int __nocast flags,					 int override_rlimit){	struct sigqueue *q = NULL; 	atomic_inc(&t->user->sigpending);	if (override_rlimit ||	    atomic_read(&t->user->sigpending) <=			t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur)		q = kmem_cache_alloc(sigqueue_cachep, flags);	if (unlikely(q == NULL)) {		atomic_dec(&t->user->sigpending);	} else {		INIT_LIST_HEAD(&q->list);		q->flags = 0;		q->lock = NULL;		q->user = get_uid(t->user);	}	return(q);} static inline void __sigqueue_free(struct sigqueue *q){	if (q->flags & SIGQUEUE_PREALLOC)		return;	atomic_dec(&q->user->sigpending);	free_uid(q->user);	kmem_cache_free(sigqueue_cachep, q);} static void flush_sigqueue(struct sigpending *queue){	struct sigqueue *q; 	sigemptyset(&queue->signal);	while (!list_empty(&queue->list)) {		q = list_entry(queue->list.next, struct sigqueue , list);		list_del_init(&q->list);		__sigqueue_free(q);	}} /* * Flush all pending signals for a task. */ voidflush_signals(struct task_struct *t){	unsigned long flags; 	spin_lock_irqsave(&t->sighand->siglock, flags);	clear_tsk_thread_flag(t,TIF_SIGPENDING);	flush_sigqueue(&t->pending);	flush_sigqueue(&t->signal->shared_pending);	spin_unlock_irqrestore(&t->sighand->siglock, flags);} /* * This function expects the tasklist_lock write-locked. */void __exit_sighand(struct task_struct *tsk){	struct sighand_struct * sighand = tsk->sighand; 	/* Ok, we're done with the signal handlers */	tsk->sighand = NULL;	if (atomic_dec_and_test(&sighand->count))		kmem_cache_free(sighand_cachep, sighand);} void exit_sighand(struct task_struct *tsk){	write_lock_irq(&tasklist_lock);	__exit_sighand(tsk);	write_unlock_irq(&tasklist_lock);} /* * This function expects the tasklist_lock write-locked. */void __exit_signal(struct task_struct *tsk){	struct signal_struct * sig = tsk->signal;	struct sighand_struct * sighand = tsk->sighand; 	if (!sig)		BUG();	if (!atomic_read(&sig->count))		BUG();	spin_lock(&sighand->siglock);	posix_cpu_timers_exit(tsk);	if (atomic_dec_and_test(&sig->count)) {		posix_cpu_timers_exit_group(tsk);		if (tsk == sig->curr_target)			sig->curr_target = next_thread(tsk);		tsk->signal = NULL;		spin_unlock(&sighand->siglock);		flush_sigqueue(&sig->shared_pending);	} else {		/*		 * If there is any task waiting for the group exit		 * then notify it:		 */		if (sig->group_exit_task && atomic_read(&sig->count) == sig->notify_count) {			wake_up_process(sig->group_exit_task);			sig->group_exit_task = NULL;		}		if (tsk == sig->curr_target)			sig->curr_target = next_thread(tsk);		tsk->signal = NULL;		/*		 * Accumulate here the counters for all threads but the		 * group leader as they die, so they can be added into		 * the process-wide totals when those are taken.		 * The group leader stays around as a zombie as long		 * as there are other threads.  When it gets reaped,		 * the exit.c code will add its counts into these totals.		 * We won't ever get here for the group leader, since it		 * will have been the last reference on the signal_struct.		 */		sig->utime = cputime_add(sig->utime, tsk->utime);		sig->stime = cputime_add(sig->stime, tsk->stime);		sig->min_flt += tsk->min_flt;		sig->maj_flt += tsk->maj_flt;		sig->nvcsw += tsk->nvcsw;		sig->nivcsw += tsk->nivcsw;		sig->sched_time += tsk->sched_time;		spin_unlock(&sighand->siglock);		sig = NULL;	/* Marker for below.  */	}	clear_tsk_thread_flag(tsk,TIF_SIGPENDING);	flush_sigqueue(&tsk->pending);	if (sig) {		/*		 * We are cleaning up the signal_struct here.  We delayed		 * calling exit_itimers until after flush_sigqueue, just in		 * case our thread-local pending queue contained a queued		 * timer signal that would have been cleared in		 * exit_itimers.  When that called sigqueue_free, it would		 * attempt to re-take the tasklist_lock and deadlock.  This		 * can never happen if we ensure that all queues the		 * timer's signal might be queued on have been flushed		 * first.  The shared_pending queue, and our own pending		 * queue are the only queues the timer could be on, since		 * there are no other threads left in the group and timer		 * signals are constrained to threads inside the group.		 */		exit_itimers(sig);		exit_thread_group_keys(sig);		kmem_cache_free(signal_cachep, sig);	}} void exit_signal(struct task_struct *tsk){	write_lock_irq(&tasklist_lock);	__exit_signal(tsk);	write_unlock_irq(&tasklist_lock);} /* * Flush all handlers for a task. */ voidflush_signal_handlers(struct task_struct *t, int force_default){	int i;	struct k_sigaction *ka = &t->sighand->action[0];	for (i = _NSIG ; i != 0 ; i--) {		if (force_default || ka->sa.sa_handler != SIG_IGN)			ka->sa.sa_handler = SIG_DFL;		ka->sa.sa_flags = 0;		sigemptyset(&ka->sa.sa_mask);		ka++;	}}  /* Notify the system that a driver wants to block all signals for this * process, and wants to be notified if any signals at all were to be * sent/acted upon.  If the notifier routine returns non-zero, then the * signal will be acted upon after all.  If the notifier routine returns 0, * then then signal will be blocked.  Only one block per process is * allowed.  priv is a pointer to private data that the notifier routine * can use to determine if the signal should be blocked or not.  */ voidblock_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask){	unsigned long flags; 	spin_lock_irqsave(&current->sighand->siglock, flags);	current->notifier_mask = mask;	current->notifier_data = priv;	current->notifier = notifier;	spin_unlock_irqrestore(&current->sighand->siglock, flags);} /* Notify the system that blocking has ended. */ voidunblock_all_signals(void){	unsigned long flags; 	spin_lock_irqsave(&current->sighand->siglock, flags);	current->notifier = NULL;	current->notifier_data = NULL;	recalc_sigpending();	spin_unlock_irqrestore(&current->sighand->siglock, flags);} static inline int collect_signal(int sig, struct sigpending *list, siginfo_t *info){	struct sigqueue *q, *first = NULL;	int still_pending = 0; 	if (unlikely(!sigismember(&list->signal, sig)))		return 0; 	/*	 * Collect the siginfo appropriate to this signal.  Check if	 * there is another siginfo for the same signal.	*/	list_for_each_entry(q, &list->list, list) {		if (q->info.si_signo == sig) {			if (first) {				still_pending = 1;				break;			}			first = q;		}	}	if (first) {

Lines 1–500 of 2673.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t