2.11BSD/sys/sys/kern_sig.c
/*
* 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);
}