/* * Copyright (c) 1982, 1986, 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * @(#)kern_sig.c 7.7 (Berkeley) 05/05/89 */ #include "param.h" #include "systm.h" #include "user.h" #include "vnode.h" #include "proc.h" #include "timeb.h" #include "times.h" #include "buf.h" #include "mount.h" #include "text.h" #include "seg.h" #include "vm.h" #include "acct.h" #include "uio.h" #include "file.h" #include "kernel.h" #include "machine/reg.h" #include "machine/pte.h" #include "machine/psl.h" #include "machine/mtpr.h" #define cantmask (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP)) #define stopsigmask (sigmask(SIGSTOP)|sigmask(SIGTSTP)| \ sigmask(SIGTTIN)|sigmask(SIGTTOU)) /* * Generalized interface signal handler. */ sigvec() { register struct a { int signo; struct sigvec *nsv; struct sigvec *osv; } *uap = (struct a *)u.u_ap; struct sigvec vec; register struct sigvec *sv; register int sig; int bit; sig = uap->signo; if (sig <= 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP) { u.u_error = EINVAL; return; } sv = &vec; if (uap->osv) { sv->sv_handler = u.u_signal[sig]; sv->sv_mask = u.u_sigmask[sig]; bit = sigmask(sig); sv->sv_flags = 0; if ((u.u_sigonstack & bit) != 0) sv->sv_flags |= SV_ONSTACK; if ((u.u_sigintr & bit) != 0) sv->sv_flags |= SV_INTERRUPT; u.u_error = copyout((caddr_t)sv, (caddr_t)uap->osv, sizeof (vec)); if (u.u_error) return; } if (uap->nsv) { u.u_error = copyin((caddr_t)uap->nsv, (caddr_t)sv, sizeof (vec)); if (u.u_error) return; if (sig == SIGCONT && sv->sv_handler == SIG_IGN) { u.u_error = EINVAL; return; } setsigvec(sig, sv); } } setsigvec(sig, sv) int sig; register struct sigvec *sv; { register struct proc *p; register int bit; bit = sigmask(sig); p = u.u_procp; /* * Change setting atomically. */ (void) splhigh(); u.u_signal[sig] = sv->sv_handler; u.u_sigmask[sig] = sv->sv_mask &~ cantmask; if (sv->sv_flags & SV_INTERRUPT) u.u_sigintr |= bit; else u.u_sigintr &= ~bit; if (sv->sv_flags & SV_ONSTACK) u.u_sigonstack |= bit; else u.u_sigonstack &= ~bit; if (sv->sv_handler == SIG_IGN) { p->p_sig &= ~bit; /* never to be seen again */ p->p_sigignore |= bit; p->p_sigcatch &= ~bit; } else { p->p_sigignore &= ~bit; if (sv->sv_handler == SIG_DFL) p->p_sigcatch &= ~bit; else p->p_sigcatch |= bit; } (void) spl0(); } sigblock() { struct a { int mask; } *uap = (struct a *)u.u_ap; register struct proc *p = u.u_procp; (void) splhigh(); u.u_r.r_val1 = p->p_sigmask; p->p_sigmask |= uap->mask &~ cantmask; (void) spl0(); } sigsetmask() { struct a { int mask; } *uap = (struct a *)u.u_ap; register struct proc *p = u.u_procp; (void) splhigh(); u.u_r.r_val1 = p->p_sigmask; p->p_sigmask = uap->mask &~ cantmask; (void) spl0(); } sigpause() { struct a { int mask; } *uap = (struct a *)u.u_ap; register struct proc *p = u.u_procp; /* * When returning from sigpause, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the proc structure * to indicate this (should be in u.). */ u.u_oldmask = p->p_sigmask; p->p_flag |= SOMASK; p->p_sigmask = uap->mask &~ cantmask; for (;;) sleep((caddr_t)&u, PSLEP); /*NOTREACHED*/ } #undef cantmask sigstack() { register struct a { struct sigstack *nss; struct sigstack *oss; } *uap = (struct a *)u.u_ap; struct sigstack ss; if (uap->oss) { u.u_error = copyout((caddr_t)&u.u_sigstack, (caddr_t)uap->oss, sizeof (struct sigstack)); if (u.u_error) return; } if (uap->nss) { u.u_error = copyin((caddr_t)uap->nss, (caddr_t)&ss, sizeof (ss)); if (u.u_error == 0) u.u_sigstack = ss; } } kill() { register struct a { int pid; int signo; } *uap = (struct a *)u.u_ap; register struct proc *p; if (uap->signo < 0 || uap->signo > NSIG) { u.u_error = EINVAL; return; } if (uap->pid > 0) { /* kill single process */ p = pfind(uap->pid); if (p == 0) { u.u_error = ESRCH; return; } if (u.u_uid && u.u_uid != p->p_uid) u.u_error = EPERM; else if (uap->signo) psignal(p, uap->signo); return; } switch (uap->pid) { case -1: /* broadcast signal */ u.u_error = killpg1(uap->signo, 0, 1); break; case 0: /* signal own process group */ u.u_error = killpg1(uap->signo, 0, 0); break; default: /* negative explicit process group */ u.u_error = killpg1(uap->signo, -uap->pid, 0); break; } return; } killpg() { register struct a { int pgid; int signo; } *uap = (struct a *)u.u_ap; if (uap->signo < 0 || uap->signo > NSIG) { u.u_error = EINVAL; return; } u.u_error = killpg1(uap->signo, uap->pgid, 0); } /* KILL CODE SHOULDNT KNOW ABOUT PROCESS INTERNALS !?! */ killpg1(signo, pgid, all) int signo, pgid, all; { register struct proc *p; struct pgrp *pgrp; int f = 0, error = 0; if (all) /* * broadcast */ for (p = allproc; p != NULL; p = p->p_nxt) { if (p->p_ppid == 0 || p->p_flag&SSYS || p == u.u_procp || (u.u_uid && u.u_uid != p->p_uid && !(signo == SIGCONT && inferior(p)))) continue; f++; if (signo) psignal(p, signo); } else { if (pgid == 0) /* * zero pgid means send to my process group. */ pgrp = u.u_procp->p_pgrp; else { pgrp = pgfind(pgid); if (pgrp == NULL) return(ESRCH); } if (!(pgrp->pg_jobc) && (signo==SIGTTIN || signo==SIGTTOU || signo==SIGTSTP)) return(EPERM); for (p = pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt) { if (p->p_ppid == 0 || p->p_flag&SSYS) continue; if (u.u_uid && u.u_uid != p->p_uid && !(signo == SIGCONT && inferior(p))) { error = EPERM; continue; } f++; if (signo) psignal(p, signo); } } return (error ? error : (f == 0 ? ESRCH : 0)); } /* * Send the specified signal to * all processes with 'pgid' as * process group. */ gsignal(pgid, sig) { register struct pgrp *pgrp; register struct proc *p; if (!pgid) return; if ((pgrp = pgfind(pgid)) == NULL) return; pgsignal(pgrp, sig); } pgsignal(pgrp, sig) register struct pgrp *pgrp; { register struct proc *p; if (!(pgrp->pg_jobc) && (sig==SIGTTIN || sig==SIGTTOU || sig==SIGTSTP)) return; for (p = pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt) psignal(p, sig); } /* * Send the specified signal to * the specified process. */ psignal(p, sig) register struct proc *p; register int sig; { register int s; register int (*action)(); int mask; if ((unsigned)sig >= NSIG) return; mask = sigmask(sig); /* * If proc is traced, always give parent a chance. */ if (p->p_flag & STRC) 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 (sig) { switch (sig) { case SIGTERM: if ((p->p_flag&STRC) || action != SIG_DFL) break; /* fall into ... */ case SIGKILL: if (p->p_nice > NZERO) p->p_nice = NZERO; break; case SIGCONT: p->p_sig &= ~stopsigmask; break; case SIGTSTP: case SIGTTIN: case SIGTTOU: /*FALLTHROUGH*/ case SIGSTOP: p->p_sig &= ~sigmask(SIGCONT); break; } p->p_sig |= mask; } /* * Defer further processing for signals which are held. */ if (action == SIG_HOLD) return; s = splhigh(); switch (p->p_stat) { case SSLEEP: /* * If process is sleeping at negative priority * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ if (p->p_pri <= PZERO) goto out; /* * Process is sleeping and traced... make it runnable * so it can discover the signal in issig() and stop * for the parent. */ if (p->p_flag&STRC) goto run; switch (sig) { case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: /* * These are the signals which by default * stop a process. */ if (action != SIG_DFL) goto run; /* * If a child in vfork(), stopping could * cause deadlock. */ if (p->p_flag&SVFORK) goto out; p->p_sig &= ~mask; p->p_cursig = sig; psignal(p->p_pptr, SIGCHLD); stop(p); goto out; case SIGIO: case SIGURG: case SIGCHLD: case SIGWINCH: /* * These signals are special in that they * don't get propogated... if the process * isn't interested, forget it. */ if (action != SIG_DFL) goto run; p->p_sig &= ~mask; /* take it away */ goto out; default: /* * All other signals cause the process to run */ goto run; } /*NOTREACHED*/ case SSTOP: /* * If traced process is already stopped, * then no further action is necessary. */ if (p->p_flag&STRC) goto out; switch (sig) { case SIGKILL: /* * Kill signal always sets processes running. */ goto run; case SIGCONT: /* * 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_wchan == 0) goto run; p->p_stat = SSLEEP; goto out; case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: /* * 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; default: /* * If process is sleeping interruptibly, then * unstick it so that when it is continued * it can look at the signal. * But don't setrun the process as its not to * be unstopped by the signal alone. */ if (p->p_wchan && p->p_pri > PZERO) 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. */ if (p == u.u_procp && !noproc) aston(); goto out; } /*NOTREACHED*/ run: /* * Raise priority to at least PUSER. */ if (p->p_pri > PUSER) p->p_pri = PUSER; setrun(p); out: splx(s); } /* * Returns true if the current * process has a signal to process. * The signal to process is put in p_cursig. * This is asked at least once each time a process enters the * system (though this can usually be done without actually * calling issig by checking the pending signal masks.) * A signal does not do anything * directly to a process; it sets * a flag that asks the process to * do something to itself. */ issig() { register struct proc *p; register int sig; int sigbits, mask; p = u.u_procp; for (;;) { sigbits = p->p_sig &~ p->p_sigmask; if ((p->p_flag&STRC) == 0) sigbits &= ~p->p_sigignore; if (p->p_flag&SVFORK) sigbits &= ~stopsigmask; if (sigbits == 0) break; sig = ffs((long)sigbits); mask = sigmask(sig); p->p_sig &= ~mask; /* take the signal! */ p->p_cursig = sig; if (p->p_flag&STRC && (p->p_flag&SVFORK) == 0) { /* * If traced, always stop, and stay * stopped until released by the parent. */ psignal(p->p_pptr, SIGCHLD); do { stop(p); swtch(); } while (!procxmt() && p->p_flag&STRC); /* * If the traced bit got turned off, * then put the signal taken above back into p_sig * and go back up to the top to rescan signals. * This ensures that p_sig* and u_signal are consistent. */ if ((p->p_flag&STRC) == 0) { p->p_sig |= mask; continue; } /* * If parent wants us to take the signal, * then it will leave it in p->p_cursig; * otherwise we just look for signals again. */ sig = p->p_cursig; if (sig == 0) continue; /* * If signal is being masked put it back * into p_sig and look for other signals. */ mask = sigmask(sig); if (p->p_sigmask & mask) { p->p_sig |= mask; continue; } } switch ((int)u.u_signal[sig]) { case SIG_DFL: /* * Don't take default actions on system processes. */ if (p->p_ppid == 0) break; switch (sig) { case SIGTSTP: case SIGTTIN: case SIGTTOU: case SIGSTOP: if (p->p_flag&STRC) continue; psignal(p->p_pptr, SIGCHLD); stop(p); swtch(); continue; case SIGCONT: case SIGCHLD: case SIGURG: case SIGIO: case SIGWINCH: /* * These signals are normally not * sent if the action is the default. */ continue; /* == ignore */ default: goto send; } /*NOTREACHED*/ case SIG_HOLD: case SIG_IGN: /* * Masking above should prevent us * ever trying to take action on a held * or ignored signal, unless process is traced. */ if ((p->p_flag&STRC) == 0) printf("issig\n"); continue; default: /* * This signal has an action, let * psig process it. */ goto send; } /*NOTREACHED*/ } /* * Didn't find a signal to send. */ p->p_cursig = 0; return (0); send: /* * Let psig process the signal. */ return (sig); } /* * 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 &= ~SWTED; wakeup((caddr_t)p->p_pptr); } /* * Perform the action specified by * the current signal. * The usual sequence is: * if (issig()) * psig(); * The signal bit has already been cleared by issig, * and the current signal number stored in p->p_cursig. */ psig() { register struct proc *p = u.u_procp; register int sig = p->p_cursig; int mask = sigmask(sig), returnmask; register int (*action)(); if (sig == 0) panic("psig"); action = u.u_signal[sig]; if (action != SIG_DFL) { if (action == SIG_IGN || (p->p_sigmask & mask)) panic("psig action"); u.u_error = 0; /* * Set the new mask value and also defer further * occurences of this signal (unless we're simulating * the old signal facilities). * * Special case: user has done a sigpause. Here the * current mask is not of interest, but rather the * mask from before the sigpause is what we want restored * after the signal processing is completed. */ (void) splhigh(); if (p->p_flag & SOUSIG) { if (sig != SIGILL && sig != SIGTRAP) { u.u_signal[sig] = SIG_DFL; p->p_sigcatch &= ~mask; } mask = 0; } if (p->p_flag & SOMASK) { returnmask = u.u_oldmask; p->p_flag &= ~SOMASK; } else returnmask = p->p_sigmask; p->p_sigmask |= u.u_sigmask[sig] | mask; (void) spl0(); u.u_ru.ru_nsignals++; sendsig(action, sig, returnmask); p->p_cursig = 0; return; } u.u_acflag |= AXSIG; switch (sig) { case SIGILL: case SIGIOT: case SIGBUS: case SIGQUIT: case SIGTRAP: case SIGEMT: case SIGFPE: case SIGSEGV: case SIGSYS: u.u_arg[0] = sig; if (core() == 0) 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 block of the * user.h area followed by the entire * data+stack segments. */ core() { register struct vnode *vp, *dvp; register struct nameidata *ndp = &u.u_nd; struct vattr vattr; int error; if (u.u_uid != u.u_ruid || u.u_gid != u.u_rgid) return (EFAULT); if (ctob(UPAGES + u.u_dsize + u.u_ssize) >= u.u_rlimit[RLIMIT_CORE].rlim_cur) return (EFAULT); if (u.u_procp->p_textp) { vop_lock(u.u_procp->p_textp->x_vptr); error = vn_access(u.u_procp->p_textp->x_vptr, VREAD, u.u_cred); vop_unlock(u.u_procp->p_textp->x_vptr); if (error) return (EFAULT); } ndp->ni_segflg = UIO_SYSSPACE; ndp->ni_dirp = "core"; if (error = vn_open(ndp, FCREAT|FWRITE, 0644)) return (error); vp = ndp->ni_vp; if (vp->v_type != VREG || vop_getattr(vp, &vattr, u.u_cred) || vattr.va_nlink != 1) { error = EFAULT; goto out; } #ifdef MMAP { register int fd; /* unmasp funky devices in the user's address space */ for (fd = 0; fd < u.u_lastfile; fd++) if (u.u_ofile[fd] && (u.u_pofile[fd] & UF_MAPPED)) munmapfd(fd); } #endif vattr_null(&vattr); vattr.va_size = 0; vop_setattr(vp, &vattr, u.u_cred); u.u_acflag |= ACORE; error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&u, ctob(UPAGES), (off_t)0, UIO_SYSSPACE, IO_UNIT, (int *)0); if (error == 0) error = vn_rdwr(UIO_WRITE, vp, (caddr_t)ctob(dptov(u.u_procp, 0)), (int)ctob(u.u_dsize), (off_t)ctob(UPAGES), UIO_USERSPACE, IO_UNIT, (int *)0); if (error == 0) error = vn_rdwr(UIO_WRITE, vp, (caddr_t)ctob(sptov(u.u_procp, u.u_ssize - 1)), (int)ctob(u.u_ssize), (off_t)ctob(UPAGES) + ctob(u.u_dsize), UIO_USERSPACE, IO_UNIT, (int *)0); out: if (vp) vrele(vp); return (error); }