2.11BSD/sys/sys/kern_sig.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)kern_sig.c	1.13 (2.11BSD) 2000/2/20
 */

#include "param.h"
#include "../machine/seg.h"
#include "systm.h"
#include "user.h"
#include "inode.h"
#include "proc.h"
#include "text.h"
#include "namei.h"
#include "acct.h"
#include "signalvar.h"
extern	char	sigprop[];	/* XXX - defined in kern_sig2.c */

/*
 * Can the current process send the signal `signum' to process `q'?
 * This is complicated by the need to access the `real uid' of `q'.
 * The 'real uid' is in the u area and `q' may be (but usually is not) swapped 
 * out.  Use the routine `fill_from_u' which the sysctl() call uses.  See the
 * notes in kern_sysctl.c
 *
 * The previous checks for a process to post a signal to another process
 * checked _only_ the effective userid.  With the implementation of the
 * 'saved id' feature and the ability of a setuid program to assume either
 * uid that check was inadequate.
 *
 * The 'c'urrent process is allowed to send a signal to a 't'arget process if 
 * 1) either the real or effective user ids match OR 2) if the signal is 
 * SIGCONT and the target process is a descendant of the current process
*/
cansignal(q, signum)
	register struct proc *q;
	int	signum;
	{
	register struct proc *curp = u.u_procp;
	uid_t	ruid;

	fill_from_u(q, &ruid, NULL, NULL);	/* XXX */
	if	(curp->p_uid == 0 ||		/* c effective root */
		 u.u_ruid == ruid ||		/* c real = t real */
		 curp->p_uid == ruid ||		/* c effective = t real */
		 u.u_ruid == q->p_uid ||	/* c real = t effective */
		 curp->p_uid == q->p_uid ||	/* c effective = t effective */
		 (signum == SIGCONT && inferior(q)))
		return(1);
	return(0);
	}

/*
 * 4.3 Compatibility
*/
sigstack()
	{
	register struct a
		{
		struct	sigstack *nss;
		struct	sigstack *oss;
		} *uap = (struct a *)u.u_ap;
	struct sigstack ss;
	register int error = 0;

	ss.ss_sp = u.u_sigstk.ss_base;
	ss.ss_onstack = u.u_sigstk.ss_flags & SA_ONSTACK;
	if	(uap->oss && (error = copyout((caddr_t)&ss,
					(caddr_t)uap->oss, sizeof (ss))))
		goto out;
	if	(uap->nss && (error = copyin((caddr_t)uap->nss, (caddr_t)&ss, 
					sizeof (ss))) == 0)
		{
		u.u_sigstk.ss_base = ss.ss_sp;
		u.u_sigstk.ss_size = 0;
		u.u_sigstk.ss_flags |= (ss.ss_onstack & SA_ONSTACK);
		u.u_psflags |= SAS_ALTSTACK;
		}
out:
	return(u.u_error = error);
	}

kill()
{
	register struct a {
		int	pid;
		int	signo;
	} *uap = (struct a *)u.u_ap;
	register struct proc *p;
	register int error = 0;

	/*
	 * BSD4.3 botches the comparison against NSIG - it's a good thing for
	 * them psignal catches the error - however, since psignal is the
	 * kernel's internel signal mechanism and *should be getting correct
	 * parameters from the rest of the kernel, psignal shouldn't *have*
	 * to check it's parameters for validity.  If you feel differently,
	 * feel free to clutter up the entire inner kernel with parameter
	 * checks - start with postsig ...
	 */
	if (uap->signo < 0 || uap->signo >= NSIG) {
		error = EINVAL;
		goto out;
	}
	if (uap->pid > 0) {
		/* kill single process */
		p = pfind(uap->pid);
		if (p == 0) {
			error = ESRCH;
			goto out;
		}
		if (!cansignal(p, uap->signo))
			error = EPERM;
		else if (uap->signo)
			psignal(p, uap->signo);
		goto out;
	}
	switch (uap->pid) {
	case -1:		/* broadcast signal */
		error = killpg1(uap->signo, 0, 1);
		break;
	case 0:			/* signal own process group */
		error = killpg1(uap->signo, 0, 0);
		break;
	default:		/* negative explicit process group */
		error = killpg1(uap->signo, -uap->pid, 0);
		break;
	}
out:
	return(u.u_error = error);
}

killpg()
{
	register struct a {
		int	pgrp;
		int	signo;
	} *uap = (struct a *)u.u_ap;
	register int error = 0;

	if (uap->signo < 0 || uap->signo >= NSIG) {
		error = EINVAL;
		goto out;
	}
	error = killpg1(uap->signo, uap->pgrp, 0);
out:
	return(u.u_error = error);
}

killpg1(signo, pgrp, all)
	int signo, pgrp, all;
{
	register struct proc *p;
	int f, error = 0;

	if (!all && pgrp == 0) {
		/*
		 * Zero process id means send to my process group.
		 */
		pgrp = u.u_procp->p_pgrp;
		if (pgrp == 0)
			return (ESRCH);
	}
	for (f = 0, p = allproc; p != NULL; p = p->p_nxt) {
		if ((p->p_pgrp != pgrp && !all) || p->p_ppid == 0 ||
		    (p->p_flag&SSYS) || (all && p == u.u_procp))
			continue;
		if (!cansignal(p, signo)) {
			if (!all)
				error = EPERM;
			continue;
		}
		f++;
		if (signo)
			psignal(p, signo);
	}
	return (error ? error : (f == 0 ? ESRCH : 0));
}

/*
 * Send the specified signal to
 * all processes with 'pgrp' as
 * process group.
 */
gsignal(pgrp, sig)
	register int pgrp;
{
	register struct proc *p;
	mapinfo	map;

	if (pgrp == 0)
		return;
	savemap(map);

	for (p = allproc; p != NULL; p = p->p_nxt)
		if (p->p_pgrp == pgrp)
			psignal(p, sig);
	restormap(map);
}

/*
 * Send the specified signal to
 * the specified process.
 */
psignal(p, sig)
	register struct proc *p;
	register int sig;
{
	register int s;
	int (*action)();
	int prop;
	long mask;

	mask = sigmask(sig);
	prop = sigprop[sig];

	/*
	 * If proc is traced, always give parent a chance.
	 */
	if (p->p_flag & P_TRACED)
		action = SIG_DFL;
	else {
		/*
		 * If the signal is being ignored,
		 * then we forget about it immediately.
		 */
		if (p->p_sigignore & mask)
			return;
		if (p->p_sigmask & mask)
			action = SIG_HOLD;
		else if (p->p_sigcatch & mask)
			action = SIG_CATCH;
		else
			action = SIG_DFL;
	}

	if (p->p_nice > NZERO && action == SIG_DFL && (prop & SA_KILL) &&
	    (p->p_flag & P_TRACED) == 0)
		p->p_nice = NZERO;

	if (prop & SA_CONT)
		p->p_sig &= ~stopsigmask;

	if (prop & SA_STOP) {
		/*
		 * If sending a tty stop signal to a member of an orphaned
		 * process group (i.e. a child of init), discard the signal 
		 * here if the action is default; don't stop the process 
		 * below if sleeping, and don't clear any pending SIGCONT.
		 */
		if (prop & SA_TTYSTOP && (p->p_pptr == &proc[1]) &&
		    action == SIG_DFL)
			return;
		p->p_sig &= ~contsigmask;
	}
	p->p_sig |= mask;

	/*
	 * Defer further processing for signals which are held.
	 */
	if (action == SIG_HOLD && ((prop & SA_CONT) == 0 || p->p_stat != SSTOP))
		return;
	s = splhigh();
	switch (p->p_stat) {

	case SSLEEP:
		/*
		 * If process is sleeping uninterruptibly we can not
		 * interrupt the sleep... the signal will be noticed 
		 * when the process returns through trap() or syscall().
		 */
		if	((p->p_flag & P_SINTR) == 0)
			goto out;
		/*
		 * Process is sleeping and traced... make it runnable
		 * so it can discover the signal in issignal() and stop
		 * for the parent.
		 */
		if	(p->p_flag& P_TRACED)
			goto run;

		/*
		 * If SIGCONT is default (or ignored) and process is
		 * asleep, we are finished; the process should not
		 * be awakened.
		 */
		if ((prop & SA_CONT) && action == SIG_DFL) {
			p->p_sig &= ~mask;
			goto out;
		}
		/*
		 * When a sleeping process receives a stop
		 * signal, process immediately if possible.
		 * All other (caught or default) signals
		 * cause the process to run.
		 */
		if (prop & SA_STOP) {
			if (action != SIG_DFL)
				goto run;
			/*
			 * If a child holding parent blocked,
			 * stopping could cause deadlock.
			 */
			if (p->p_flag & SVFORK)
				goto out;
			p->p_sig &= ~mask;
			p->p_ptracesig = sig;
			if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0)
				psignal(p->p_pptr, SIGCHLD);
			stop(p);
			goto out;
		} else
			goto run;
		/*NOTREACHED*/
	case SSTOP:
		/*
		 * If traced process is already stopped,
		 * then no further action is necessary.
		 */
		if (p->p_flag & P_TRACED)
			goto out;
		if (sig == SIGKILL)
			goto run;
		if (prop & SA_CONT) {
			/*
			 * If SIGCONT is default (or ignored), we continue the
			 * process but don't leave the signal in p_sig, as
			 * it has no further action.  If SIGCONT is held, we
			 * continue the process and leave the signal in
			 * p_sig.  If the process catches SIGCONT, let it
			 * handle the signal itself.  If it isn't waiting on
			 * an event, then it goes back to run state.
			 * Otherwise, process goes back to sleep state.
			 */
			if (action == SIG_DFL)
				p->p_sig &= ~mask;
			if (action == SIG_CATCH || p->p_wchan == 0)
				goto run;
			p->p_stat = SSLEEP;
			goto out;
		}

		if (prop & SA_STOP) {
			/*
			 * Already stopped, don't need to stop again.
			 * (If we did the shell could get confused.)
			 */
			p->p_sig &= ~mask;		/* take it away */
			goto out;
		}

		/*
		 * If process is sleeping interruptibly, then simulate a
		 * wakeup so that when it is continued, it will be made
		 * runnable and can look at the signal.  But don't make
		 * the process runnable, leave it stopped.
		 */
		if (p->p_wchan && (p->p_flag & P_SINTR))
			unsleep(p);
		goto out;
		/*NOTREACHED*/

	default:
		/*
		 * SRUN, SIDL, SZOMB do nothing with the signal,
		 * other than kicking ourselves if we are running.
		 * It will either never be noticed, or noticed very soon.
		 */
		goto out;
	}
	/*NOTREACHED*/
run:
	/*
	 * Raise priority to at least PUSER.
	 */
	if (p->p_pri > PUSER)
		p->p_pri = PUSER;
	setrun(p);
out:
	splx(s);
}

/*
 * If the current process has received a signal (should be caught
 * or cause termination, should interrupt current syscall) return the
 * signal number.  Stop signals with default action are processed
 * immediately then cleared; they are not returned.  This is checked
 * after each entry into the kernel for a syscall of trap (though this 
 * can usually be done without calling issignal by checking the pending
 * signals masks in CURSIG)/  The normal sequence is:
 *
 *	while (signum = CURSIG(u.u_procp))
 *		postsig(signum);
 */
issignal(p)
	register struct proc *p;
{
	register int sig;
	long mask;
	int prop;

	for (;;) {
		mask = p->p_sig & ~p->p_sigmask;
		if (p->p_flag&SVFORK)
			mask &= ~stopsigmask;
		if (mask == 0)
			return(0);		/* No signals to send */
		sig = ffs(mask);
		mask = sigmask(sig);
		prop = sigprop[sig];
		/*
		 * We should see pending but ignored signals
		 * only if P_TRACED was on when they were posted.
		*/
		if (mask & p->p_sigignore && (p->p_flag& P_TRACED) == 0) {
			p->p_sig &= ~mask;
			continue;
		}
		if (p->p_flag & P_TRACED && (p->p_flag & SVFORK) == 0) {
			/*
			 * If traced, always stop, and stay
			 * stopped until released by the parent.
			 *
			 * Note that we  must clear the pending signal
			 * before we call procxmt since that routine
			 * might cause a fault, calling sleep and 
			 * leading us back here again with the same signal.
			 * Then we would be deadlocked because the tracer
			 * would still be blocked on the ipc struct from 
			 * the initial request.
			 */
			p->p_sig &= ~mask;
			p->p_ptracesig = sig;
			psignal(p->p_pptr, SIGCHLD);
			do {
				stop(p);
				swtch();
			} while (!procxmt() && p->p_flag & P_TRACED);

			/*
			 * If parent wants us to take the signal,
			 * then it will leave it in p->p_ptracesig;
			 * otherwise we just look for signals again.
			 */
			sig = p->p_ptracesig;
			if (sig == 0)
				continue;

			/*
			 * Put the new signal into p_sig.  If the
			 * signal is being masked, look for other signals.
			 */
			mask = sigmask(sig);
			p->p_sig |= mask;
			if (p->p_sigmask & mask)
				continue;

			/*
			 * If the traced bit got turned off, go back up
			 * to the top to rescan signals.  This ensures
			 * that p_sig* and u_signal are consistent.
			 */
			if ((p->p_flag& P_TRACED) == 0)
				continue;
			prop = sigprop[sig];
		}

		switch ((int)u.u_signal[sig]) {

		case SIG_DFL:
			/*
			 * Don't take default actions on system processes.
			 */
			if (p->p_pid <= 1) {
#ifdef DIAGNOSTIC
				/*
 				 * Are you sure you want to ignore SIGSEGV
 				 * in init? XXX
				 */
				printf("Process (pid %d) got signal %d\n",
					p->p_pid, sig);
#endif
				break;
			}
			/*
			 * If there is a pending stop signal to process
			 * with default action, stop here,
			 * then clear the signal.  However,
			 * if process is member of an orphaned
			 * process group, ignore tty stop signals.
			 */
			if (prop & SA_STOP) {
				if (p->p_flag & P_TRACED ||
		    		    (p->p_pptr == &proc[1] &&
				    prop & SA_TTYSTOP))
					break;	/* == ignore */
				p->p_ptracesig = sig;
				if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0)
					psignal(p->p_pptr, SIGCHLD);
				stop(p);
				swtch();
				break;
			} else if (prop & SA_IGNORE) {
				/*
				 * Except for SIGCONT, shouldn't get here.
				 * Default action is to ignore; drop it.
				 */
				break;		/* == ignore */
			} else
				return(sig);
			/*NOTREACHED*/

		case SIG_IGN:
			/*
			 * Masking above should prevent us
			 * ever trying to take action on a held
			 * or ignored signal, unless process is traced.
			 */
			if ((prop & SA_CONT) == 0 &&
				(p->p_flag & P_TRACED) == 0)
				printf("issig\n");
			break;			/* == ignore */

		default:
			/*
			 * This signal has an action, let postsig process it.
			 */
			return(sig);
		}
		p->p_sig &= ~mask;		/* take the signal away! */
	}
	/* NOTREACHED */
}

/*
 * Put the argument process into the stopped
 * state and notify the parent via wakeup.
 * Signals are handled elsewhere.
 */
stop(p)
	register struct proc *p;
{

	p->p_stat = SSTOP;
	p->p_flag &= ~P_WAITED;
	wakeup((caddr_t)p->p_pptr);
}

/*
 * Take the action for the specified signal
 * from the current set of pending signals.
 */

postsig(sig)
	int sig;
{
	register struct proc *p = u.u_procp;
	long mask = sigmask(sig), returnmask;
	register int (*action)();

	if (u.u_fpsaved == 0) {
		savfp(&u.u_fps);
		u.u_fpsaved = 1;
	}

	p->p_sig &= ~mask;
	action = u.u_signal[sig];

	if (action != SIG_DFL) {
#ifdef DIAGNOSTIC
		if (action == SIG_IGN || (p->p_sigmask & mask))
			panic("postsig action");
#endif
		u.u_error = 0;	/* XXX - why? */
		/*
		 * Set the new mask value and also defer further
		 * occurences of this signal.
		 *
		 * Special case: user has done a sigsuspend.  Here the
		 * current mask is not of interest, but rather the
		 * mask from before the sigsuspend is what we want restored
		 * after the signal processing is completed.
		 */
		(void) _splhigh();
		if (u.u_psflags & SAS_OLDMASK) {
			returnmask = u.u_oldmask;
			u.u_psflags &= ~SAS_OLDMASK;
		} else
			returnmask = p->p_sigmask;
		p->p_sigmask |= u.u_sigmask[sig] | mask;
		(void) _spl0();
		u.u_ru.ru_nsignals++;
		sendsig(action, sig, returnmask);
		return;
	}
	u.u_acflag |= AXSIG;
	if	(sigprop[sig] & SA_CORE)
		{
		u.u_arg[0] = sig;
		if	(core())
			sig |= 0200;
		}
	exit(sig);
}

/*
 * Create a core image on the file "core"
 * If you are looking for protection glitches,
 * there are probably a wealth of them here
 * when this occurs to a suid command.
 *
 * It writes UPAGES (USIZE for pdp11) block of the
 * user.h area followed by the entire
 * data+stack segments.
 */
core()
{
	register struct inode *ip;
	struct	nameidata nd;
	register struct	nameidata *ndp = &nd;
	register char *np;
	char	*cp, name[MAXCOMLEN + 6];

	/*
	 * Don't dump if not root and the process has used set user or
	 * group privileges.
	*/
	if	(u.u_acflag & ASUGID && !suser())
		return(0);
	if (ctob(USIZE+u.u_dsize+u.u_ssize) >=
	    u.u_rlimit[RLIMIT_CORE].rlim_cur)
		return (0);
	if (u.u_procp->p_textp && access(u.u_procp->p_textp->x_iptr, IREAD))
		return (0);
	cp = u.u_comm;
	np = name;
	while	(*np++ = *cp++)
		;
	cp = ".core";
	np--;
	while	(*np++ = *cp++)
		;
	u.u_error = 0;
	NDINIT(ndp, CREATE, FOLLOW, UIO_SYSSPACE, name);
	ip = namei(ndp);
	if (ip == NULL) {
		if (u.u_error)
			return (0);
		ip = maknode(0644, ndp);
		if (ip==NULL)
			return (0);
	}
	if (access(ip, IWRITE) ||
	   (ip->i_mode&IFMT) != IFREG ||
	   ip->i_nlink != 1) {
		u.u_error = EFAULT;
		goto out;
	}
	itrunc(ip, (u_long)0, 0);
	u.u_acflag |= ACORE;
	u.u_error = rdwri(UIO_WRITE, ip, &u, ctob(USIZE), (off_t)0,
			UIO_SYSSPACE, IO_UNIT, (int *)0);
	if (u.u_error)
		goto out;

	estabur((u_int)0, u.u_dsize, u.u_ssize, 0, RO);
	u.u_error = rdwri(UIO_WRITE, ip, 0, ctob(u.u_dsize), (off_t)ctob(USIZE),
			UIO_USERSPACE, IO_UNIT, (int *)0);
	if (u.u_error)
		goto out;

	u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)(-(ctob(u.u_ssize))), ctob(u.u_ssize),
			(off_t)ctob(USIZE) + (off_t)ctob(u.u_dsize),
			 UIO_USERSPACE, IO_UNIT, (int *)0);
out:
	iput(ip);
	return (u.u_error == 0);
}