Synopsis: System V Semaphore denial of service NetBSD versions: NetBSD 1.4, 1.4.1, and 1.4.2 Thanks to: Artur Grabowski, Jason Thorpe, Bill Sommerfeld Reported in NetBSD Security Advisory: SA2000-005 *** sys/sys/proc.h.orig 1999/03/25 04:45:56 1.74 --- sys/sys/proc.h 2000/04/30 20:12:04 1.74.2.2 *************** *** 230,235 **** --- 230,252 ---- #define P_NOCLDWAIT 0x20000 /* No zombies if child dies */ /* + * These flags are kept in schedflags. schedflags may be modified + * only at splstatclock(). + */ + extern int schedflags; + + #define PSCHED_SEENRR 0x0001 /* process has been in roundrobin() */ + #define PSCHED_SHOULDYIELD 0x0002 /* process should yield */ + + #define PSCHED_SWITCHCLEAR (PSCHED_SEENRR|PSCHED_SHOULDYIELD) + + /* + * Macro to compute the exit signal to be delivered. + */ + #define P_EXITSIG(p) (((p)->p_flag & (P_TRACED|P_FSTRACE)) ? SIGCHLD : \ + p->p_exitsig) + + /* * MOVE TO ucred.h? * * Shareable process credentials (always resident). This includes a reference *************** *** 330,335 **** --- 347,354 ---- void fixjobc __P((struct proc *p, struct pgrp *pgrp, int entering)); int inferior __P((struct proc *p)); int leavepgrp __P((struct proc *p)); + void yield __P((void)); + void preempt __P((struct proc *)); void mi_switch __P((void)); void pgdelete __P((struct pgrp *pgrp)); void procinit __P((void)); *** sys/kern/kern_subr.c.orig 2000/02/01 22:54:45 1.45.2.1 --- sys/kern/kern_subr.c 2000/04/30 20:12:42 1.45.2.3 *************** *** 125,135 **** u_int cnt; int error = 0; char *cp = buf; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE) panic("uiomove: mode"); ! if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) panic("uiomove proc"); #endif while (n > 0 && uio->uio_resid) { --- 125,136 ---- u_int cnt; int error = 0; char *cp = buf; + struct proc *p = uio->uio_procp; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE) panic("uiomove: mode"); ! if (uio->uio_segflg == UIO_USERSPACE && p != curproc) panic("uiomove proc"); #endif while (n > 0 && uio->uio_resid) { *************** *** 145,150 **** --- 146,153 ---- switch (uio->uio_segflg) { case UIO_USERSPACE: + if (schedflags & PSCHED_SHOULDYIELD) + preempt(NULL); if (uio->uio_rw == UIO_READ) error = copyout(cp, iov->iov_base, cnt); else *** sys/kern/kern_synch.c.orig 1999/10/17 22:29:40 1.57.2.1 --- sys/kern/kern_synch.c 2000/04/30 20:13:11 1.57.2.3 *************** *** 1,6 **** --- 1,43 ---- /* $NetBSD: 20000527-yield,v 1.2 2000/05/28 23:52:32 sommerfeld Exp $ */ /*- + * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + /*- * Copyright (c) 1982, 1986, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. *************** *** 68,73 **** --- 105,111 ---- u_char curpriority; /* usrpri of curproc */ int lbolt; /* once a second sleep address */ + int schedflags; /* preemption needed? */ void roundrobin __P((void *)); void schedcpu __P((void *)); *************** *** 82,88 **** --- 120,140 ---- roundrobin(arg) void *arg; { + int s; + if (curproc != NULL) { + s = splstatclock(); + if (schedflags & PSCHED_SEENRR) { + /* + * The process has already been through a roundrobin + * without switching and may be hogging the CPU. + * Indicate that the process should yield. + */ + schedflags |= PSCHED_SHOULDYIELD; + } else + schedflags |= PSCHED_SEENRR; + splx(s); + } need_resched(); timeout(roundrobin, NULL, hz / 10); } *************** *** 569,574 **** --- 621,671 ---- } /* + * General yield call. Puts the current process back on its run queue and + * performs a voluntary context switch. + */ + void + yield() + { + struct proc *p = curproc; + int s; + + p->p_priority = p->p_usrpri; + s = splstatclock(); + setrunqueue(p); + p->p_stats->p_ru.ru_nvcsw++; + mi_switch(); + splx(s); + } + + /* + * General preemption call. Puts the current process back on its run queue + * and performs an involuntary context switch. If a process is supplied, + * we switch to that process. Otherwise, we use the normal process selection + * criteria. + */ + void + preempt(newp) + struct proc *newp; + { + struct proc *p = curproc; + int s; + + /* + * XXX Switching to a specific process is not supported yet. + */ + if (newp != NULL) + panic("preempt: cpu_preempt not yet implemented"); + + p->p_priority = p->p_usrpri; + s = splstatclock(); + setrunqueue(p); + p->p_stats->p_ru.ru_nivcsw++; + mi_switch(); + splx(s); + } + + /* * The machine independent parts of mi_switch(). * Must be called at splstatclock() or higher. */ *************** *** 625,630 **** --- 722,733 ---- p->p_nice = autoniceval + NZERO; resetpriority(p); } + + /* + * Process is about to yield the CPU; clear the appropriate + * scheduling flags. + */ + schedflags &= ~PSCHED_SWITCHCLEAR; /* * Pick a new current process and record its start time. *** sys/kern/kern_ktrace.c.orig 1998/09/11 12:50:10 1.33 --- sys/kern/kern_ktrace.c 2000/04/30 20:13:33 1.33.6.2 *************** *** 53,64 **** #include #include ! struct ktr_header *ktrgetheader __P((int)); ! int ktrops __P((struct proc *, struct proc *, int, int, void *)); ! int ktrsetchildren __P((struct proc *, struct proc *, int, int, void *)); ! void ktrwrite __P((struct proc *, void *, struct ktr_header *)); ! int ktrcanset __P((struct proc *, struct proc *)); void ktrderef(p) struct proc *p; --- 53,67 ---- #include #include ! #include /* XXX for uvmexp, needed by PAGE_SIZE */ ! #include /* XXX for uvmexp, needed by PAGE_SIZE */ + void ktrinitheader __P((struct ktr_header *, struct proc *, int)); + int ktrops __P((struct proc *, struct proc *, int, int, void *)); + int ktrsetchildren __P((struct proc *, struct proc *, int, int, void *)); + int ktrwrite __P((struct proc *, void *, struct ktr_header *)); + int ktrcanset __P((struct proc *, struct proc *)); + void ktrderef(p) struct proc *p; *************** *** 94,113 **** } } ! struct ktr_header * ! ktrgetheader(type) int type; { - struct ktr_header *kth; - struct proc *p = curproc; /* XXX */ ! MALLOC(kth, struct ktr_header *, sizeof(struct ktr_header), ! M_TEMP, M_WAITOK); kth->ktr_type = type; microtime(&kth->ktr_time); kth->ktr_pid = p->p_pid; memcpy(kth->ktr_comm, p->p_comm, MAXCOMLEN); - return (kth); } void --- 97,114 ---- } } ! void ! ktrinitheader(kth, p, type) ! struct ktr_header *kth; ! struct proc *p; int type; { ! memset(kth, 0, sizeof(*kth)); kth->ktr_type = type; microtime(&kth->ktr_time); kth->ktr_pid = p->p_pid; memcpy(kth->ktr_comm, p->p_comm, MAXCOMLEN); } void *************** *** 117,142 **** size_t argsize; register_t args[]; { ! struct ktr_header *kth; ! struct ktr_syscall *ktp; struct proc *p = curproc; /* XXX */ register_t *argp; ! int len = sizeof(struct ktr_syscall) + argsize; int i; p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_SYSCALL); ! MALLOC(ktp, struct ktr_syscall *, len, M_TEMP, M_WAITOK); ktp->ktr_code = code; ktp->ktr_argsize = argsize; argp = (register_t *)((char *)ktp + sizeof(struct ktr_syscall)); for (i = 0; i < (argsize / sizeof(*argp)); i++) *argp++ = args[i]; ! kth->ktr_buf = (caddr_t)ktp; ! kth->ktr_len = len; ! ktrwrite(p, v, kth); ! FREE(ktp, M_TEMP); ! FREE(kth, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 118,142 ---- size_t argsize; register_t args[]; { ! struct ktr_header kth; ! struct ktr_syscall *ktp; struct proc *p = curproc; /* XXX */ register_t *argp; ! size_t len = sizeof(struct ktr_syscall) + argsize; int i; p->p_traceflag |= KTRFAC_ACTIVE; ! ktrinitheader(&kth, p, KTR_SYSCALL); ! ktp = malloc(len, M_TEMP, M_WAITOK); ktp->ktr_code = code; ktp->ktr_argsize = argsize; argp = (register_t *)((char *)ktp + sizeof(struct ktr_syscall)); for (i = 0; i < (argsize / sizeof(*argp)); i++) *argp++ = args[i]; ! kth.ktr_buf = (caddr_t)ktp; ! kth.ktr_len = len; ! (void) ktrwrite(p, v, &kth); ! free(ktp, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 147,167 **** int error; register_t retval; { ! struct ktr_header *kth; struct ktr_sysret ktp; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_SYSRET); ktp.ktr_code = code; ktp.ktr_error = error; ktp.ktr_retval = retval; /* what about val2 ? */ ! kth->ktr_buf = (caddr_t)&ktp; ! kth->ktr_len = sizeof(struct ktr_sysret); ! ktrwrite(p, v, kth); ! FREE(kth, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 147,166 ---- int error; register_t retval; { ! struct ktr_header kth; struct ktr_sysret ktp; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! ktrinitheader(&kth, p, KTR_SYSRET); ktp.ktr_code = code; ktp.ktr_error = error; ktp.ktr_retval = retval; /* what about val2 ? */ ! kth.ktr_buf = (caddr_t)&ktp; ! kth.ktr_len = sizeof(struct ktr_sysret); ! (void) ktrwrite(p, v, &kth); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 170,185 **** void *v; char *path; { ! struct ktr_header *kth; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_NAMEI); ! kth->ktr_len = strlen(path); ! kth->ktr_buf = path; ! ktrwrite(p, v, kth); ! FREE(kth, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 169,183 ---- void *v; char *path; { ! struct ktr_header kth; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! ktrinitheader(&kth, p, KTR_NAMEI); ! kth.ktr_len = strlen(path); ! kth.ktr_buf = path; ! (void) ktrwrite(p, v, &kth); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 189,203 **** struct proc *p; char *emul; { ! struct ktr_header *kth; p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_EMUL); ! kth->ktr_len = strlen(emul); ! kth->ktr_buf = emul; ! ktrwrite(p, v, kth); ! FREE(kth, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 187,200 ---- struct proc *p; char *emul; { ! struct ktr_header kth; p->p_traceflag |= KTRFAC_ACTIVE; ! ktrinitheader(&kth, p, KTR_EMUL); ! kth.ktr_len = strlen(emul); ! kth.ktr_buf = emul; ! (void) ktrwrite(p, v, &kth); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 209,245 **** struct iovec *iov; int len, error; { ! struct ktr_header *kth; struct ktr_genio *ktp; caddr_t cp; int resid = len, cnt; struct proc *p = curproc; /* XXX */ ! if (error) return; p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_GENIO); ! MALLOC(ktp, struct ktr_genio *, sizeof(struct ktr_genio) + len, ! M_TEMP, M_WAITOK); ktp->ktr_fd = fd; ktp->ktr_rw = rw; cp = (caddr_t)((char *)ktp + sizeof(struct ktr_genio)); while (resid > 0) { ! if ((cnt = iov->iov_len) > resid) cnt = resid; ! if (copyin(iov->iov_base, cp, (unsigned)cnt)) ! goto done; ! cp += cnt; resid -= cnt; - iov++; } - kth->ktr_buf = (caddr_t)ktp; - kth->ktr_len = sizeof(struct ktr_genio) + len; ! ktrwrite(p, v, kth); ! done: ! FREE(kth, M_TEMP); ! FREE(ktp, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 206,260 ---- struct iovec *iov; int len, error; { ! struct ktr_header kth; struct ktr_genio *ktp; caddr_t cp; int resid = len, cnt; struct proc *p = curproc; /* XXX */ ! int buflen; ! if (error) return; + p->p_traceflag |= KTRFAC_ACTIVE; ! ! buflen = min(PAGE_SIZE, len + sizeof(struct ktr_genio)); ! ! ktrinitheader(&kth, p, KTR_GENIO); ! ktp = malloc(buflen, M_TEMP, M_WAITOK); ktp->ktr_fd = fd; ktp->ktr_rw = rw; + + kth.ktr_buf = (caddr_t)ktp; + cp = (caddr_t)((char *)ktp + sizeof(struct ktr_genio)); + buflen -= sizeof(struct ktr_genio); + while (resid > 0) { ! if (schedflags & PSCHED_SHOULDYIELD) ! preempt(NULL); ! ! cnt = min(iov->iov_len, buflen); ! if (cnt > resid) cnt = resid; ! if (copyin(iov->iov_base, cp, cnt)) ! break; ! ! kth.ktr_len = cnt + sizeof(struct ktr_genio); ! ! if (ktrwrite(p, v, &kth) != 0) ! break; ! ! iov->iov_base = (caddr_t)iov->iov_base + cnt; ! iov->iov_len -= cnt; ! ! if (iov->iov_len == 0) ! iov++; ! resid -= cnt; } ! free(ktp, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 251,271 **** sigset_t *mask; int code; { ! struct ktr_header *kth; struct ktr_psig kp; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_PSIG); kp.signo = (char)sig; kp.action = action; kp.mask = *mask; kp.code = code; ! kth->ktr_buf = (caddr_t)&kp; ! kth->ktr_len = sizeof(struct ktr_psig); ! ktrwrite(p, v, kth); ! FREE(kth, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 266,285 ---- sigset_t *mask; int code; { ! struct ktr_header kth; struct ktr_psig kp; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! ktrinitheader(&kth, p, KTR_PSIG); kp.signo = (char)sig; kp.action = action; kp.mask = *mask; kp.code = code; ! kth.ktr_buf = (caddr_t)&kp; ! kth.ktr_len = sizeof(struct ktr_psig); ! (void) ktrwrite(p, v, &kth); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 274,292 **** void *v; int out, user; { ! struct ktr_header *kth; ! struct ktr_csw kc; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! kth = ktrgetheader(KTR_CSW); kc.out = out; kc.user = user; ! kth->ktr_buf = (caddr_t)&kc; ! kth->ktr_len = sizeof(struct ktr_csw); ! ktrwrite(p, v, kth); ! FREE(kth, M_TEMP); p->p_traceflag &= ~KTRFAC_ACTIVE; } --- 288,305 ---- void *v; int out, user; { ! struct ktr_header kth; ! struct ktr_csw kc; struct proc *p = curproc; /* XXX */ p->p_traceflag |= KTRFAC_ACTIVE; ! ktrinitheader(&kth, p, KTR_CSW); kc.out = out; kc.user = user; ! kth.ktr_buf = (caddr_t)&kc; ! kth.ktr_len = sizeof(struct ktr_csw); ! (void) ktrwrite(p, v, &kth); p->p_traceflag &= ~KTRFAC_ACTIVE; } *************** *** 361,371 **** error = ESRCH; goto done; } ! for (p = pg->pg_members.lh_first; p != 0; p = p->p_pglist.le_next) if (descend) ret |= ktrsetchildren(curp, p, ops, facs, fp); else ret |= ktrops(curp, p, ops, facs, fp); } else { /* --- 374,386 ---- error = ESRCH; goto done; } ! for (p = LIST_FIRST(&pg->pg_members); p != NULL; ! p = LIST_NEXT(p, p_pglist)) { if (descend) ret |= ktrsetchildren(curp, p, ops, facs, fp); else ret |= ktrops(curp, p, ops, facs, fp); + } } else { /* *************** *** 463,473 **** error = ESRCH; goto done; } ! for (p = pg->pg_members.lh_first; p != 0; p = p->p_pglist.le_next) if (descend) ret |= ktrsetchildren(curp, p, ops, facs, vp); else ret |= ktrops(curp, p, ops, facs, vp); } else { /* --- 478,490 ---- error = ESRCH; goto done; } ! for (p = LIST_FIRST(&pg->pg_members); p != NULL; ! p = LIST_NEXT(p, p_pglist)) { if (descend) ret |= ktrsetchildren(curp, p, ops, facs, vp); else ret |= ktrops(curp, p, ops, facs, vp); + } } else { /* *************** *** 550,562 **** * otherwise do any siblings, and if done with this level, * follow back up the tree (but not past top). */ ! if (p->p_children.lh_first) ! p = p->p_children.lh_first; else for (;;) { if (p == top) return (ret); ! if (p->p_sibling.le_next) { ! p = p->p_sibling.le_next; break; } p = p->p_pptr; --- 567,579 ---- * otherwise do any siblings, and if done with this level, * follow back up the tree (but not past top). */ ! if (LIST_FIRST(&p->p_children) != NULL) ! p = LIST_FIRST(&p->p_children); else for (;;) { if (p == top) return (ret); ! if (LIST_NEXT(p, p_sibling) != NULL) { ! p = LIST_NEXT(p, p_sibling); break; } p = p->p_pptr; *************** *** 565,571 **** /*NOTREACHED*/ } ! void ktrwrite(p, v, kth) struct proc *p; void *v; --- 582,588 ---- /*NOTREACHED*/ } ! int ktrwrite(p, v, kth) struct proc *p; void *v; *************** *** 576,582 **** int error; if (v == NULL) ! return; auio.uio_iov = &aiov[0]; auio.uio_offset = 0; auio.uio_segflg = UIO_SYSSPACE; --- 593,599 ---- int error; if (v == NULL) ! return (0); auio.uio_iov = &aiov[0]; auio.uio_offset = 0; auio.uio_segflg = UIO_SYSSPACE; *************** *** 605,621 **** error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, p->p_ucred); VOP_UNLOCK(vp, 0); } ! if (!error) ! return; /* ! * If error encountered, give up tracing on this vnode. */ ! log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n", ! error); for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { if (p->p_tracep == v) ktrderef(p); } } /* --- 622,643 ---- error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, p->p_ucred); VOP_UNLOCK(vp, 0); } ! if (error == 0) ! return (0); /* ! * If error encountered, give up tracing on this vnode. Don't report ! * EPIPE as this can easily happen with fktrace()/ktruss. */ ! if (error != EPIPE) ! log(LOG_NOTICE, ! "ktrace write failed, errno %d, tracing stopped\n", ! error); for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { if (p->p_tracep == v) ktrderef(p); } + + return (error); } /*