diff --git a/sys/fs/procfs/procfs.h b/sys/fs/procfs/procfs.h index bd6a5540016f..1402a7c82f2c 100644 --- a/sys/fs/procfs/procfs.h +++ b/sys/fs/procfs/procfs.h @@ -1,175 +1,162 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs.h 8.9 (Berkeley) 5/14/95 * * From: * $FreeBSD$ */ /* * The different types of node in a procfs filesystem */ typedef enum { Proot, /* the filesystem root */ Pcurproc, /* symbolic link for curproc */ Pproc, /* a process-specific sub-directory */ Pfile, /* the executable file */ Pmem, /* the process's memory image */ Pregs, /* the process's register set */ Pfpregs, /* the process's FP register set */ Pdbregs, /* the process's debug register set */ Pctl, /* process control */ Pstatus, /* process status */ Pnote, /* process notifier */ Pnotepg, /* process group notifier */ Pmap, /* memory map */ Ptype, /* executable type */ Pcmdline, /* command line */ Prlimit /* resource limits */ } pfstype; /* * control data for the proc file system. */ struct pfsnode { struct pfsnode *pfs_next; /* next on list */ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */ pfstype pfs_type; /* type of procfs node */ pid_t pfs_pid; /* associated process */ u_short pfs_mode; /* mode bits for stat() */ u_long pfs_flags; /* open flags */ u_long pfs_fileno; /* unique file id */ pid_t pfs_lockowner; /* pfs lock owner */ }; #define PROCFS_NOTELEN 64 /* max length of a note (/proc/$pid/note) */ #define PROCFS_CTLLEN 8 /* max length of a ctl msg (/proc/$pid/ctl */ #define PROCFS_NAMELEN 8 /* max length of a filename component */ /* * Kernel stuff follows */ #ifdef KERNEL #define CNEQ(cnp, s, len) \ ((cnp)->cn_namelen == (len) && \ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0)) #define KMEM_GROUP 2 -/* - * Check to see whether access to target process is allowed - * Evaluates to 1 if access is allowed. - */ -#define CHECKIO(p1, p2) \ - ((p1) == (p2) || \ - (PRISON_CHECK(p1, p2) && \ - ((((p1)->p_ucred->cr_uid == (p2)->p_cred->p_ruid) && \ - ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && \ - ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && \ - ((p2)->p_flag & P_SUGID) == 0) || \ - (suser_xxx(0, (p1), PRISON_ROOT) == 0)))) - #define PROCFS_FILENO(pid, type) \ (((type) < Pproc) ? \ ((type) + 2) : \ ((((pid)+1) << 4) + ((int) (type)))) /* * Convert between pfsnode vnode */ #define VTOPFS(vp) ((struct pfsnode *)(vp)->v_data) #define PFSTOV(pfs) ((pfs)->pfs_vnode) typedef struct vfs_namemap vfs_namemap_t; struct vfs_namemap { const char *nm_name; int nm_val; }; int vfs_getuserstr __P((struct uio *, char *, int *)); vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int)); /* */ struct reg; struct fpreg; struct dbreg; #define PFIND(pid) ((pid) ? pfind(pid) : &proc0) void procfs_exit __P((struct proc *)); int procfs_freevp __P((struct vnode *)); int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype)); struct vnode *procfs_findtextvp __P((struct proc *)); int procfs_sstep __P((struct proc *)); void procfs_fix_sstep __P((struct proc *)); int procfs_read_regs __P((struct proc *, struct reg *)); int procfs_write_regs __P((struct proc *, struct reg *)); int procfs_read_fpregs __P((struct proc *, struct fpreg *)); int procfs_write_fpregs __P((struct proc *, struct fpreg *)); int procfs_read_dbregs __P((struct proc *, struct dbreg *)); int procfs_write_dbregs __P((struct proc *, struct dbreg *)); int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dodbregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_domap __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dotype __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_docmdline __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dorlimit __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); /* Return 1 if process has special kernel digging privileges */ int procfs_kmemaccess __P((struct proc *)); /* functions to check whether or not files should be displayed */ int procfs_validfile __P((struct proc *)); int procfs_validfpregs __P((struct proc *)); int procfs_validregs __P((struct proc *)); int procfs_validdbregs __P((struct proc *)); int procfs_validmap __P((struct proc *)); int procfs_validtype __P((struct proc *)); #define PROCFS_LOCKED 0x01 #define PROCFS_WANT 0x02 extern vop_t **procfs_vnodeop_p; int procfs_root __P((struct mount *, struct vnode **)); int procfs_rw __P((struct vop_read_args *)); #endif /* KERNEL */ diff --git a/sys/fs/procfs/procfs_dbregs.c b/sys/fs/procfs/procfs_dbregs.c index ce990e983745..0df1c3e529cc 100644 --- a/sys/fs/procfs/procfs_dbregs.c +++ b/sys/fs/procfs/procfs_dbregs.c @@ -1,101 +1,101 @@ /*- * Copyright (c) 1999 Brian Scott Dean, brdean@unx.sas.com. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry under the following copyrights and conditions: * * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include int procfs_dodbregs(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { int error; struct dbreg r; char *kv; int kl; - if (!CHECKIO(curp, p)) + if (p_trespass(curp, p)) return (EPERM); kl = sizeof(r); kv = (char *) &r; kv += uio->uio_offset; kl -= uio->uio_offset; if (kl > uio->uio_resid) kl = uio->uio_resid; PHOLD(p); if (kl < 0) error = EINVAL; else error = procfs_read_dbregs(p, &r); if (error == 0) error = uiomove(kv, kl, uio); if (error == 0 && uio->uio_rw == UIO_WRITE) { if (p->p_stat != SSTOP) error = EBUSY; else error = procfs_write_dbregs(p, &r); } PRELE(p); uio->uio_offset = 0; return (error); } int procfs_validdbregs(p) struct proc *p; { return ((p->p_flag & P_SYSTEM) == 0); } diff --git a/sys/fs/procfs/procfs_fpregs.c b/sys/fs/procfs/procfs_fpregs.c index b3331e5a726e..d4a4cfee5390 100644 --- a/sys/fs/procfs/procfs_fpregs.c +++ b/sys/fs/procfs/procfs_fpregs.c @@ -1,98 +1,98 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_fpregs.c 8.2 (Berkeley) 6/15/94 * * From: * $FreeBSD$ */ #include #include #include #include #include #include #include int procfs_dofpregs(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { int error; struct fpreg r; char *kv; int kl; - if (!CHECKIO(curp, p)) + if (p_trespass(curp, p)) return EPERM; kl = sizeof(r); kv = (char *) &r; kv += uio->uio_offset; kl -= uio->uio_offset; if (kl > uio->uio_resid) kl = uio->uio_resid; PHOLD(p); if (kl < 0) error = EINVAL; else error = procfs_read_fpregs(p, &r); if (error == 0) error = uiomove(kv, kl, uio); if (error == 0 && uio->uio_rw == UIO_WRITE) { if (p->p_stat != SSTOP) error = EBUSY; else error = procfs_write_fpregs(p, &r); } PRELE(p); uio->uio_offset = 0; return (error); } int procfs_validfpregs(p) struct proc *p; { return ((p->p_flag & P_SYSTEM) == 0); } diff --git a/sys/fs/procfs/procfs_mem.c b/sys/fs/procfs/procfs_mem.c index 521bd5bbc551..e075a7adbc81 100644 --- a/sys/fs/procfs/procfs_mem.c +++ b/sys/fs/procfs/procfs_mem.c @@ -1,356 +1,356 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 Sean Eric Fagan * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry and Sean Eric Fagan. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_mem.c 8.5 (Berkeley) 6/15/94 * * $FreeBSD$ */ /* * This is a lightly hacked and merged version * of sef's pread/pwrite functions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int procfs_rwmem __P((struct proc *curp, struct proc *p, struct uio *uio)); static int procfs_rwmem(curp, p, uio) struct proc *curp; struct proc *p; struct uio *uio; { int error; int writing; struct vmspace *vm; vm_map_t map; vm_object_t object = NULL; vm_offset_t pageno = 0; /* page number */ vm_prot_t reqprot; vm_offset_t kva; /* * if the vmspace is in the midst of being deallocated or the * process is exiting, don't try to grab anything. The page table * usage in that process can be messed up. */ vm = p->p_vmspace; if ((p->p_flag & P_WEXIT) || (vm->vm_refcnt < 1)) return EFAULT; ++vm->vm_refcnt; /* * The map we want... */ map = &vm->vm_map; writing = uio->uio_rw == UIO_WRITE; reqprot = writing ? (VM_PROT_WRITE | VM_PROT_OVERRIDE_WRITE) : VM_PROT_READ; kva = kmem_alloc_pageable(kernel_map, PAGE_SIZE); /* * Only map in one page at a time. We don't have to, but it * makes things easier. This way is trivial - right? */ do { vm_map_t tmap; vm_offset_t uva; int page_offset; /* offset into page */ vm_map_entry_t out_entry; vm_prot_t out_prot; boolean_t wired; vm_pindex_t pindex; u_int len; vm_page_t m; object = NULL; uva = (vm_offset_t) uio->uio_offset; /* * Get the page number of this segment. */ pageno = trunc_page(uva); page_offset = uva - pageno; /* * How many bytes to copy */ len = min(PAGE_SIZE - page_offset, uio->uio_resid); if (uva >= VM_MAXUSER_ADDRESS) { vm_offset_t tkva; if (writing || uva >= VM_MAXUSER_ADDRESS + UPAGES * PAGE_SIZE || (ptrace_read_u_check(p, uva - (vm_offset_t) VM_MAXUSER_ADDRESS, (size_t) len) && !procfs_kmemaccess(curp))) { error = 0; break; } /* we are reading the "U area", force it into core */ PHOLD(p); /* sanity check */ if (!(p->p_flag & P_INMEM)) { /* aiee! */ PRELE(p); error = EFAULT; break; } /* populate the ptrace/procfs area */ p->p_addr->u_kproc.kp_proc = *p; fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); /* locate the in-core address */ tkva = (uintptr_t)p->p_addr + uva - VM_MAXUSER_ADDRESS; /* transfer it */ error = uiomove((caddr_t)tkva, len, uio); /* let the pages go */ PRELE(p); continue; } /* * Fault the page on behalf of the process */ error = vm_fault(map, pageno, reqprot, VM_FAULT_NORMAL); if (error) { error = EFAULT; break; } /* * Now we need to get the page. out_entry, out_prot, wired, * and single_use aren't used. One would think the vm code * would be a *bit* nicer... We use tmap because * vm_map_lookup() can change the map argument. */ tmap = map; error = vm_map_lookup(&tmap, pageno, reqprot, &out_entry, &object, &pindex, &out_prot, &wired); if (error) { error = EFAULT; /* * Make sure that there is no residue in 'object' from * an error return on vm_map_lookup. */ object = NULL; break; } m = vm_page_lookup(object, pindex); /* Allow fallback to backing objects if we are reading */ while (m == NULL && !writing && object->backing_object) { pindex += OFF_TO_IDX(object->backing_object_offset); object = object->backing_object; m = vm_page_lookup(object, pindex); } if (m == NULL) { error = EFAULT; /* * Make sure that there is no residue in 'object' from * an error return on vm_map_lookup. */ object = NULL; vm_map_lookup_done(tmap, out_entry); break; } /* * Wire the page into memory */ vm_page_wire(m); /* * We're done with tmap now. * But reference the object first, so that we won't loose * it. */ vm_object_reference(object); vm_map_lookup_done(tmap, out_entry); pmap_kenter(kva, VM_PAGE_TO_PHYS(m)); /* * Now do the i/o move. */ error = uiomove((caddr_t)(kva + page_offset), len, uio); pmap_kremove(kva); /* * release the page and the object */ vm_page_unwire(m, 1); vm_object_deallocate(object); object = NULL; } while (error == 0 && uio->uio_resid > 0); if (object) vm_object_deallocate(object); kmem_free(kernel_map, kva, PAGE_SIZE); vmspace_free(vm); return (error); } /* * Copy data in and out of the target process. * We do this by mapping the process's page into * the kernel and then doing a uiomove direct * from the kernel address space. */ int procfs_domem(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { if (uio->uio_resid == 0) return (0); /* * XXX * We need to check for KMEM_GROUP because ps is sgid kmem; * not allowing it here causes ps to not work properly. Arguably, * this is a bug with what ps does. We only need to do this * for Pmem nodes, and only if it's reading. This is still not * good, as it may still be possible to grab illicit data if * a process somehow gets to be KMEM_GROUP. Note that this also * means that KMEM_GROUP can't change without editing procfs.h! * All in all, quite yucky. */ - if (!CHECKIO(curp, p) && + if (p_trespass(curp, p) && !(uio->uio_rw == UIO_READ && procfs_kmemaccess(curp))) return EPERM; return (procfs_rwmem(curp, p, uio)); } /* * Given process (p), find the vnode from which * its text segment is being executed. * * It would be nice to grab this information from * the VM system, however, there is no sure-fire * way of doing that. Instead, fork(), exec() and * wait() all maintain the p_textvp field in the * process proc structure which contains a held * reference to the exec'ed vnode. * * XXX - Currently, this is not not used, as the * /proc/pid/file object exposes an information leak * that shouldn't happen. Using a mount option would * make it configurable on a per-system (or, at least, * per-mount) basis; however, that's not really best. * The best way to do it, I think, would be as an * ioctl; this would restrict it to the uid running * program, or root, which seems a reasonable compromise. * However, the number of applications for this is * minimal, if it can't be seen in the filesytem space, * and doint it as an ioctl makes it somewhat less * useful due to the, well, inelegance. * */ struct vnode * procfs_findtextvp(p) struct proc *p; { return (p->p_textvp); } int procfs_kmemaccess(curp) struct proc *curp; { int i; struct ucred *cred; cred = curp->p_ucred; if (suser(curp)) return 1; /* XXX: Why isn't this done with file-perms ??? */ for (i = 0; i < cred->cr_ngroups; i++) if (cred->cr_groups[i] == KMEM_GROUP) return 1; return 0; } diff --git a/sys/fs/procfs/procfs_regs.c b/sys/fs/procfs/procfs_regs.c index 0edb9ad60639..88f85d3d1857 100644 --- a/sys/fs/procfs/procfs_regs.c +++ b/sys/fs/procfs/procfs_regs.c @@ -1,98 +1,98 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_regs.c 8.4 (Berkeley) 6/15/94 * * From: * $FreeBSD$ */ #include #include #include #include #include #include #include int procfs_doregs(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { int error; struct reg r; char *kv; int kl; - if (!CHECKIO(curp, p)) + if (p_trespass(curp, p)) return EPERM; kl = sizeof(r); kv = (char *) &r; kv += uio->uio_offset; kl -= uio->uio_offset; if (kl > uio->uio_resid) kl = uio->uio_resid; PHOLD(p); if (kl < 0) error = EINVAL; else error = procfs_read_regs(p, &r); if (error == 0) error = uiomove(kv, kl, uio); if (error == 0 && uio->uio_rw == UIO_WRITE) { if (p->p_stat != SSTOP) error = EBUSY; else error = procfs_write_regs(p, &r); } PRELE(p); uio->uio_offset = 0; return (error); } int procfs_validregs(p) struct proc *p; { return ((p->p_flag & P_SYSTEM) == 0); } diff --git a/sys/fs/procfs/procfs_status.c b/sys/fs/procfs/procfs_status.c index 0eb38581eae9..e63a12bcbeb5 100644 --- a/sys/fs/procfs/procfs_status.c +++ b/sys/fs/procfs/procfs_status.c @@ -1,229 +1,229 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_status.c 8.4 (Berkeley) 6/15/94 * * From: * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include int procfs_dostatus(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { struct session *sess; struct tty *tp; struct ucred *cr; char *ps; char *sep; int pid, ppid, pgid, sid; int i; int xlen; int error; char psbuf[256]; /* XXX - conservative */ if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); pid = p->p_pid; ppid = p->p_pptr ? p->p_pptr->p_pid : 0, pgid = p->p_pgrp->pg_id; sess = p->p_pgrp->pg_session; sid = sess->s_leader ? sess->s_leader->p_pid : 0; /* comm pid ppid pgid sid maj,min ctty,sldr start ut st wmsg euid ruid rgid,egid,groups[1 .. NGROUPS] */ ps = psbuf; bcopy(p->p_comm, ps, MAXCOMLEN); ps[MAXCOMLEN] = '\0'; ps += strlen(ps); ps += sprintf(ps, " %d %d %d %d ", pid, ppid, pgid, sid); if ((p->p_flag&P_CONTROLT) && (tp = sess->s_ttyp)) ps += sprintf(ps, "%d,%d ", major(tp->t_dev), minor(tp->t_dev)); else ps += sprintf(ps, "%d,%d ", -1, -1); sep = ""; if (sess->s_ttyvp) { ps += sprintf(ps, "%sctty", sep); sep = ","; } if (SESS_LEADER(p)) { ps += sprintf(ps, "%ssldr", sep); sep = ","; } if (*sep != ',') ps += sprintf(ps, "noflags"); if (p->p_flag & P_INMEM) { struct timeval ut, st; calcru(p, &ut, &st, (struct timeval *) NULL); ps += sprintf(ps, " %ld,%ld %ld,%ld %ld,%ld", p->p_stats->p_start.tv_sec, p->p_stats->p_start.tv_usec, ut.tv_sec, ut.tv_usec, st.tv_sec, st.tv_usec); } else ps += sprintf(ps, " -1,-1 -1,-1 -1,-1"); ps += sprintf(ps, " %s", (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "nochan"); cr = p->p_ucred; ps += sprintf(ps, " %lu %lu %lu", (u_long)cr->cr_uid, (u_long)p->p_cred->p_ruid, (u_long)p->p_cred->p_rgid); /* egid (p->p_cred->p_svgid) is equal to cr_ngroups[0] see also getegid(2) in /sys/kern/kern_prot.c */ for (i = 0; i < cr->cr_ngroups; i++) ps += sprintf(ps, ",%lu", (u_long)cr->cr_groups[i]); if (p->p_prison) ps += sprintf(ps, " %s", p->p_prison->pr_host); else ps += sprintf(ps, " -"); ps += sprintf(ps, "\n"); xlen = ps - psbuf; xlen -= uio->uio_offset; ps = psbuf + uio->uio_offset; xlen = imin(xlen, uio->uio_resid); if (xlen <= 0) error = 0; else error = uiomove(ps, xlen, uio); return (error); } int procfs_docmdline(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { char *ps; int xlen; int error; char *buf, *bp; int buflen; struct ps_strings pstr; int i; size_t bytes_left, done; if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); /* * If we are using the ps/cmdline caching, use that. Otherwise * revert back to the old way which only implements full cmdline * for the currept process and just p->p_comm for all other * processes. * Note that if the argv is no longer available, we deliberately * don't fall back on p->p_comm or return an error: the authentic * Linux behaviour is to return zero-length in this case. */ - if (p->p_args) { + if (p->p_args && !p_trespass(curp, p)) { bp = p->p_args->ar_args; buflen = p->p_args->ar_length; buf = 0; } else if (p != curp) { bp = p->p_comm; buflen = MAXCOMLEN; buf = 0; } else { buflen = 256; MALLOC(buf, char *, buflen + 1, M_TEMP, M_WAITOK); bp = buf; ps = buf; error = copyin((void*)PS_STRINGS, &pstr, sizeof(pstr)); if (error) { FREE(buf, M_TEMP); return (error); } bytes_left = buflen; for (i = 0; bytes_left && (i < pstr.ps_nargvstr); i++) { error = copyinstr(pstr.ps_argvstr[i], ps, bytes_left, &done); /* If too long or malformed, just truncate */ if (error) { error = 0; break; } ps += done; bytes_left -= done; } buflen = ps - buf; } buflen -= uio->uio_offset; ps = bp + uio->uio_offset; xlen = min(buflen, uio->uio_resid); if (xlen <= 0) error = 0; else error = uiomove(ps, xlen, uio); if (buf) FREE(buf, M_TEMP); return (error); } diff --git a/sys/fs/procfs/procfs_vnops.c b/sys/fs/procfs/procfs_vnops.c index c579d73d8374..17075db6bf82 100644 --- a/sys/fs/procfs/procfs_vnops.c +++ b/sys/fs/procfs/procfs_vnops.c @@ -1,1031 +1,1031 @@ /* * Copyright (c) 1993, 1995 Jan-Simon Pendry * Copyright (c) 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 * * $FreeBSD$ */ /* * procfs vnode interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int procfs_abortop __P((struct vop_abortop_args *)); static int procfs_access __P((struct vop_access_args *)); static int procfs_badop __P((void)); static int procfs_bmap __P((struct vop_bmap_args *)); static int procfs_close __P((struct vop_close_args *)); static int procfs_getattr __P((struct vop_getattr_args *)); static int procfs_inactive __P((struct vop_inactive_args *)); static int procfs_ioctl __P((struct vop_ioctl_args *)); static int procfs_lookup __P((struct vop_lookup_args *)); static int procfs_open __P((struct vop_open_args *)); static int procfs_print __P((struct vop_print_args *)); static int procfs_readdir __P((struct vop_readdir_args *)); static int procfs_readlink __P((struct vop_readlink_args *)); static int procfs_reclaim __P((struct vop_reclaim_args *)); static int procfs_setattr __P((struct vop_setattr_args *)); /* * This is a list of the valid names in the * process-specific sub-directories. It is * used in procfs_lookup and procfs_readdir */ static struct proc_target { u_char pt_type; u_char pt_namlen; char *pt_name; pfstype pt_pfstype; int (*pt_valid) __P((struct proc *p)); } proc_targets[] = { #define N(s) sizeof(s)-1, s /* name type validp */ { DT_DIR, N("."), Pproc, NULL }, { DT_DIR, N(".."), Proot, NULL }, { DT_REG, N("mem"), Pmem, NULL }, { DT_REG, N("regs"), Pregs, procfs_validregs }, { DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs }, { DT_REG, N("dbregs"), Pdbregs, procfs_validdbregs }, { DT_REG, N("ctl"), Pctl, NULL }, { DT_REG, N("status"), Pstatus, NULL }, { DT_REG, N("note"), Pnote, NULL }, { DT_REG, N("notepg"), Pnotepg, NULL }, { DT_REG, N("map"), Pmap, procfs_validmap }, { DT_REG, N("etype"), Ptype, procfs_validtype }, { DT_REG, N("cmdline"), Pcmdline, NULL }, { DT_REG, N("rlimit"), Prlimit, NULL }, #undef N }; static const int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]); static pid_t atopid __P((const char *, u_int)); /* * set things up for doing i/o on * the pfsnode (vp). (vp) is locked * on entry, and should be left locked * on exit. * * for procfs we don't need to do anything * in particular for i/o. all that is done * is to support exclusive open on process * memory images. */ static int procfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p1, *p2; p2 = PFIND(pfs->pfs_pid); if (p2 == NULL) return (ENOENT); if (pfs->pfs_pid && !PRISON_CHECK(ap->a_p, p2)) return (ENOENT); switch (pfs->pfs_type) { case Pmem: if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) || ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) return (EBUSY); p1 = ap->a_p; - if (!CHECKIO(p1, p2) && + if (p_trespass(p1, p2) && !procfs_kmemaccess(p1)) return (EPERM); if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); return (0); default: break; } return (0); } /* * close the pfsnode (vp) after doing i/o. * (vp) is not locked on entry or exit. * * nothing to do for procfs other than undo * any exclusive open flag (see _open above). */ static int procfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p; switch (pfs->pfs_type) { case Pmem: if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) pfs->pfs_flags &= ~(FWRITE|O_EXCL); /* * This rather complicated-looking code is trying to * determine if this was the last close on this particular * vnode. While one would expect v_usecount to be 1 at * that point, it seems that (according to John Dyson) * the VM system will bump up the usecount. So: if the * usecount is 2, and VOBJBUF is set, then this is really * the last close. Otherwise, if the usecount is < 2 * then it is definitely the last close. * If this is the last close, then it checks to see if * the target process has PF_LINGER set in p_pfsflags, * if this is *not* the case, then the process' stop flags * are cleared, and the process is woken up. This is * to help prevent the case where a process has been * told to stop on an event, but then the requesting process * has gone away or forgotten about it. */ if ((ap->a_vp->v_usecount < 2) && (p = pfind(pfs->pfs_pid)) && !(p->p_pfsflags & PF_LINGER)) { p->p_stops = 0; p->p_step = 0; wakeup(&p->p_step); } break; default: break; } return (0); } /* * do an ioctl operation on a pfsnode (vp). * (vp) is not locked on entry or exit. */ static int procfs_ioctl(ap) struct vop_ioctl_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *procp, *p; int error; int signo; struct procfs_status *psp; unsigned char flags; p = ap->a_p; procp = pfind(pfs->pfs_pid); if (procp == NULL) { return ENOTTY; } - if (!CHECKIO(p, procp)) + if (p_trespass(p, procp)) return EPERM; switch (ap->a_command) { case PIOCBIS: procp->p_stops |= *(unsigned int*)ap->a_data; break; case PIOCBIC: procp->p_stops &= ~*(unsigned int*)ap->a_data; break; case PIOCSFL: /* * NFLAGS is "non-suser_xxx flags" -- currently, only * PFS_ISUGID ("ignore set u/g id"); */ #define NFLAGS (PF_ISUGID) flags = (unsigned char)*(unsigned int*)ap->a_data; if (flags & NFLAGS && (error = suser(p))) return error; procp->p_pfsflags = flags; break; case PIOCGFL: *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags; case PIOCSTATUS: psp = (struct procfs_status *)ap->a_data; psp->state = (procp->p_step == 0); psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; if (procp->p_step) { psp->why = procp->p_stype; psp->val = procp->p_xstat; } else { psp->why = psp->val = 0; /* Not defined values */ } break; case PIOCWAIT: psp = (struct procfs_status *)ap->a_data; if (procp->p_step == 0) { error = tsleep(&procp->p_stype, PWAIT | PCATCH, "piocwait", 0); if (error) return error; } psp->state = 1; /* It stopped */ psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; psp->why = procp->p_stype; /* why it stopped */ psp->val = procp->p_xstat; /* any extra info */ break; case PIOCCONT: /* Restart a proc */ if (procp->p_step == 0) return EINVAL; /* Can only start a stopped process */ if ((signo = *(int*)ap->a_data) != 0) { if (signo >= NSIG || signo <= 0) return EINVAL; psignal(procp, signo); } procp->p_step = 0; wakeup(&procp->p_step); break; default: return (ENOTTY); } return 0; } /* * do block mapping for pfsnode (vp). * since we don't use the buffer cache * for procfs this function should never * be called. in any case, it's not clear * what part of the kernel ever makes use * of this function. for sanity, this is the * usual no-op bmap, although returning * (EIO) would be a reasonable alternative. */ static int procfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; return (0); } /* * procfs_inactive is called when the pfsnode * is vrele'd and the reference count goes * to zero. (vp) will be on the vnode free * list, so to get it back vget() must be * used. * * (vp) is locked on entry, but must be unlocked on exit. */ static int procfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; VOP_UNLOCK(vp, 0, ap->a_p); return (0); } /* * _reclaim is called when getnewvnode() * wants to make use of an entry on the vnode * free list. at this time the filesystem needs * to free any private data and remove the node * from any private lists. */ static int procfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { return (procfs_freevp(ap->a_vp)); } /* * _print is used for debugging. * just print a readable description * of (vp). */ static int procfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); printf("tag VT_PROCFS, type %d, pid %ld, mode %x, flags %lx\n", pfs->pfs_type, (long)pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags); return (0); } /* * _abortop is called when operations such as * rename and create fail. this entry is responsible * for undoing any side-effects caused by the lookup. * this will always include freeing the pathname buffer. */ static int procfs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) zfree(namei_zone, ap->a_cnp->cn_pnbuf); return (0); } /* * generic entry point for unsupported operations */ static int procfs_badop() { return (EIO); } /* * Invent attributes for pfsnode (vp) and store * them in (vap). * Directories lengths are returned as zero since * any real length would require the genuine size * to be computed, and nothing cares anyway. * * this is relatively minimal for procfs. */ static int procfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct vattr *vap = ap->a_vap; struct proc *procp; int error; /* * First make sure that the process and its credentials * still exist. */ switch (pfs->pfs_type) { case Proot: case Pcurproc: procp = 0; break; default: procp = PFIND(pfs->pfs_pid); if (procp == 0 || procp->p_cred == NULL || procp->p_ucred == NULL) return (ENOENT); } error = 0; /* start by zeroing out the attributes */ VATTR_NULL(vap); /* next do all the common fields */ vap->va_type = ap->a_vp->v_type; vap->va_mode = pfs->pfs_mode; vap->va_fileid = pfs->pfs_fileno; vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; /* * Make all times be current TOD. * It would be possible to get the process start * time from the p_stat structure, but there's * no "file creation" time stamp anyway, and the * p_stat structure is not addressible if u. gets * swapped out for that process. */ nanotime(&vap->va_ctime); vap->va_atime = vap->va_mtime = vap->va_ctime; /* * If the process has exercised some setuid or setgid * privilege, then rip away read/write permission so * that only root can gain access. */ switch (pfs->pfs_type) { case Pctl: case Pregs: case Pfpregs: case Pdbregs: if (procp->p_flag & P_SUGID) vap->va_mode &= ~((VREAD|VWRITE)| ((VREAD|VWRITE)>>3)| ((VREAD|VWRITE)>>6)); break; case Pmem: /* Retain group kmem readablity. */ if (procp->p_flag & P_SUGID) vap->va_mode &= ~(VREAD|VWRITE); break; default: break; } /* * now do the object specific fields * * The size could be set from struct reg, but it's hardly * worth the trouble, and it puts some (potentially) machine * dependent data into this machine-independent code. If it * becomes important then this function should break out into * a per-file stat function in the corresponding .c file. */ vap->va_nlink = 1; if (procp) { vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; } switch (pfs->pfs_type) { case Proot: /* * Set nlink to 1 to tell fts(3) we don't actually know. */ vap->va_nlink = 1; vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pcurproc: { char buf[16]; /* should be enough */ vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = snprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid); break; } case Pproc: vap->va_nlink = nproc_targets; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pfile: error = EOPNOTSUPP; break; case Pmem: /* * If we denied owner access earlier, then we have to * change the owner to root - otherwise 'ps' and friends * will break even though they are setgid kmem. *SIGH* */ if (procp->p_flag & P_SUGID) vap->va_uid = 0; else vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = KMEM_GROUP; break; case Pregs: vap->va_bytes = vap->va_size = sizeof(struct reg); break; case Pfpregs: vap->va_bytes = vap->va_size = sizeof(struct fpreg); break; case Pdbregs: vap->va_bytes = vap->va_size = sizeof(struct dbreg); break; case Ptype: case Pmap: case Pctl: case Pstatus: case Pnote: case Pnotepg: case Pcmdline: case Prlimit: break; default: panic("procfs_getattr"); } return (error); } static int procfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { if (ap->a_vap->va_flags != VNOVAL) return (EOPNOTSUPP); /* * just fake out attribute setting * it's not good to generate an error * return, otherwise things like creat() * will fail when they try to set the * file length to 0. worse, this means * that echo $note > /proc/$pid/note will fail. */ return (0); } /* * implement access checking. * * something very similar to this code is duplicated * throughout the 4bsd kernel and should be moved * into kern/vfs_subr.c sometime. * * actually, the check for super-user is slightly * broken since it will allow read access to write-only * objects. this doesn't cause any particular trouble * but does mean that the i/o entry points need to check * that the operation really does make sense. */ static int procfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vattr *vap; struct vattr vattr; int error; /* * If you're the super-user, * you always get access. */ if (ap->a_cred->cr_uid == 0) return (0); vap = &vattr; error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p); if (error) return (error); /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (ap->a_cred->cr_uid != vap->va_uid) { gid_t *gp; int i; ap->a_mode >>= 3; gp = ap->a_cred->cr_groups; for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; ap->a_mode >>= 3; found: ; } if ((vap->va_mode & ap->a_mode) == ap->a_mode) return (0); return (EACCES); } /* * lookup. this is incredibly complicated in the * general case, however for most pseudo-filesystems * very little needs to be done. * * unless you want to get a migraine, just make sure your * filesystem doesn't do any locking of its own. otherwise * read and inwardly digest ufs_lookup(). */ static int procfs_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct proc *curp = cnp->cn_proc; struct proc_target *pt; struct vnode *fvp; pid_t pid; struct pfsnode *pfs; struct proc *p; int i; *vpp = NULL; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /* vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, curp); */ return (0); } pfs = VTOPFS(dvp); switch (pfs->pfs_type) { case Proot: if (cnp->cn_flags & ISDOTDOT) return (EIO); if (CNEQ(cnp, "curproc", 7)) return (procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc)); pid = atopid(pname, cnp->cn_namelen); if (pid == NO_PID) break; p = PFIND(pid); if (p == 0) break; return (procfs_allocvp(dvp->v_mount, vpp, pid, Pproc)); case Pproc: if (cnp->cn_flags & ISDOTDOT) return (procfs_root(dvp->v_mount, vpp)); p = PFIND(pfs->pfs_pid); if (p == 0) break; for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) { if (cnp->cn_namelen == pt->pt_namlen && bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && (pt->pt_valid == NULL || (*pt->pt_valid)(p))) goto found; } break; found: if (pt->pt_pfstype == Pfile) { fvp = procfs_findtextvp(p); /* We already checked that it exists. */ VREF(fvp); vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, curp); *vpp = fvp; return (0); } return (procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, pt->pt_pfstype)); default: return (ENOTDIR); } return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS); } /* * Does this process have a text file? */ int procfs_validfile(p) struct proc *p; { return (procfs_findtextvp(p) != NULLVP); } /* * readdir() returns directory entries from pfsnode (vp). * * We generate just one directory entry at a time, as it would probably * not pay off to buffer several entries locally to save uiomove calls. */ static int procfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long **a_cookies; } */ *ap; { struct uio *uio = ap->a_uio; struct dirent d; struct dirent *dp = &d; struct pfsnode *pfs; int count, error, i, off; static u_int delen; if (!delen) { d.d_namlen = PROCFS_NAMELEN; delen = GENERIC_DIRSIZ(&d); } pfs = VTOPFS(ap->a_vp); off = (int)uio->uio_offset; if (off != uio->uio_offset || off < 0 || off % delen != 0 || uio->uio_resid < delen) return (EINVAL); error = 0; count = 0; i = off / delen; switch (pfs->pfs_type) { /* * this is for the process-specific sub-directories. * all that is needed to is copy out all the entries * from the procent[] table (top of this file). */ case Pproc: { struct proc *p; struct proc_target *pt; p = PFIND(pfs->pfs_pid); if (p == NULL) break; if (!PRISON_CHECK(curproc, p)) break; for (pt = &proc_targets[i]; uio->uio_resid >= delen && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(p) == 0) continue; dp->d_reclen = delen; dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype); dp->d_namlen = pt->pt_namlen; bcopy(pt->pt_name, dp->d_name, pt->pt_namlen + 1); dp->d_type = pt->pt_type; if ((error = uiomove((caddr_t)dp, delen, uio)) != 0) break; } break; } /* * this is for the root of the procfs filesystem * what is needed is a special entry for "curproc" * followed by an entry for each process on allproc #ifdef PROCFS_ZOMBIE * and zombproc. #endif */ case Proot: { #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif int pcnt = 0; volatile struct proc *p = allproc.lh_first; for (; p && uio->uio_resid >= delen; i++, pcnt++) { bzero((char *) dp, delen); dp->d_reclen = delen; switch (i) { case 0: /* `.' */ case 1: /* `..' */ dp->d_fileno = PROCFS_FILENO(0, Proot); dp->d_namlen = i + 1; bcopy("..", dp->d_name, dp->d_namlen); dp->d_name[i + 1] = '\0'; dp->d_type = DT_DIR; break; case 2: dp->d_fileno = PROCFS_FILENO(0, Pcurproc); dp->d_namlen = 7; bcopy("curproc", dp->d_name, 8); dp->d_type = DT_LNK; break; default: while (pcnt < i) { p = p->p_list.le_next; if (!p) goto done; if (!PRISON_CHECK(curproc, p)) continue; pcnt++; } while (!PRISON_CHECK(curproc, p)) { p = p->p_list.le_next; if (!p) goto done; } dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); dp->d_namlen = sprintf(dp->d_name, "%ld", (long)p->p_pid); dp->d_type = DT_REG; p = p->p_list.le_next; break; } if ((error = uiomove((caddr_t)dp, delen, uio)) != 0) break; } done: #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = zombproc.lh_first; goto again; } #endif break; } default: error = ENOTDIR; break; } uio->uio_offset = i * delen; return (error); } /* * readlink reads the link of `curproc' */ static int procfs_readlink(ap) struct vop_readlink_args *ap; { char buf[16]; /* should be enough */ int len; if (VTOPFS(ap->a_vp)->pfs_fileno != PROCFS_FILENO(0, Pcurproc)) return (EINVAL); len = snprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid); return (uiomove((caddr_t)buf, len, ap->a_uio)); } /* * convert decimal ascii to pid_t */ static pid_t atopid(b, len) const char *b; u_int len; { pid_t p = 0; while (len--) { char c = *b++; if (c < '0' || c > '9') return (NO_PID); p = 10 * p + (c - '0'); if (p > PID_MAX) return (NO_PID); } return (p); } /* * procfs vnode operations. */ vop_t **procfs_vnodeop_p; static struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_abortop_desc, (vop_t *) procfs_abortop }, { &vop_access_desc, (vop_t *) procfs_access }, { &vop_advlock_desc, (vop_t *) procfs_badop }, { &vop_bmap_desc, (vop_t *) procfs_bmap }, { &vop_close_desc, (vop_t *) procfs_close }, { &vop_create_desc, (vop_t *) procfs_badop }, { &vop_getattr_desc, (vop_t *) procfs_getattr }, { &vop_inactive_desc, (vop_t *) procfs_inactive }, { &vop_link_desc, (vop_t *) procfs_badop }, { &vop_lookup_desc, (vop_t *) procfs_lookup }, { &vop_mkdir_desc, (vop_t *) procfs_badop }, { &vop_mknod_desc, (vop_t *) procfs_badop }, { &vop_open_desc, (vop_t *) procfs_open }, { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, { &vop_print_desc, (vop_t *) procfs_print }, { &vop_read_desc, (vop_t *) procfs_rw }, { &vop_readdir_desc, (vop_t *) procfs_readdir }, { &vop_readlink_desc, (vop_t *) procfs_readlink }, { &vop_reclaim_desc, (vop_t *) procfs_reclaim }, { &vop_remove_desc, (vop_t *) procfs_badop }, { &vop_rename_desc, (vop_t *) procfs_badop }, { &vop_rmdir_desc, (vop_t *) procfs_badop }, { &vop_setattr_desc, (vop_t *) procfs_setattr }, { &vop_symlink_desc, (vop_t *) procfs_badop }, { &vop_write_desc, (vop_t *) procfs_rw }, { &vop_ioctl_desc, (vop_t *) procfs_ioctl }, { NULL, NULL } }; static struct vnodeopv_desc procfs_vnodeop_opv_desc = { &procfs_vnodeop_p, procfs_vnodeop_entries }; VNODEOP_SET(procfs_vnodeop_opv_desc); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 124bf02be123..e0f9ec104d27 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -1,687 +1,687 @@ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)kern_proc.c 8.7 (Berkeley) 2/14/95 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PGRP, "pgrp", "process group header"); MALLOC_DEFINE(M_SESSION, "session", "session header"); static MALLOC_DEFINE(M_PROC, "proc", "Proc structures"); MALLOC_DEFINE(M_SUBPROC, "subproc", "Proc sub-structures"); static void pgdelete __P((struct pgrp *)); /* * Structure associated with user cacheing. */ struct uidinfo { LIST_ENTRY(uidinfo) ui_hash; uid_t ui_uid; long ui_proccnt; rlim_t ui_sbsize; }; #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) static LIST_HEAD(uihashhead, uidinfo) *uihashtbl; static u_long uihash; /* size of hash table - 1 */ static void orphanpg __P((struct pgrp *pg)); /* * Other process lists */ struct pidhashhead *pidhashtbl; u_long pidhash; struct pgrphashhead *pgrphashtbl; u_long pgrphash; struct proclist allproc; struct proclist zombproc; vm_zone_t proc_zone; /* * Initialize global process hashing structures. */ void procinit() { LIST_INIT(&allproc); LIST_INIT(&zombproc); pidhashtbl = hashinit(maxproc / 4, M_PROC, &pidhash); pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash); uihashtbl = hashinit(maxproc / 16, M_PROC, &uihash); proc_zone = zinit("PROC", sizeof (struct proc), 0, 0, 5); } /* * Change the count associated with number of processes * a given user is using. */ int chgproccnt(uid, diff) uid_t uid; int diff; { register struct uidinfo *uip; register struct uihashhead *uipp; uipp = UIHASH(uid); LIST_FOREACH(uip, uipp, ui_hash) if (uip->ui_uid == uid) break; if (uip) { uip->ui_proccnt += diff; if (uip->ui_proccnt < 0) panic("chgproccnt: procs < 0"); if (uip->ui_proccnt > 0 || uip->ui_sbsize > 0) return (uip->ui_proccnt); LIST_REMOVE(uip, ui_hash); FREE(uip, M_PROC); return (0); } if (diff <= 0) { if (diff == 0) return(0); panic("chgproccnt: lost user"); } MALLOC(uip, struct uidinfo *, sizeof(*uip), M_PROC, M_WAITOK); LIST_INSERT_HEAD(uipp, uip, ui_hash); uip->ui_uid = uid; uip->ui_proccnt = diff; uip->ui_sbsize = 0; return (diff); } /* * Change the total socket buffer size a user has used. */ rlim_t chgsbsize(uid, diff) uid_t uid; rlim_t diff; { register struct uidinfo *uip; register struct uihashhead *uipp; uipp = UIHASH(uid); LIST_FOREACH(uip, uipp, ui_hash) if (uip->ui_uid == uid) break; if (diff <= 0) { if (diff == 0) return (uip ? uip->ui_sbsize : 0); KASSERT(uip != NULL, ("uidinfo (%d) gone", uid)); } if (uip) { uip->ui_sbsize += diff; if (uip->ui_sbsize == 0 && uip->ui_proccnt == 0) { LIST_REMOVE(uip, ui_hash); FREE(uip, M_PROC); return (0); } return (uip->ui_sbsize); } MALLOC(uip, struct uidinfo *, sizeof(*uip), M_PROC, M_WAITOK); LIST_INSERT_HEAD(uipp, uip, ui_hash); uip->ui_uid = uid; uip->ui_proccnt = 0; uip->ui_sbsize = diff; return (diff); } /* * Is p an inferior of the current process? */ int inferior(p) register struct proc *p; { for (; p != curproc; p = p->p_pptr) if (p->p_pid == 0) return (0); return (1); } /* * Locate a process by number */ struct proc * pfind(pid) register pid_t pid; { register struct proc *p; LIST_FOREACH(p, PIDHASH(pid), p_hash) if (p->p_pid == pid) return (p); return (NULL); } /* * Locate a process group by number */ struct pgrp * pgfind(pgid) register pid_t pgid; { register struct pgrp *pgrp; LIST_FOREACH(pgrp, PGRPHASH(pgid), pg_hash) if (pgrp->pg_id == pgid) return (pgrp); return (NULL); } /* * Move p to a new or existing process group (and session) */ int enterpgrp(p, pgid, mksess) register struct proc *p; pid_t pgid; int mksess; { register struct pgrp *pgrp = pgfind(pgid); KASSERT(pgrp == NULL || !mksess, ("enterpgrp: setsid into non-empty pgrp")); KASSERT(!SESS_LEADER(p), ("enterpgrp: session leader attempted setpgrp")); if (pgrp == NULL) { pid_t savepid = p->p_pid; struct proc *np; /* * new process group */ KASSERT(p->p_pid == pgid, ("enterpgrp: new pgrp and pid != pgid")); MALLOC(pgrp, struct pgrp *, sizeof(struct pgrp), M_PGRP, M_WAITOK); if ((np = pfind(savepid)) == NULL || np != p) return (ESRCH); if (mksess) { register struct session *sess; /* * new session */ MALLOC(sess, struct session *, sizeof(struct session), M_SESSION, M_WAITOK); sess->s_leader = p; sess->s_sid = p->p_pid; sess->s_count = 1; sess->s_ttyvp = NULL; sess->s_ttyp = NULL; bcopy(p->p_session->s_login, sess->s_login, sizeof(sess->s_login)); p->p_flag &= ~P_CONTROLT; pgrp->pg_session = sess; KASSERT(p == curproc, ("enterpgrp: mksession and p != curproc")); } else { pgrp->pg_session = p->p_session; pgrp->pg_session->s_count++; } pgrp->pg_id = pgid; LIST_INIT(&pgrp->pg_members); LIST_INSERT_HEAD(PGRPHASH(pgid), pgrp, pg_hash); pgrp->pg_jobc = 0; SLIST_INIT(&pgrp->pg_sigiolst); } else if (pgrp == p->p_pgrp) return (0); /* * Adjust eligibility of affected pgrps to participate in job control. * Increment eligibility counts before decrementing, otherwise we * could reach 0 spuriously during the first call. */ fixjobc(p, pgrp, 1); fixjobc(p, p->p_pgrp, 0); LIST_REMOVE(p, p_pglist); if (LIST_EMPTY(&p->p_pgrp->pg_members)) pgdelete(p->p_pgrp); p->p_pgrp = pgrp; LIST_INSERT_HEAD(&pgrp->pg_members, p, p_pglist); return (0); } /* * remove process from process group */ int leavepgrp(p) register struct proc *p; { LIST_REMOVE(p, p_pglist); if (LIST_EMPTY(&p->p_pgrp->pg_members)) pgdelete(p->p_pgrp); p->p_pgrp = 0; return (0); } /* * delete a process group */ static void pgdelete(pgrp) register struct pgrp *pgrp; { /* * Reset any sigio structures pointing to us as a result of * F_SETOWN with our pgid. */ funsetownlst(&pgrp->pg_sigiolst); if (pgrp->pg_session->s_ttyp != NULL && pgrp->pg_session->s_ttyp->t_pgrp == pgrp) pgrp->pg_session->s_ttyp->t_pgrp = NULL; LIST_REMOVE(pgrp, pg_hash); if (--pgrp->pg_session->s_count == 0) FREE(pgrp->pg_session, M_SESSION); FREE(pgrp, M_PGRP); } /* * Adjust pgrp jobc counters when specified process changes process group. * We count the number of processes in each process group that "qualify" * the group for terminal job control (those with a parent in a different * process group of the same session). If that count reaches zero, the * process group becomes orphaned. Check both the specified process' * process group and that of its children. * entering == 0 => p is leaving specified group. * entering == 1 => p is entering specified group. */ void fixjobc(p, pgrp, entering) register struct proc *p; register struct pgrp *pgrp; int entering; { register struct pgrp *hispgrp; register struct session *mysession = pgrp->pg_session; /* * Check p's parent to see whether p qualifies its own process * group; if so, adjust count for p's process group. */ if ((hispgrp = p->p_pptr->p_pgrp) != pgrp && hispgrp->pg_session == mysession) { if (entering) pgrp->pg_jobc++; else if (--pgrp->pg_jobc == 0) orphanpg(pgrp); } /* * Check this process' children to see whether they qualify * their process groups; if so, adjust counts for children's * process groups. */ LIST_FOREACH(p, &p->p_children, p_sibling) if ((hispgrp = p->p_pgrp) != pgrp && hispgrp->pg_session == mysession && p->p_stat != SZOMB) { if (entering) hispgrp->pg_jobc++; else if (--hispgrp->pg_jobc == 0) orphanpg(hispgrp); } } /* * A process group has become orphaned; * if there are any stopped processes in the group, * hang-up all process in that group. */ static void orphanpg(pg) struct pgrp *pg; { register struct proc *p; LIST_FOREACH(p, &pg->pg_members, p_pglist) { if (p->p_stat == SSTOP) { LIST_FOREACH(p, &pg->pg_members, p_pglist) { psignal(p, SIGHUP); psignal(p, SIGCONT); } return; } } } #include "opt_ddb.h" #ifdef DDB #include DB_SHOW_COMMAND(pgrpdump, pgrpdump) { register struct pgrp *pgrp; register struct proc *p; register int i; for (i = 0; i <= pgrphash; i++) { if (!LIST_EMPTY(&pgrphashtbl[i])) { printf("\tindx %d\n", i); LIST_FOREACH(pgrp, &pgrphashtbl[i], pg_hash) { printf( "\tpgrp %p, pgid %ld, sess %p, sesscnt %d, mem %p\n", (void *)pgrp, (long)pgrp->pg_id, (void *)pgrp->pg_session, pgrp->pg_session->s_count, (void *)LIST_FIRST(&pgrp->pg_members)); LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { printf("\t\tpid %ld addr %p pgrp %p\n", (long)p->p_pid, (void *)p, (void *)p->p_pgrp); } } } } } #endif /* DDB */ /* * Fill in an eproc structure for the specified process. */ void fill_eproc(p, ep) register struct proc *p; register struct eproc *ep; { register struct tty *tp; bzero(ep, sizeof(*ep)); ep->e_paddr = p; if (p->p_cred) { ep->e_pcred = *p->p_cred; if (p->p_ucred) ep->e_ucred = *p->p_ucred; } if (p->p_procsig) { ep->e_procsig = *p->p_procsig; } if (p->p_stat != SIDL && p->p_stat != SZOMB && p->p_vmspace != NULL) { register struct vmspace *vm = p->p_vmspace; ep->e_vm = *vm; ep->e_vm.vm_rssize = vmspace_resident_count(vm); /*XXX*/ } if ((p->p_flag & P_INMEM) && p->p_stats) ep->e_stats = *p->p_stats; if (p->p_pptr) ep->e_ppid = p->p_pptr->p_pid; if (p->p_pgrp) { ep->e_pgid = p->p_pgrp->pg_id; ep->e_jobc = p->p_pgrp->pg_jobc; ep->e_sess = p->p_pgrp->pg_session; if (ep->e_sess) { bcopy(ep->e_sess->s_login, ep->e_login, sizeof(ep->e_login)); if (ep->e_sess->s_ttyvp) ep->e_flag = EPROC_CTTY; if (p->p_session && SESS_LEADER(p)) ep->e_flag |= EPROC_SLEADER; } } if ((p->p_flag & P_CONTROLT) && (ep->e_sess != NULL) && ((tp = ep->e_sess->s_ttyp) != NULL)) { ep->e_tdev = dev2udev(tp->t_dev); ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; ep->e_tsess = tp->t_session; } else ep->e_tdev = NOUDEV; if (p->p_wmesg) { strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN); ep->e_wmesg[WMESGLEN] = 0; } } static struct proc * zpfind(pid_t pid) { struct proc *p; LIST_FOREACH(p, &zombproc, p_list) if (p->p_pid == pid) return (p); return (NULL); } static int sysctl_out_proc(struct proc *p, struct sysctl_req *req, int doingzomb) { struct eproc eproc; int error; pid_t pid = p->p_pid; fill_eproc(p, &eproc); error = SYSCTL_OUT(req,(caddr_t)p, sizeof(struct proc)); if (error) return (error); error = SYSCTL_OUT(req,(caddr_t)&eproc, sizeof(eproc)); if (error) return (error); if (!doingzomb && pid && (pfind(pid) != p)) return EAGAIN; if (doingzomb && zpfind(pid) != p) return EAGAIN; return (0); } static int sysctl_kern_proc SYSCTL_HANDLER_ARGS { int *name = (int*) arg1; u_int namelen = arg2; struct proc *p; int doingzomb; int error = 0; if (oidp->oid_number == KERN_PROC_PID) { if (namelen != 1) return (EINVAL); p = pfind((pid_t)name[0]); if (!p) return (0); if (!PRISON_CHECK(curproc, p)) return (0); error = sysctl_out_proc(p, req, 0); return (error); } if (oidp->oid_number == KERN_PROC_ALL && !namelen) ; else if (oidp->oid_number != KERN_PROC_ALL && namelen == 1) ; else return (EINVAL); if (!req->oldptr) { /* overestimate by 5 procs */ error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5); if (error) return (error); } for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) { if (!doingzomb) p = LIST_FIRST(&allproc); else p = LIST_FIRST(&zombproc); for (; p != 0; p = LIST_NEXT(p, p_list)) { /* * Skip embryonic processes. */ if (p->p_stat == SIDL) continue; /* * TODO - make more efficient (see notes below). * do by session. */ switch (oidp->oid_number) { case KERN_PROC_PGRP: /* could do this by traversing pgrp */ if (p->p_pgrp == NULL || p->p_pgrp->pg_id != (pid_t)name[0]) continue; break; case KERN_PROC_TTY: if ((p->p_flag & P_CONTROLT) == 0 || p->p_session == NULL || p->p_session->s_ttyp == NULL || dev2udev(p->p_session->s_ttyp->t_dev) != (udev_t)name[0]) continue; break; case KERN_PROC_UID: if (p->p_ucred == NULL || p->p_ucred->cr_uid != (uid_t)name[0]) continue; break; case KERN_PROC_RUID: if (p->p_ucred == NULL || p->p_cred->p_ruid != (uid_t)name[0]) continue; break; } if (!PRISON_CHECK(curproc, p)) continue; error = sysctl_out_proc(p, req, doingzomb); if (error) return (error); } } return (0); } /* * This sysctl allows a process to retrieve the argument list or process * title for another process without groping around in the address space * of the other process. It also allow a process to set its own "process * title to a string of its own choice. */ static int sysctl_kern_proc_args SYSCTL_HANDLER_ARGS { int *name = (int*) arg1; u_int namelen = arg2; struct proc *p; struct pargs *pa; int error = 0; if (namelen != 1) return (EINVAL); p = pfind((pid_t)name[0]); if (!p) return (0); - if (!PRISON_CHECK(curproc, p)) + if (p_trespass(curproc, p)) return (0); if (req->newptr && curproc != p) return (EPERM); if (req->oldptr && p->p_args != NULL) error = SYSCTL_OUT(req, p->p_args->ar_args, p->p_args->ar_length); if (req->newptr == NULL) return (error); if (p->p_args && --p->p_args->ar_ref == 0) FREE(p->p_args, M_PARGS); p->p_args = NULL; if (req->newlen + sizeof(struct pargs) > ps_arg_cache_limit) return (error); MALLOC(pa, struct pargs *, sizeof(struct pargs) + req->newlen, M_PARGS, M_WAITOK); pa->ar_ref = 1; pa->ar_length = req->newlen; error = SYSCTL_IN(req, pa->ar_args, req->newlen); if (!error) p->p_args = pa; else FREE(pa, M_PARGS); return (error); } SYSCTL_NODE(_kern, KERN_PROC, proc, CTLFLAG_RD, 0, "Process table"); SYSCTL_PROC(_kern_proc, KERN_PROC_ALL, all, CTLFLAG_RD|CTLTYPE_STRUCT, 0, 0, sysctl_kern_proc, "S,proc", "Return entire process table"); SYSCTL_NODE(_kern_proc, KERN_PROC_PGRP, pgrp, CTLFLAG_RD, sysctl_kern_proc, "Process table"); SYSCTL_NODE(_kern_proc, KERN_PROC_TTY, tty, CTLFLAG_RD, sysctl_kern_proc, "Process table"); SYSCTL_NODE(_kern_proc, KERN_PROC_UID, uid, CTLFLAG_RD, sysctl_kern_proc, "Process table"); SYSCTL_NODE(_kern_proc, KERN_PROC_RUID, ruid, CTLFLAG_RD, sysctl_kern_proc, "Process table"); SYSCTL_NODE(_kern_proc, KERN_PROC_PID, pid, CTLFLAG_RD, sysctl_kern_proc, "Process table"); SYSCTL_NODE(_kern_proc, KERN_PROC_ARGS, args, CTLFLAG_RW, sysctl_kern_proc_args, "Return process argument"); diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 63d4346c2694..1611cc3bb6bb 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -1,912 +1,937 @@ /* * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)kern_prot.c 8.6 (Berkeley) 1/21/94 * $FreeBSD$ */ /* * System calls related to processes and protection */ #include "opt_compat.h" #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_CRED, "cred", "credentials"); #ifndef _SYS_SYSPROTO_H_ struct getpid_args { int dummy; }; #endif /* ARGSUSED */ int getpid(p, uap) struct proc *p; struct getpid_args *uap; { p->p_retval[0] = p->p_pid; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_retval[1] = p->p_pptr->p_pid; #endif return (0); } #ifndef _SYS_SYSPROTO_H_ struct getppid_args { int dummy; }; #endif /* ARGSUSED */ int getppid(p, uap) struct proc *p; struct getppid_args *uap; { p->p_retval[0] = p->p_pptr->p_pid; return (0); } /* Get process group ID; note that POSIX getpgrp takes no parameter */ #ifndef _SYS_SYSPROTO_H_ struct getpgrp_args { int dummy; }; #endif int getpgrp(p, uap) struct proc *p; struct getpgrp_args *uap; { p->p_retval[0] = p->p_pgrp->pg_id; return (0); } /* Get an arbitary pid's process group id */ #ifndef _SYS_SYSPROTO_H_ struct getpgid_args { pid_t pid; }; #endif int getpgid(p, uap) struct proc *p; struct getpgid_args *uap; { struct proc *pt; pt = p; if (uap->pid == 0) goto found; if ((pt = pfind(uap->pid)) == 0) return ESRCH; found: p->p_retval[0] = pt->p_pgrp->pg_id; return 0; } /* * Get an arbitary pid's session id. */ #ifndef _SYS_SYSPROTO_H_ struct getsid_args { pid_t pid; }; #endif int getsid(p, uap) struct proc *p; struct getsid_args *uap; { struct proc *pt; pt = p; if (uap->pid == 0) goto found; if ((pt == pfind(uap->pid)) == 0) return ESRCH; found: p->p_retval[0] = pt->p_session->s_sid; return 0; } #ifndef _SYS_SYSPROTO_H_ struct getuid_args { int dummy; }; #endif /* ARGSUSED */ int getuid(p, uap) struct proc *p; struct getuid_args *uap; { p->p_retval[0] = p->p_cred->p_ruid; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_retval[1] = p->p_ucred->cr_uid; #endif return (0); } #ifndef _SYS_SYSPROTO_H_ struct geteuid_args { int dummy; }; #endif /* ARGSUSED */ int geteuid(p, uap) struct proc *p; struct geteuid_args *uap; { p->p_retval[0] = p->p_ucred->cr_uid; return (0); } #ifndef _SYS_SYSPROTO_H_ struct getgid_args { int dummy; }; #endif /* ARGSUSED */ int getgid(p, uap) struct proc *p; struct getgid_args *uap; { p->p_retval[0] = p->p_cred->p_rgid; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_retval[1] = p->p_ucred->cr_groups[0]; #endif return (0); } /* * Get effective group ID. The "egid" is groups[0], and could be obtained * via getgroups. This syscall exists because it is somewhat painful to do * correctly in a library function. */ #ifndef _SYS_SYSPROTO_H_ struct getegid_args { int dummy; }; #endif /* ARGSUSED */ int getegid(p, uap) struct proc *p; struct getegid_args *uap; { p->p_retval[0] = p->p_ucred->cr_groups[0]; return (0); } #ifndef _SYS_SYSPROTO_H_ struct getgroups_args { u_int gidsetsize; gid_t *gidset; }; #endif int getgroups(p, uap) struct proc *p; register struct getgroups_args *uap; { register struct pcred *pc = p->p_cred; register u_int ngrp; int error; if ((ngrp = uap->gidsetsize) == 0) { p->p_retval[0] = pc->pc_ucred->cr_ngroups; return (0); } if (ngrp < pc->pc_ucred->cr_ngroups) return (EINVAL); ngrp = pc->pc_ucred->cr_ngroups; if ((error = copyout((caddr_t)pc->pc_ucred->cr_groups, (caddr_t)uap->gidset, ngrp * sizeof(gid_t)))) return (error); p->p_retval[0] = ngrp; return (0); } #ifndef _SYS_SYSPROTO_H_ struct setsid_args { int dummy; }; #endif /* ARGSUSED */ int setsid(p, uap) register struct proc *p; struct setsid_args *uap; { if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) { return (EPERM); } else { (void)enterpgrp(p, p->p_pid, 1); p->p_retval[0] = p->p_pid; return (0); } } /* * set process group (setpgid/old setpgrp) * * caller does setpgid(targpid, targpgid) * * pid must be caller or child of caller (ESRCH) * if a child * pid must be in same session (EPERM) * pid can't have done an exec (EACCES) * if pgid != pid * there must exist some pid in same session having pgid (EPERM) * pid must not be session leader (EPERM) */ #ifndef _SYS_SYSPROTO_H_ struct setpgid_args { int pid; /* target process id */ int pgid; /* target pgrp id */ }; #endif /* ARGSUSED */ int setpgid(curp, uap) struct proc *curp; register struct setpgid_args *uap; { register struct proc *targp; /* target process */ register struct pgrp *pgrp; /* target pgrp */ if (uap->pgid < 0) return (EINVAL); if (uap->pid != 0 && uap->pid != curp->p_pid) { if ((targp = pfind(uap->pid)) == 0 || !inferior(targp)) return (ESRCH); if (targp->p_pgrp == NULL || targp->p_session != curp->p_session) return (EPERM); if (targp->p_flag & P_EXEC) return (EACCES); } else targp = curp; if (SESS_LEADER(targp)) return (EPERM); if (uap->pgid == 0) uap->pgid = targp->p_pid; else if (uap->pgid != targp->p_pid) if ((pgrp = pgfind(uap->pgid)) == 0 || pgrp->pg_session != curp->p_session) return (EPERM); return (enterpgrp(targp, uap->pgid, 0)); } /* * Use the clause in B.4.2.2 that allows setuid/setgid to be 4.2/4.3BSD * compatable. It says that setting the uid/gid to euid/egid is a special * case of "appropriate privilege". Once the rules are expanded out, this * basically means that setuid(nnn) sets all three id's, in all permitted * cases unless _POSIX_SAVED_IDS is enabled. In that case, setuid(getuid()) * does not set the saved id - this is dangerous for traditional BSD * programs. For this reason, we *really* do not want to set * _POSIX_SAVED_IDS and do not want to clear POSIX_APPENDIX_B_4_2_2. */ #define POSIX_APPENDIX_B_4_2_2 #ifndef _SYS_SYSPROTO_H_ struct setuid_args { uid_t uid; }; #endif /* ARGSUSED */ int setuid(p, uap) struct proc *p; struct setuid_args *uap; { register struct pcred *pc = p->p_cred; register uid_t uid; int error; /* * See if we have "permission" by POSIX 1003.1 rules. * * Note that setuid(geteuid()) is a special case of * "appropriate privileges" in appendix B.4.2.2. We need * to use this clause to be compatable with traditional BSD * semantics. Basically, it means that "setuid(xx)" sets all * three id's (assuming you have privs). * * Notes on the logic. We do things in three steps. * 1: We determine if the euid is going to change, and do EPERM * right away. We unconditionally change the euid later if this * test is satisfied, simplifying that part of the logic. * 2: We determine if the real and/or saved uid's are going to * change. Determined by compile options. * 3: Change euid last. (after tests in #2 for "appropriate privs") */ uid = uap->uid; if (uid != pc->p_ruid && /* allow setuid(getuid()) */ #ifdef _POSIX_SAVED_IDS uid != pc->p_svuid && /* allow setuid(saved gid) */ #endif #ifdef POSIX_APPENDIX_B_4_2_2 /* Use BSD-compat clause from B.4.2.2 */ uid != pc->pc_ucred->cr_uid && /* allow setuid(geteuid()) */ #endif (error = suser_xxx(0, p, PRISON_ROOT))) return (error); #ifdef _POSIX_SAVED_IDS /* * Do we have "appropriate privileges" (are we root or uid == euid) * If so, we are changing the real uid and/or saved uid. */ if ( #ifdef POSIX_APPENDIX_B_4_2_2 /* Use the clause from B.4.2.2 */ uid == pc->pc_ucred->cr_uid || #endif suser_xxx(0, p, PRISON_ROOT) == 0) /* we are using privs */ #endif { /* * Transfer proc count to new user. */ if (uid != pc->p_ruid) { (void)chgproccnt(pc->p_ruid, -1); (void)chgproccnt(uid, 1); } /* * Set real uid */ if (uid != pc->p_ruid) { pc->p_ruid = uid; setsugid(p); } /* * Set saved uid * * XXX always set saved uid even if not _POSIX_SAVED_IDS, as * the security of seteuid() depends on it. B.4.2.2 says it * is important that we should do this. */ if (pc->p_svuid != uid) { pc->p_svuid = uid; setsugid(p); } } /* * In all permitted cases, we are changing the euid. * Copy credentials so other references do not see our changes. */ if (pc->pc_ucred->cr_uid != uid) { pc->pc_ucred = crcopy(pc->pc_ucred); pc->pc_ucred->cr_uid = uid; setsugid(p); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct seteuid_args { uid_t euid; }; #endif /* ARGSUSED */ int seteuid(p, uap) struct proc *p; struct seteuid_args *uap; { register struct pcred *pc = p->p_cred; register uid_t euid; int error; euid = uap->euid; if (euid != pc->p_ruid && /* allow seteuid(getuid()) */ euid != pc->p_svuid && /* allow seteuid(saved uid) */ (error = suser_xxx(0, p, PRISON_ROOT))) return (error); /* * Everything's okay, do it. Copy credentials so other references do * not see our changes. */ if (pc->pc_ucred->cr_uid != euid) { pc->pc_ucred = crcopy(pc->pc_ucred); pc->pc_ucred->cr_uid = euid; setsugid(p); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct setgid_args { gid_t gid; }; #endif /* ARGSUSED */ int setgid(p, uap) struct proc *p; struct setgid_args *uap; { register struct pcred *pc = p->p_cred; register gid_t gid; int error; /* * See if we have "permission" by POSIX 1003.1 rules. * * Note that setgid(getegid()) is a special case of * "appropriate privileges" in appendix B.4.2.2. We need * to use this clause to be compatable with traditional BSD * semantics. Basically, it means that "setgid(xx)" sets all * three id's (assuming you have privs). * * For notes on the logic here, see setuid() above. */ gid = uap->gid; if (gid != pc->p_rgid && /* allow setgid(getgid()) */ #ifdef _POSIX_SAVED_IDS gid != pc->p_svgid && /* allow setgid(saved gid) */ #endif #ifdef POSIX_APPENDIX_B_4_2_2 /* Use BSD-compat clause from B.4.2.2 */ gid != pc->pc_ucred->cr_groups[0] && /* allow setgid(getegid()) */ #endif (error = suser_xxx(0, p, PRISON_ROOT))) return (error); #ifdef _POSIX_SAVED_IDS /* * Do we have "appropriate privileges" (are we root or gid == egid) * If so, we are changing the real uid and saved gid. */ if ( #ifdef POSIX_APPENDIX_B_4_2_2 /* use the clause from B.4.2.2 */ gid == pc->pc_ucred->cr_groups[0] || #endif suser_xxx(0, p, PRISON_ROOT) == 0) /* we are using privs */ #endif { /* * Set real gid */ if (pc->p_rgid != gid) { pc->p_rgid = gid; setsugid(p); } /* * Set saved gid * * XXX always set saved gid even if not _POSIX_SAVED_IDS, as * the security of setegid() depends on it. B.4.2.2 says it * is important that we should do this. */ if (pc->p_svgid != gid) { pc->p_svgid = gid; setsugid(p); } } /* * In all cases permitted cases, we are changing the egid. * Copy credentials so other references do not see our changes. */ if (pc->pc_ucred->cr_groups[0] != gid) { pc->pc_ucred = crcopy(pc->pc_ucred); pc->pc_ucred->cr_groups[0] = gid; setsugid(p); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct setegid_args { gid_t egid; }; #endif /* ARGSUSED */ int setegid(p, uap) struct proc *p; struct setegid_args *uap; { register struct pcred *pc = p->p_cred; register gid_t egid; int error; egid = uap->egid; if (egid != pc->p_rgid && /* allow setegid(getgid()) */ egid != pc->p_svgid && /* allow setegid(saved gid) */ (error = suser_xxx(0, p, PRISON_ROOT))) return (error); if (pc->pc_ucred->cr_groups[0] != egid) { pc->pc_ucred = crcopy(pc->pc_ucred); pc->pc_ucred->cr_groups[0] = egid; setsugid(p); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct setgroups_args { u_int gidsetsize; gid_t *gidset; }; #endif /* ARGSUSED */ int setgroups(p, uap) struct proc *p; struct setgroups_args *uap; { register struct pcred *pc = p->p_cred; register u_int ngrp; int error; if ((error = suser_xxx(0, p, PRISON_ROOT))) return (error); ngrp = uap->gidsetsize; if (ngrp > NGROUPS) return (EINVAL); /* * XXX A little bit lazy here. We could test if anything has * changed before crcopy() and setting P_SUGID. */ pc->pc_ucred = crcopy(pc->pc_ucred); if (ngrp < 1) { /* * setgroups(0, NULL) is a legitimate way of clearing the * groups vector on non-BSD systems (which generally do not * have the egid in the groups[0]). We risk security holes * when running non-BSD software if we do not do the same. */ pc->pc_ucred->cr_ngroups = 1; } else { if ((error = copyin((caddr_t)uap->gidset, (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t)))) return (error); pc->pc_ucred->cr_ngroups = ngrp; } setsugid(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct setreuid_args { uid_t ruid; uid_t euid; }; #endif /* ARGSUSED */ int setreuid(p, uap) register struct proc *p; struct setreuid_args *uap; { register struct pcred *pc = p->p_cred; register uid_t ruid, euid; int error; ruid = uap->ruid; euid = uap->euid; if (((ruid != (uid_t)-1 && ruid != pc->p_ruid && ruid != pc->p_svuid) || (euid != (uid_t)-1 && euid != pc->pc_ucred->cr_uid && euid != pc->p_ruid && euid != pc->p_svuid)) && (error = suser_xxx(0, p, PRISON_ROOT)) != 0) return (error); if (euid != (uid_t)-1 && pc->pc_ucred->cr_uid != euid) { pc->pc_ucred = crcopy(pc->pc_ucred); pc->pc_ucred->cr_uid = euid; setsugid(p); } if (ruid != (uid_t)-1 && pc->p_ruid != ruid) { (void)chgproccnt(pc->p_ruid, -1); (void)chgproccnt(ruid, 1); pc->p_ruid = ruid; setsugid(p); } if ((ruid != (uid_t)-1 || pc->pc_ucred->cr_uid != pc->p_ruid) && pc->p_svuid != pc->pc_ucred->cr_uid) { pc->p_svuid = pc->pc_ucred->cr_uid; setsugid(p); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct setregid_args { gid_t rgid; gid_t egid; }; #endif /* ARGSUSED */ int setregid(p, uap) register struct proc *p; struct setregid_args *uap; { register struct pcred *pc = p->p_cred; register gid_t rgid, egid; int error; rgid = uap->rgid; egid = uap->egid; if (((rgid != (gid_t)-1 && rgid != pc->p_rgid && rgid != pc->p_svgid) || (egid != (gid_t)-1 && egid != pc->pc_ucred->cr_groups[0] && egid != pc->p_rgid && egid != pc->p_svgid)) && (error = suser_xxx(0, p, PRISON_ROOT)) != 0) return (error); if (egid != (gid_t)-1 && pc->pc_ucred->cr_groups[0] != egid) { pc->pc_ucred = crcopy(pc->pc_ucred); pc->pc_ucred->cr_groups[0] = egid; setsugid(p); } if (rgid != (gid_t)-1 && pc->p_rgid != rgid) { pc->p_rgid = rgid; setsugid(p); } if ((rgid != (gid_t)-1 || pc->pc_ucred->cr_groups[0] != pc->p_rgid) && pc->p_svgid != pc->pc_ucred->cr_groups[0]) { pc->p_svgid = pc->pc_ucred->cr_groups[0]; setsugid(p); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct issetugid_args { int dummy; }; #endif /* ARGSUSED */ int issetugid(p, uap) register struct proc *p; struct issetugid_args *uap; { /* * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, * we use P_SUGID because we consider changing the owners as * "tainting" as well. * This is significant for procs that start as root and "become" * a user without an exec - programs cannot know *everything* * that libc *might* have put in their data segment. */ if (p->p_flag & P_SUGID) return (1); return (0); } /* * Check if gid is a member of the group set. */ int groupmember(gid, cred) gid_t gid; register struct ucred *cred; { register gid_t *gp; gid_t *egp; egp = &(cred->cr_groups[cred->cr_ngroups]); for (gp = cred->cr_groups; gp < egp; gp++) if (*gp == gid) return (1); return (0); } /* * Test whether the specified credentials imply "super-user" * privilege; if so, and we have accounting info, set the flag * indicating use of super-powers. * Returns 0 or error. */ int suser(p) struct proc *p; { return suser_xxx(0, p, 0); } int suser_xxx(cred, proc, flag) struct ucred *cred; struct proc *proc; int flag; { if (!cred && !proc) { printf("suser_xxx(): THINK!\n"); return (EPERM); } if (!cred) cred = proc->p_ucred; if (cred->cr_uid != 0) return (EPERM); if (proc && proc->p_prison && !(flag & PRISON_ROOT)) return (EPERM); if (proc) proc->p_acflag |= ASU; return (0); } +/* + * Return zero if p1 can fondle p2, return errno (EPERM/ESRCH) otherwise. + */ + +int +p_trespass(struct proc *p1, struct proc *p2) +{ + + if (p1 == p2) + return (0); + if (!PRISON_CHECK(p1, p2)) + return (ESRCH); + if (p1->p_cred->p_ruid == p2->p_cred->p_ruid) + return (0); + if (p1->p_ucred->cr_uid == p2->p_cred->p_ruid) + return (0); + if (p1->p_cred->p_ruid == p2->p_ucred->cr_uid) + return (0); + if (p1->p_ucred->cr_uid == p2->p_ucred->cr_uid) + return (0); + if (!suser_xxx(0, p1, PRISON_ROOT)) + return (0); + return (EPERM); +} + /* * Allocate a zeroed cred structure. */ struct ucred * crget() { register struct ucred *cr; MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK); bzero((caddr_t)cr, sizeof(*cr)); cr->cr_ref = 1; return (cr); } /* * Free a cred structure. * Throws away space when ref count gets to 0. */ void crfree(cr) struct ucred *cr; { if (--cr->cr_ref == 0) FREE((caddr_t)cr, M_CRED); } /* * Copy cred structure to a new one and free the old one. */ struct ucred * crcopy(cr) struct ucred *cr; { struct ucred *newcr; if (cr->cr_ref == 1) return (cr); newcr = crget(); *newcr = *cr; crfree(cr); newcr->cr_ref = 1; return (newcr); } /* * Dup cred struct to a new held one. */ struct ucred * crdup(cr) struct ucred *cr; { struct ucred *newcr; newcr = crget(); *newcr = *cr; newcr->cr_ref = 1; return (newcr); } /* * Get login name, if available. */ #ifndef _SYS_SYSPROTO_H_ struct getlogin_args { char *namebuf; u_int namelen; }; #endif /* ARGSUSED */ int getlogin(p, uap) struct proc *p; struct getlogin_args *uap; { if (uap->namelen > MAXLOGNAME) uap->namelen = MAXLOGNAME; return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, (caddr_t) uap->namebuf, uap->namelen)); } /* * Set login name. */ #ifndef _SYS_SYSPROTO_H_ struct setlogin_args { char *namebuf; }; #endif /* ARGSUSED */ int setlogin(p, uap) struct proc *p; struct setlogin_args *uap; { int error; char logintmp[MAXLOGNAME]; if ((error = suser_xxx(0, p, PRISON_ROOT))) return (error); error = copyinstr((caddr_t) uap->namebuf, (caddr_t) logintmp, sizeof(logintmp), (size_t *)0); if (error == ENAMETOOLONG) error = EINVAL; else if (!error) (void) memcpy(p->p_pgrp->pg_session->s_login, logintmp, sizeof(logintmp)); return (error); } void setsugid(p) struct proc *p; { p->p_flag |= P_SUGID; if (!(p->p_pfsflags & PF_ISUGID)) p->p_stops = 0; } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index a7b6499c8026..cf27029906d2 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,1683 +1,1677 @@ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)kern_sig.c 8.7 (Berkeley) 4/18/94 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SMP #include #endif #define ONSIG 32 /* NSIG for osig* syscalls. XXX. */ static int coredump __P((struct proc *)); static int do_sigaction __P((struct proc *p, int sig, struct sigaction *act, struct sigaction *oact, int old)); static int do_sigprocmask __P((struct proc *p, int how, sigset_t *set, sigset_t *oset, int old)); static char *expand_name __P((const char *, uid_t, pid_t)); static int killpg1 __P((struct proc *cp, int sig, int pgid, int all)); static int sig_ffs __P((sigset_t *set)); static int sigprop __P((int sig)); static void stop __P((struct proc *)); static int kern_logsigexit = 1; SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, &kern_logsigexit, 0, "Log processes quitting on abnormal signals to syslog(3)"); /* * Can process p, with pcred pc, send the signal sig to process q? */ -#define CANSIGNAL(p, pc, q, sig) \ - (PRISON_CHECK(p, q) && ((pc)->pc_ucred->cr_uid == 0 || \ - (pc)->p_ruid == (q)->p_cred->p_ruid || \ - (pc)->pc_ucred->cr_uid == (q)->p_cred->p_ruid || \ - (pc)->p_ruid == (q)->p_ucred->cr_uid || \ - (pc)->pc_ucred->cr_uid == (q)->p_ucred->cr_uid || \ - ((sig) == SIGCONT && (q)->p_session == (p)->p_session))) +#define CANSIGNAL(p, q, sig) \ + (!p_trespass(p, q) || \ + ((sig) == SIGCONT && (q)->p_session == (p)->p_session)) /* * Policy -- Can real uid ruid with ucred uc send a signal to process q? */ #define CANSIGIO(ruid, uc, q) \ ((uc)->cr_uid == 0 || \ (ruid) == (q)->p_cred->p_ruid || \ (uc)->cr_uid == (q)->p_cred->p_ruid || \ (ruid) == (q)->p_ucred->cr_uid || \ (uc)->cr_uid == (q)->p_ucred->cr_uid) int sugid_coredump; SYSCTL_INT(_kern, OID_AUTO, sugid_coredump, CTLFLAG_RW, &sugid_coredump, 0, "Enable coredumping set user/group ID processes"); /* * Signal properties and actions. * The array below categorizes the signals and their default actions * according to the following properties: */ #define SA_KILL 0x01 /* terminates process by default */ #define SA_CORE 0x02 /* ditto and coredumps */ #define SA_STOP 0x04 /* suspend process */ #define SA_TTYSTOP 0x08 /* ditto, from tty */ #define SA_IGNORE 0x10 /* ignore by default */ #define SA_CONT 0x20 /* continue if suspended */ #define SA_CANTMASK 0x40 /* non-maskable, catchable */ static int sigproptbl[NSIG] = { SA_KILL, /* SIGHUP */ SA_KILL, /* SIGINT */ SA_KILL|SA_CORE, /* SIGQUIT */ SA_KILL|SA_CORE, /* SIGILL */ SA_KILL|SA_CORE, /* SIGTRAP */ SA_KILL|SA_CORE, /* SIGABRT */ SA_KILL|SA_CORE, /* SIGEMT */ SA_KILL|SA_CORE, /* SIGFPE */ SA_KILL, /* SIGKILL */ SA_KILL|SA_CORE, /* SIGBUS */ SA_KILL|SA_CORE, /* SIGSEGV */ SA_KILL|SA_CORE, /* SIGSYS */ SA_KILL, /* SIGPIPE */ SA_KILL, /* SIGALRM */ SA_KILL, /* SIGTERM */ SA_IGNORE, /* SIGURG */ SA_STOP, /* SIGSTOP */ SA_STOP|SA_TTYSTOP, /* SIGTSTP */ SA_IGNORE|SA_CONT, /* SIGCONT */ SA_IGNORE, /* SIGCHLD */ SA_STOP|SA_TTYSTOP, /* SIGTTIN */ SA_STOP|SA_TTYSTOP, /* SIGTTOU */ SA_IGNORE, /* SIGIO */ SA_KILL, /* SIGXCPU */ SA_KILL, /* SIGXFSZ */ SA_KILL, /* SIGVTALRM */ SA_KILL, /* SIGPROF */ SA_IGNORE, /* SIGWINCH */ SA_IGNORE, /* SIGINFO */ SA_KILL, /* SIGUSR1 */ SA_KILL, /* SIGUSR2 */ }; static __inline int sigprop(int sig) { if (sig > 0 && sig < NSIG) return (sigproptbl[_SIG_IDX(sig)]); return (0); } static __inline int sig_ffs(sigset_t *set) { int i; for (i = 0; i < _SIG_WORDS; i++) if (set->__bits[i]) return (ffs(set->__bits[i]) + (i * 32)); return (0); } /* * do_sigaction * sigaction * osigaction */ static int do_sigaction(p, sig, act, oact, old) struct proc *p; register int sig; struct sigaction *act, *oact; int old; { register struct sigacts *ps = p->p_sigacts; if (sig <= 0 || sig > _SIG_MAXSIG) return (EINVAL); if (oact) { oact->sa_handler = ps->ps_sigact[_SIG_IDX(sig)]; oact->sa_mask = ps->ps_catchmask[_SIG_IDX(sig)]; oact->sa_flags = 0; if (SIGISMEMBER(ps->ps_sigonstack, sig)) oact->sa_flags |= SA_ONSTACK; if (!SIGISMEMBER(ps->ps_sigintr, sig)) oact->sa_flags |= SA_RESTART; if (SIGISMEMBER(ps->ps_sigreset, sig)) oact->sa_flags |= SA_RESETHAND; if (SIGISMEMBER(ps->ps_signodefer, sig)) oact->sa_flags |= SA_NODEFER; if (SIGISMEMBER(ps->ps_siginfo, sig)) oact->sa_flags |= SA_SIGINFO; if (sig == SIGCHLD && p->p_procsig->ps_flag & PS_NOCLDSTOP) oact->sa_flags |= SA_NOCLDSTOP; if (sig == SIGCHLD && p->p_procsig->ps_flag & PS_NOCLDWAIT) oact->sa_flags |= SA_NOCLDWAIT; } if (act) { if ((sig == SIGKILL || sig == SIGSTOP) && act->sa_handler != SIG_DFL) return (EINVAL); /* * Change setting atomically. */ (void) splhigh(); ps->ps_catchmask[_SIG_IDX(sig)] = act->sa_mask; SIG_CANTMASK(ps->ps_catchmask[_SIG_IDX(sig)]); if (act->sa_flags & SA_SIGINFO) { ps->ps_sigact[_SIG_IDX(sig)] = act->sa_handler; SIGADDSET(ps->ps_siginfo, sig); } else { ps->ps_sigact[_SIG_IDX(sig)] = (__sighandler_t *)act->sa_sigaction; SIGDELSET(ps->ps_siginfo, sig); } if (!(act->sa_flags & SA_RESTART)) SIGADDSET(ps->ps_sigintr, sig); else SIGDELSET(ps->ps_sigintr, sig); if (act->sa_flags & SA_ONSTACK) SIGADDSET(ps->ps_sigonstack, sig); else SIGDELSET(ps->ps_sigonstack, sig); if (act->sa_flags & SA_RESETHAND) SIGADDSET(ps->ps_sigreset, sig); else SIGDELSET(ps->ps_sigreset, sig); if (act->sa_flags & SA_NODEFER) SIGADDSET(ps->ps_signodefer, sig); else SIGDELSET(ps->ps_signodefer, sig); #ifdef COMPAT_SUNOS if (act->sa_flags & SA_USERTRAMP) SIGADDSET(ps->ps_usertramp, sig); else SIGDELSET(ps->ps_usertramp, seg); #endif if (sig == SIGCHLD) { if (act->sa_flags & SA_NOCLDSTOP) p->p_procsig->ps_flag |= PS_NOCLDSTOP; else p->p_procsig->ps_flag &= ~PS_NOCLDSTOP; if (act->sa_flags & SA_NOCLDWAIT) { /* * Paranoia: since SA_NOCLDWAIT is implemented * by reparenting the dying child to PID 1 (and * trust it to reap the zombie), PID 1 itself * is forbidden to set SA_NOCLDWAIT. */ if (p->p_pid == 1) p->p_procsig->ps_flag &= ~PS_NOCLDWAIT; else p->p_procsig->ps_flag |= PS_NOCLDWAIT; } else p->p_procsig->ps_flag &= ~PS_NOCLDWAIT; } /* * Set bit in p_sigignore for signals that are set to SIG_IGN, * and for signals set to SIG_DFL where the default is to * ignore. However, don't put SIGCONT in p_sigignore, as we * have to restart the process. */ if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || (sigprop(sig) & SA_IGNORE && ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL)) { /* never to be seen again */ SIGDELSET(p->p_siglist, sig); if (sig != SIGCONT) /* easier in psignal */ SIGADDSET(p->p_sigignore, sig); SIGDELSET(p->p_sigcatch, sig); } else { SIGDELSET(p->p_sigignore, sig); if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL) SIGDELSET(p->p_sigcatch, sig); else SIGADDSET(p->p_sigcatch, sig); } if (ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN || ps->ps_sigact[_SIG_IDX(sig)] == SIG_DFL || !old) SIGDELSET(ps->ps_osigset, sig); else SIGADDSET(ps->ps_osigset, sig); (void) spl0(); } return (0); } #ifndef _SYS_SYSPROTO_H_ struct sigaction_args { int sig; struct sigaction *act; struct sigaction *oact; }; #endif /* ARGSUSED */ int sigaction(p, uap) struct proc *p; register struct sigaction_args *uap; { struct sigaction act, oact; register struct sigaction *actp, *oactp; int error; actp = (uap->act != NULL) ? &act : NULL; oactp = (uap->oact != NULL) ? &oact : NULL; if (actp) { error = copyin(uap->act, actp, sizeof(act)); if (error) return (error); } error = do_sigaction(p, uap->sig, actp, oactp, 0); if (oactp && !error) { error = copyout(oactp, uap->oact, sizeof(oact)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigaction_args { int signum; struct osigaction *nsa; struct osigaction *osa; }; #endif /* ARGSUSED */ int osigaction(p, uap) struct proc *p; register struct osigaction_args *uap; { struct osigaction sa; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsa != NULL) ? &nsa : NULL; osap = (uap->osa != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsa, &sa, sizeof(sa)); if (error) return (error); nsap->sa_handler = sa.sa_handler; nsap->sa_flags = sa.sa_flags; OSIG2SIG(sa.sa_mask, nsap->sa_mask); } error = do_sigaction(p, uap->signum, nsap, osap, 1); if (osap && !error) { sa.sa_handler = osap->sa_handler; sa.sa_flags = osap->sa_flags; SIG2OSIG(osap->sa_mask, sa.sa_mask); error = copyout(&sa, uap->osa, sizeof(sa)); } return (error); } /* * Initialize signal state for process 0; * set to ignore signals that are ignored by default. */ void siginit(p) struct proc *p; { register int i; for (i = 1; i <= NSIG; i++) if (sigprop(i) & SA_IGNORE && i != SIGCONT) SIGADDSET(p->p_sigignore, i); } /* * Reset signals for an exec of the specified process. */ void execsigs(p) register struct proc *p; { register struct sigacts *ps = p->p_sigacts; register int sig; /* * Reset caught signals. Held signals remain held * through p_sigmask (unless they were caught, * and are now ignored by default). */ while (SIGNOTEMPTY(p->p_sigcatch)) { sig = sig_ffs(&p->p_sigcatch); SIGDELSET(p->p_sigcatch, sig); if (sigprop(sig) & SA_IGNORE) { if (sig != SIGCONT) SIGADDSET(p->p_sigignore, sig); SIGDELSET(p->p_siglist, sig); } ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } /* * Reset stack state to the user stack. * Clear set of signals caught on the signal stack. */ p->p_sigstk.ss_flags = SS_DISABLE; p->p_sigstk.ss_size = 0; p->p_sigstk.ss_sp = 0; /* * Reset no zombies if child dies flag as Solaris does. */ p->p_procsig->ps_flag &= ~PS_NOCLDWAIT; } /* * Manipulate signal mask. * Note that we receive new mask, not pointer, * and return old mask as return value; * the library stub does the rest. */ static int do_sigprocmask(p, how, set, oset, old) struct proc *p; int how; sigset_t *set, *oset; int old; { int error; if (oset != NULL) *oset = p->p_sigmask; error = 0; if (set != NULL) { (void) splhigh(); switch (how) { case SIG_BLOCK: SIG_CANTMASK(*set); SIGSETOR(p->p_sigmask, *set); break; case SIG_UNBLOCK: SIGSETNAND(p->p_sigmask, *set); break; case SIG_SETMASK: SIG_CANTMASK(*set); if (old) SIGSETLO(p->p_sigmask, *set); else p->p_sigmask = *set; break; default: error = EINVAL; break; } (void) spl0(); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct sigprocmask_args { int how; const sigset_t *set; sigset_t *oset; }; #endif int sigprocmask(p, uap) register struct proc *p; struct sigprocmask_args *uap; { sigset_t set, oset; sigset_t *setp, *osetp; int error; setp = (uap->set != NULL) ? &set : NULL; osetp = (uap->oset != NULL) ? &oset : NULL; if (setp) { error = copyin(uap->set, setp, sizeof(set)); if (error) return (error); } error = do_sigprocmask(p, uap->how, setp, osetp, 0); if (osetp && !error) { error = copyout(osetp, uap->oset, sizeof(oset)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigprocmask_args { int how; osigset_t mask; }; #endif int osigprocmask(p, uap) register struct proc *p; struct osigprocmask_args *uap; { sigset_t set, oset; int error; OSIG2SIG(uap->mask, set); error = do_sigprocmask(p, uap->how, &set, &oset, 1); SIG2OSIG(oset, p->p_retval[0]); return (error); } #ifndef _SYS_SYSPROTO_H_ struct sigpending_args { sigset_t *set; }; #endif /* ARGSUSED */ int sigpending(p, uap) struct proc *p; struct sigpending_args *uap; { return (copyout(&p->p_siglist, uap->set, sizeof(sigset_t))); } #ifndef _SYS_SYSPROTO_H_ struct osigpending_args { int dummy; }; #endif /* ARGSUSED */ int osigpending(p, uap) struct proc *p; struct osigpending_args *uap; { SIG2OSIG(p->p_siglist, p->p_retval[0]); return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Generalized interface signal handler, 4.3-compatible. */ #ifndef _SYS_SYSPROTO_H_ struct osigvec_args { int signum; struct sigvec *nsv; struct sigvec *osv; }; #endif /* ARGSUSED */ int osigvec(p, uap) struct proc *p; register struct osigvec_args *uap; { struct sigvec vec; struct sigaction nsa, osa; register struct sigaction *nsap, *osap; int error; if (uap->signum <= 0 || uap->signum >= ONSIG) return (EINVAL); nsap = (uap->nsv != NULL) ? &nsa : NULL; osap = (uap->osv != NULL) ? &osa : NULL; if (nsap) { error = copyin(uap->nsv, &vec, sizeof(vec)); if (error) return (error); nsap->sa_handler = vec.sv_handler; OSIG2SIG(vec.sv_mask, nsap->sa_mask); nsap->sa_flags = vec.sv_flags; nsap->sa_flags ^= SA_RESTART; /* opposite of SV_INTERRUPT */ #ifdef COMPAT_SUNOS nsap->sa_flags |= SA_USERTRAMP; #endif } error = do_sigaction(p, uap->signum, nsap, osap, 1); if (osap && !error) { vec.sv_handler = osap->sa_handler; SIG2OSIG(osap->sa_mask, vec.sv_mask); vec.sv_flags = osap->sa_flags; vec.sv_flags &= ~SA_NOCLDWAIT; vec.sv_flags ^= SA_RESTART; #ifdef COMPAT_SUNOS vec.sv_flags &= ~SA_NOCLDSTOP; #endif error = copyout(&vec, uap->osv, sizeof(vec)); } return (error); } #ifndef _SYS_SYSPROTO_H_ struct osigblock_args { int mask; }; #endif int osigblock(p, uap) register struct proc *p; struct osigblock_args *uap; { sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); (void) splhigh(); SIG2OSIG(p->p_sigmask, p->p_retval[0]); SIGSETOR(p->p_sigmask, set); (void) spl0(); return (0); } #ifndef _SYS_SYSPROTO_H_ struct osigsetmask_args { int mask; }; #endif int osigsetmask(p, uap) struct proc *p; struct osigsetmask_args *uap; { sigset_t set; OSIG2SIG(uap->mask, set); SIG_CANTMASK(set); (void) splhigh(); SIG2OSIG(p->p_sigmask, p->p_retval[0]); SIGSETLO(p->p_sigmask, set); (void) spl0(); return (0); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Suspend process until signal, providing mask to be set * in the meantime. Note nonstandard calling convention: * libc stub passes mask, not pointer, to save a copyin. */ #ifndef _SYS_SYSPROTO_H_ struct sigsuspend_args { const sigset_t *sigmask; }; #endif /* ARGSUSED */ int sigsuspend(p, uap) register struct proc *p; struct sigsuspend_args *uap; { sigset_t mask; register struct sigacts *ps = p->p_sigacts; int error; error = copyin(uap->sigmask, &mask, sizeof(mask)); if (error) return (error); /* * When returning from sigsuspend, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the sigacts structure * to indicate this. */ p->p_oldsigmask = p->p_sigmask; p->p_flag |= P_OLDMASK; SIG_CANTMASK(mask); p->p_sigmask = mask; while (tsleep((caddr_t) ps, PPAUSE|PCATCH, "pause", 0) == 0) /* void */; /* always return EINTR rather than ERESTART... */ return (EINTR); } #ifndef _SYS_SYSPROTO_H_ struct osigsuspend_args { osigset_t mask; }; #endif /* ARGSUSED */ int osigsuspend(p, uap) register struct proc *p; struct osigsuspend_args *uap; { sigset_t mask; register struct sigacts *ps = p->p_sigacts; p->p_oldsigmask = p->p_sigmask; p->p_flag |= P_OLDMASK; OSIG2SIG(uap->mask, mask); SIG_CANTMASK(mask); SIGSETLO(p->p_sigmask, mask); while (tsleep((caddr_t) ps, PPAUSE|PCATCH, "opause", 0) == 0) /* void */; /* always return EINTR rather than ERESTART... */ return (EINTR); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #ifndef _SYS_SYSPROTO_H_ struct osigstack_args { struct sigstack *nss; struct sigstack *oss; }; #endif /* ARGSUSED */ int osigstack(p, uap) struct proc *p; register struct osigstack_args *uap; { struct sigstack ss; int error = 0; ss.ss_sp = p->p_sigstk.ss_sp; ss.ss_onstack = p->p_sigstk.ss_flags & SS_ONSTACK; if (uap->oss && (error = copyout(&ss, uap->oss, sizeof(struct sigstack)))) return (error); if (uap->nss && (error = copyin(uap->nss, &ss, sizeof(ss))) == 0) { p->p_sigstk.ss_sp = ss.ss_sp; p->p_sigstk.ss_size = 0; p->p_sigstk.ss_flags |= ss.ss_onstack & SS_ONSTACK; p->p_flag |= P_ALTSTACK; } return (error); } #endif /* COMPAT_43 || COMPAT_SUNOS */ #ifndef _SYS_SYSPROTO_H_ struct sigaltstack_args { stack_t *ss; stack_t *oss; }; #endif /* ARGSUSED */ int sigaltstack(p, uap) struct proc *p; register struct sigaltstack_args *uap; { stack_t ss; int error; if ((p->p_flag & P_ALTSTACK) == 0) p->p_sigstk.ss_flags |= SS_DISABLE; if (uap->oss && (error = copyout(&p->p_sigstk, uap->oss, sizeof(stack_t)))) return (error); if (uap->ss == 0) return (0); if ((error = copyin(uap->ss, &ss, sizeof(ss)))) return (error); if (ss.ss_flags & SS_DISABLE) { if (p->p_sigstk.ss_flags & SS_ONSTACK) return (EINVAL); p->p_flag &= ~P_ALTSTACK; p->p_sigstk.ss_flags = ss.ss_flags; return (0); } if (ss.ss_size < MINSIGSTKSZ) return (ENOMEM); p->p_flag |= P_ALTSTACK; p->p_sigstk = ss; return (0); } /* * Common code for kill process group/broadcast kill. * cp is calling process. */ int killpg1(cp, sig, pgid, all) register struct proc *cp; int sig, pgid, all; { register struct proc *p; - register struct pcred *pc = cp->p_cred; struct pgrp *pgrp; int nfound = 0; if (all) /* * broadcast */ LIST_FOREACH(p, &allproc, p_list) { if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || - p == cp || !CANSIGNAL(cp, pc, p, sig)) + p == cp || !CANSIGNAL(cp, p, sig)) continue; nfound++; if (sig) psignal(p, sig); } else { if (pgid == 0) /* * zero pgid means send to my process group. */ pgrp = cp->p_pgrp; else { pgrp = pgfind(pgid); if (pgrp == NULL) return (ESRCH); } LIST_FOREACH(p, &pgrp->pg_members, p_pglist) { if (p->p_pid <= 1 || p->p_flag & P_SYSTEM || p->p_stat == SZOMB || - !CANSIGNAL(cp, pc, p, sig)) + !CANSIGNAL(cp, p, sig)) continue; nfound++; if (sig) psignal(p, sig); } } return (nfound ? 0 : ESRCH); } #ifndef _SYS_SYSPROTO_H_ struct kill_args { int pid; int signum; }; #endif /* ARGSUSED */ int kill(cp, uap) register struct proc *cp; register struct kill_args *uap; { register struct proc *p; - register struct pcred *pc = cp->p_cred; if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); if (uap->pid > 0) { /* kill single process */ if ((p = pfind(uap->pid)) == NULL) return (ESRCH); - if (!CANSIGNAL(cp, pc, p, uap->signum)) + if (!CANSIGNAL(cp, p, uap->signum)) return (EPERM); if (uap->signum) psignal(p, uap->signum); return (0); } switch (uap->pid) { case -1: /* broadcast signal */ return (killpg1(cp, uap->signum, 0, 1)); case 0: /* signal own process group */ return (killpg1(cp, uap->signum, 0, 0)); default: /* negative explicit process group */ return (killpg1(cp, uap->signum, -uap->pid, 0)); } /* NOTREACHED */ } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #ifndef _SYS_SYSPROTO_H_ struct okillpg_args { int pgid; int signum; }; #endif /* ARGSUSED */ int okillpg(p, uap) struct proc *p; register struct okillpg_args *uap; { if ((u_int)uap->signum > _SIG_MAXSIG) return (EINVAL); return (killpg1(p, uap->signum, uap->pgid, 0)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Send a signal to a process group. */ void gsignal(pgid, sig) int pgid, sig; { struct pgrp *pgrp; if (pgid && (pgrp = pgfind(pgid))) pgsignal(pgrp, sig, 0); } /* * Send a signal to a process group. If checktty is 1, * limit to members which have a controlling terminal. */ void pgsignal(pgrp, sig, checkctty) struct pgrp *pgrp; int sig, checkctty; { register struct proc *p; if (pgrp) LIST_FOREACH(p, &pgrp->pg_members, p_pglist) if (checkctty == 0 || p->p_flag & P_CONTROLT) psignal(p, sig); } /* * Send a signal caused by a trap to the current process. * If it will be caught immediately, deliver it with correct code. * Otherwise, post it normally. */ void trapsignal(p, sig, code) struct proc *p; register int sig; u_long code; { register struct sigacts *ps = p->p_sigacts; if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(p->p_sigcatch, sig) && SIGISMEMBER(p->p_sigmask, sig)) { p->p_stats->p_ru.ru_nsignals++; #ifdef KTRACE if (KTRPOINT(p, KTR_PSIG)) ktrpsig(p->p_tracep, sig, ps->ps_sigact[_SIG_IDX(sig)], &p->p_sigmask, code); #endif (*p->p_sysent->sv_sendsig)(ps->ps_sigact[_SIG_IDX(sig)], sig, &p->p_sigmask, code); SIGSETOR(p->p_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(p->p_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See do_sigaction() for origin of this code. */ SIGDELSET(p->p_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(p->p_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } } else { p->p_code = code; /* XXX for core dump/debugger */ p->p_sig = sig; /* XXX to verify code */ psignal(p, sig); } } /* * Send the signal to the process. If the signal has an action, the action * is usually performed by the target process rather than the caller; we add * the signal to the set of pending signals for the process. * * Exceptions: * o When a stop signal is sent to a sleeping process that takes the * default action, the process is stopped without awakening it. * o SIGCONT restarts stopped processes (or puts them back to sleep) * regardless of the signal action (eg, blocked or ignored). * * Other ignored signals are discarded immediately. */ void psignal(p, sig) register struct proc *p; register int sig; { register int s, prop; register sig_t action; if (sig > _SIG_MAXSIG || sig <= 0) { printf("psignal: signal %d\n", sig); panic("psignal signal number"); } prop = sigprop(sig); /* * If proc is traced, always give parent a chance; * if signal event is tracked by procfs, give *that* * a chance, as well. */ if ((p->p_flag & P_TRACED) || (p->p_stops & S_SIG)) action = SIG_DFL; else { /* * If the signal is being ignored, * then we forget about it immediately. * (Note: we don't set SIGCONT in p_sigignore, * and if it is set to SIG_IGN, * action will be SIG_DFL here.) */ if (SIGISMEMBER(p->p_sigignore, sig) || (p->p_flag & P_WEXIT)) return; if (SIGISMEMBER(p->p_sigmask, sig)) action = SIG_HOLD; else if (SIGISMEMBER(p->p_sigcatch, sig)) 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) SIG_STOPSIGMASK(p->p_siglist); if (prop & SA_STOP) { /* * If sending a tty stop signal to a member of an orphaned * process group, 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_pgrp->pg_jobc == 0 && action == SIG_DFL) return; SIG_CONTSIGMASK(p->p_siglist); } SIGADDSET(p->p_siglist, sig); /* * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ if (action == SIG_HOLD && (!(prop & SA_CONT) || p->p_stat != SSTOP)) return; s = splhigh(); switch (p->p_stat) { case SSLEEP: /* * If process is sleeping uninterruptibly * we can't 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) { SIGDELSET(p->p_siglist, sig); 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 runfast; /* * If a child holding parent blocked, * stopping could cause deadlock. */ if (p->p_flag & P_PPWAIT) goto out; SIGDELSET(p->p_siglist, sig); p->p_xstat = sig; if ((p->p_pptr->p_procsig->ps_flag & PS_NOCLDSTOP) == 0) psignal(p->p_pptr, SIGCHLD); stop(p); goto out; } else goto runfast; /*NOTREACHED*/ case SSTOP: /* * If traced process is already stopped, * then no further action is necessary. */ if (p->p_flag & P_TRACED) goto out; /* * Kill signal always sets processes running. */ if (sig == SIGKILL) goto runfast; if (prop & SA_CONT) { /* * If SIGCONT is default (or ignored), we continue the * process but don't leave the signal in p_siglist, as * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * p_siglist. 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) SIGDELSET(p->p_siglist, sig); if (action == SIG_CATCH) goto runfast; if (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.) */ SIGDELSET(p->p_siglist, sig); 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; 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 == curproc) signotify(p); #ifdef SMP else if (p->p_stat == SRUN) forward_signal(p); #endif goto out; } /*NOTREACHED*/ runfast: /* * Raise priority to at least PUSER. */ if (p->p_priority > PUSER) p->p_priority = PUSER; run: setrunnable(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 aren't returned. This is checked after each entry to the system for * a syscall or trap (though this can usually be done without calling issignal * by checking the pending signal masks in the CURSIG macro.) The normal call * sequence is * * while (sig = CURSIG(curproc)) * postsig(sig); */ int issignal(p) register struct proc *p; { sigset_t mask; register int sig, prop; for (;;) { int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); mask = p->p_siglist; SIGSETNAND(mask, p->p_sigmask); if (p->p_flag & P_PPWAIT) SIG_STOPSIGMASK(mask); if (!SIGNOTEMPTY(mask)) /* no signal to send */ return (0); sig = sig_ffs(&mask); prop = sigprop(sig); STOPEVENT(p, S_SIG, sig); /* * We should see pending but ignored signals * only if P_TRACED was on when they were posted. */ if (SIGISMEMBER(p->p_sigignore, sig) && (traced == 0)) { SIGDELSET(p->p_siglist, sig); continue; } if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop, and stay * stopped until released by the parent. */ p->p_xstat = sig; psignal(p->p_pptr, SIGCHLD); do { stop(p); mi_switch(); } while (!trace_req(p) && p->p_flag & P_TRACED); /* * If the traced bit got turned off, go back up * to the top to rescan signals. This ensures * that p_sig* and ps_sigact are consistent. */ if ((p->p_flag & P_TRACED) == 0) continue; /* * If parent wants us to take the signal, * then it will leave it in p->p_xstat; * otherwise we just look for signals again. */ SIGDELSET(p->p_siglist, sig); /* clear old signal */ sig = p->p_xstat; if (sig == 0) continue; /* * Put the new signal into p_siglist. If the * signal is being masked, look for other signals. */ SIGADDSET(p->p_siglist, sig); if (SIGISMEMBER(p->p_sigmask, sig)) continue; } /* * Decide whether the signal should be returned. * Return the signal's number, or fall through * to clear it from the pending mask. */ switch ((int)(intptr_t)p->p_sigacts->ps_sigact[_SIG_IDX(sig)]) { case (int)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 %lu) got signal %d\n", (u_long)p->p_pid, sig); #endif break; /* == ignore */ } /* * 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_pgrp->pg_jobc == 0 && prop & SA_TTYSTOP)) break; /* == ignore */ p->p_xstat = sig; stop(p); if ((p->p_pptr->p_procsig->ps_flag & PS_NOCLDSTOP) == 0) psignal(p->p_pptr, SIGCHLD); mi_switch(); 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 (int)SIG_IGN: /* * Masking above should prevent us ever trying * to take action on an ignored signal other * than SIGCONT, unless process is traced. */ if ((prop & SA_CONT) == 0 && (p->p_flag & P_TRACED) == 0) printf("issignal\n"); break; /* == ignore */ default: /* * This signal has an action, let * postsig() process it. */ return (sig); } SIGDELSET(p->p_siglist, sig); /* take the signal! */ } /* NOTREACHED */ } /* * Put the argument process into the stopped state and notify the parent * via wakeup. Signals are handled elsewhere. The process must not be * on the run queue. */ void 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. */ void postsig(sig) register int sig; { register struct proc *p = curproc; struct sigacts *ps = p->p_sigacts; sig_t action; sigset_t returnmask; int code; KASSERT(sig != 0, ("postsig")); SIGDELSET(p->p_siglist, sig); action = ps->ps_sigact[_SIG_IDX(sig)]; #ifdef KTRACE if (KTRPOINT(p, KTR_PSIG)) ktrpsig(p->p_tracep, sig, action, p->p_flag & P_OLDMASK ? &p->p_oldsigmask : &p->p_sigmask, 0); #endif STOPEVENT(p, S_SIG, sig); if (action == SIG_DFL) { /* * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ sigexit(p, sig); /* NOTREACHED */ } else { /* * If we get here, the signal must be caught. */ KASSERT(action != SIG_IGN && !SIGISMEMBER(p->p_sigmask, sig), ("postsig action")); /* * Set the new mask value and also defer further * occurrences 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 (p->p_flag & P_OLDMASK) { returnmask = p->p_oldsigmask; p->p_flag &= ~P_OLDMASK; } else returnmask = p->p_sigmask; SIGSETOR(p->p_sigmask, ps->ps_catchmask[_SIG_IDX(sig)]); if (!SIGISMEMBER(ps->ps_signodefer, sig)) SIGADDSET(p->p_sigmask, sig); if (SIGISMEMBER(ps->ps_sigreset, sig)) { /* * See do_sigaction() for origin of this code. */ SIGDELSET(p->p_sigcatch, sig); if (sig != SIGCONT && sigprop(sig) & SA_IGNORE) SIGADDSET(p->p_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; } (void) spl0(); p->p_stats->p_ru.ru_nsignals++; if (p->p_sig != sig) { code = 0; } else { code = p->p_code; p->p_code = 0; p->p_sig = 0; } (*p->p_sysent->sv_sendsig)(action, sig, &returnmask, code); } } /* * Kill the current process for stated reason. */ void killproc(p, why) struct proc *p; char *why; { log(LOG_ERR, "pid %d (%s), uid %d, was killed: %s\n", p->p_pid, p->p_comm, p->p_cred && p->p_ucred ? p->p_ucred->cr_uid : -1, why); psignal(p, SIGKILL); } /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, * allowing unrecoverable failures to terminate the process without changing * signal state. Mark the accounting record with the signal termination. * If dumping core, save the signal number for the debugger. Calls exit and * does not return. */ void sigexit(p, sig) register struct proc *p; int sig; { p->p_acflag |= AXSIG; if (sigprop(sig) & SA_CORE) { p->p_sig = sig; /* * Log signals which would cause core dumps * (Log as LOG_INFO to appease those who don't want * these messages.) * XXX : Todo, as well as euid, write out ruid too */ if (coredump(p) == 0) sig |= WCOREFLAG; if (kern_logsigexit) log(LOG_INFO, "pid %d (%s), uid %d: exited on signal %d%s\n", p->p_pid, p->p_comm, p->p_cred && p->p_ucred ? p->p_ucred->cr_uid : -1, sig &~ WCOREFLAG, sig & WCOREFLAG ? " (core dumped)" : ""); } exit1(p, W_EXITCODE(0, sig)); /* NOTREACHED */ } static char corefilename[MAXPATHLEN+1] = {"%N.core"}; SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RW, corefilename, sizeof(corefilename), "process corefile name format string"); /* * expand_name(name, uid, pid) * Expand the name described in corefilename, using name, uid, and pid. * corefilename is a printf-like string, with three format specifiers: * %N name of process ("name") * %P process id (pid) * %U user id (uid) * For example, "%N.core" is the default; they can be disabled completely * by using "/dev/null", or all core files can be stored in "/cores/%U/%N-%P". * This is controlled by the sysctl variable kern.corefile (see above). */ static char * expand_name(name, uid, pid) const char *name; uid_t uid; pid_t pid; { char *temp; char buf[11]; /* Buffer for pid/uid -- max 4B */ int i, n; char *format = corefilename; size_t namelen; temp = malloc(MAXPATHLEN + 1, M_TEMP, M_NOWAIT); if (temp == NULL) return NULL; namelen = strlen(name); for (i = 0, n = 0; n < MAXPATHLEN && format[i]; i++) { int l; switch (format[i]) { case '%': /* Format character */ i++; switch (format[i]) { case '%': temp[n++] = '%'; break; case 'N': /* process name */ if ((n + namelen) > MAXPATHLEN) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s%s' is too long\n", pid, name, uid, temp, name); free(temp, M_TEMP); return NULL; } memcpy(temp+n, name, namelen); n += namelen; break; case 'P': /* process id */ l = sprintf(buf, "%u", pid); if ((n + l) > MAXPATHLEN) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s%s' is too long\n", pid, name, uid, temp, name); free(temp, M_TEMP); return NULL; } memcpy(temp+n, buf, l); n += l; break; case 'U': /* user id */ l = sprintf(buf, "%u", uid); if ((n + l) > MAXPATHLEN) { log(LOG_ERR, "pid %d (%s), uid (%u): Path `%s%s' is too long\n", pid, name, uid, temp, name); free(temp, M_TEMP); return NULL; } memcpy(temp+n, buf, l); n += l; break; default: log(LOG_ERR, "Unknown format character %c in `%s'\n", format[i], format); } break; default: temp[n++] = format[i]; } } temp[n] = '\0'; return temp; } /* * Dump a process' core. The main routine does some * policy checking, and creates the name of the coredump; * then it passes on a vnode and a size limit to the process-specific * coredump routine if there is one; if there _is not_ one, it returns * ENOSYS; otherwise it returns the error from the process-specific routine. */ static int coredump(p) register struct proc *p; { register struct vnode *vp; register struct ucred *cred = p->p_ucred; struct nameidata nd; struct vattr vattr; int error, error1; char *name; /* name of corefile */ off_t limit; STOPEVENT(p, S_CORE, 0); if ((sugid_coredump == 0) && p->p_flag & P_SUGID) return (EFAULT); /* * Note that the bulk of limit checking is done after * the corefile is created. The exception is if the limit * for corefiles is 0, in which case we don't bother * creating the corefile at all. This layout means that * a corefile is truncated instead of not being created, * if it is larger than the limit. */ limit = p->p_rlimit[RLIMIT_CORE].rlim_cur; if (limit == 0) return 0; name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p); error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR); free(name, M_TEMP); if (error) return (error); vp = nd.ni_vp; /* Don't dump to non-regular files or files with links. */ if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred, p) || vattr.va_nlink != 1) { error = EFAULT; goto out; } VATTR_NULL(&vattr); vattr.va_size = 0; VOP_LEASE(vp, p, cred, LEASE_WRITE); VOP_SETATTR(vp, &vattr, cred, p); p->p_acflag |= ACORE; error = p->p_sysent->sv_coredump ? p->p_sysent->sv_coredump(p, vp, limit) : ENOSYS; out: VOP_UNLOCK(vp, 0, p); error1 = vn_close(vp, FWRITE, cred, p); if (error == 0) error = error1; return (error); } /* * Nonexistent system call-- signal process (may want to handle it). * Flag error in case process won't see signal immediately (blocked or ignored). */ #ifndef _SYS_SYSPROTO_H_ struct nosys_args { int dummy; }; #endif /* ARGSUSED */ int nosys(p, args) struct proc *p; struct nosys_args *args; { psignal(p, SIGSYS); return (EINVAL); } /* * Send a signal to a SIGIO or SIGURG to a process or process group using * stored credentials rather than those of the current process. */ void pgsigio(sigio, sig, checkctty) struct sigio *sigio; int sig, checkctty; { if (sigio == NULL) return; if (sigio->sio_pgid > 0) { if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, sigio->sio_proc)) psignal(sigio->sio_proc, sig); } else if (sigio->sio_pgid < 0) { struct proc *p; LIST_FOREACH(p, &sigio->sio_pgrp->pg_members, p_pglist) if (CANSIGIO(sigio->sio_ruid, sigio->sio_ucred, p) && (checkctty == 0 || (p->p_flag & P_CONTROLT))) psignal(p, sig); } } diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 75b72fb1d4e0..4740476bdf94 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -1,566 +1,565 @@ /* * Copyright (c) 1994, Sean Eric Fagan * All rights reserved. * * 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 Sean Eric Fagan. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* use the equivalent procfs code */ #if 0 static int pread (struct proc *procp, unsigned int addr, unsigned int *retval) { int rv; vm_map_t map, tmap; vm_object_t object; vm_offset_t kva = 0; int page_offset; /* offset into page */ vm_offset_t pageno; /* page number */ vm_map_entry_t out_entry; vm_prot_t out_prot; boolean_t wired; vm_pindex_t pindex; /* Map page into kernel space */ map = &procp->p_vmspace->vm_map; page_offset = addr - trunc_page(addr); pageno = trunc_page(addr); tmap = map; rv = vm_map_lookup (&tmap, pageno, VM_PROT_READ, &out_entry, &object, &pindex, &out_prot, &wired); if (rv != KERN_SUCCESS) return EINVAL; vm_map_lookup_done (tmap, out_entry); /* Find space in kernel_map for the page we're interested in */ rv = vm_map_find (kernel_map, object, IDX_TO_OFF(pindex), &kva, PAGE_SIZE, 0, VM_PROT_ALL, VM_PROT_ALL, 0); if (!rv) { vm_object_reference (object); rv = vm_map_pageable (kernel_map, kva, kva + PAGE_SIZE, 0); if (!rv) { *retval = 0; bcopy ((caddr_t)kva + page_offset, retval, sizeof *retval); } vm_map_remove (kernel_map, kva, kva + PAGE_SIZE); } return rv; } static int pwrite (struct proc *procp, unsigned int addr, unsigned int datum) { int rv; vm_map_t map, tmap; vm_object_t object; vm_offset_t kva = 0; int page_offset; /* offset into page */ vm_offset_t pageno; /* page number */ vm_map_entry_t out_entry; vm_prot_t out_prot; boolean_t wired; vm_pindex_t pindex; boolean_t fix_prot = 0; /* Map page into kernel space */ map = &procp->p_vmspace->vm_map; page_offset = addr - trunc_page(addr); pageno = trunc_page(addr); /* * Check the permissions for the area we're interested in. */ if (vm_map_check_protection (map, pageno, pageno + PAGE_SIZE, VM_PROT_WRITE) == FALSE) { /* * If the page was not writable, we make it so. * XXX It is possible a page may *not* be read/executable, * if a process changes that! */ fix_prot = 1; /* The page isn't writable, so let's try making it so... */ if ((rv = vm_map_protect (map, pageno, pageno + PAGE_SIZE, VM_PROT_ALL, 0)) != KERN_SUCCESS) return EFAULT; /* I guess... */ } /* * Now we need to get the page. out_entry, out_prot, wired, and * single_use aren't used. One would think the vm code would be * a *bit* nicer... We use tmap because vm_map_lookup() can * change the map argument. */ tmap = map; rv = vm_map_lookup (&tmap, pageno, VM_PROT_WRITE, &out_entry, &object, &pindex, &out_prot, &wired); if (rv != KERN_SUCCESS) { return EINVAL; } /* * Okay, we've got the page. Let's release tmap. */ vm_map_lookup_done (tmap, out_entry); /* * Fault the page in... */ rv = vm_fault(map, pageno, VM_PROT_WRITE|VM_PROT_READ, FALSE); if (rv != KERN_SUCCESS) return EFAULT; /* Find space in kernel_map for the page we're interested in */ rv = vm_map_find (kernel_map, object, IDX_TO_OFF(pindex), &kva, PAGE_SIZE, 0, VM_PROT_ALL, VM_PROT_ALL, 0); if (!rv) { vm_object_reference (object); rv = vm_map_pageable (kernel_map, kva, kva + PAGE_SIZE, 0); if (!rv) { bcopy (&datum, (caddr_t)kva + page_offset, sizeof datum); } vm_map_remove (kernel_map, kva, kva + PAGE_SIZE); } if (fix_prot) vm_map_protect (map, pageno, pageno + PAGE_SIZE, VM_PROT_READ|VM_PROT_EXECUTE, 0); return rv; } #endif /* * Process debugging system call. */ #ifndef _SYS_SYSPROTO_H_ struct ptrace_args { int req; pid_t pid; caddr_t addr; int data; }; #endif int ptrace(curp, uap) struct proc *curp; struct ptrace_args *uap; { struct proc *p; struct iovec iov; struct uio uio; int error = 0; int write; int s; write = 0; if (uap->req == PT_TRACE_ME) p = curp; else { if ((p = pfind(uap->pid)) == NULL) return ESRCH; } if (!PRISON_CHECK(curp, p)) return (ESRCH); /* * Permissions check */ switch (uap->req) { case PT_TRACE_ME: /* Always legal. */ break; case PT_ATTACH: /* Self */ if (p->p_pid == curp->p_pid) return EINVAL; /* Already traced */ if (p->p_flag & P_TRACED) return EBUSY; /* not owned by you, has done setuid (unless you're root) */ if ((p->p_cred->p_ruid != curp->p_cred->p_ruid) || (p->p_flag & P_SUGID)) { if ((error = suser(curp)) != 0) return error; } /* can't trace init when securelevel > 0 */ if (securelevel > 0 && p->p_pid == 1) return EPERM; /* OK */ break; case PT_READ_I: case PT_READ_D: case PT_READ_U: case PT_WRITE_I: case PT_WRITE_D: case PT_WRITE_U: case PT_CONTINUE: case PT_KILL: case PT_STEP: case PT_DETACH: #ifdef PT_GETREGS case PT_GETREGS: #endif #ifdef PT_SETREGS case PT_SETREGS: #endif #ifdef PT_GETFPREGS case PT_GETFPREGS: #endif #ifdef PT_SETFPREGS case PT_SETFPREGS: #endif #ifdef PT_GETDBREGS case PT_GETDBREGS: #endif #ifdef PT_SETDBREGS case PT_SETDBREGS: #endif /* not being traced... */ if ((p->p_flag & P_TRACED) == 0) return EPERM; /* not being traced by YOU */ if (p->p_pptr != curp) return EBUSY; /* not currently stopped */ if (p->p_stat != SSTOP || (p->p_flag & P_WAITED) == 0) return EBUSY; /* OK */ break; default: return EINVAL; } #ifdef FIX_SSTEP /* * Single step fixup ala procfs */ FIX_SSTEP(p); #endif /* * Actually do the requests */ curp->p_retval[0] = 0; switch (uap->req) { case PT_TRACE_ME: /* set my trace flag and "owner" so it can read/write me */ p->p_flag |= P_TRACED; p->p_oppid = p->p_pptr->p_pid; return 0; case PT_ATTACH: /* security check done above */ p->p_flag |= P_TRACED; p->p_oppid = p->p_pptr->p_pid; if (p->p_pptr != curp) proc_reparent(p, curp); uap->data = SIGSTOP; goto sendsig; /* in PT_CONTINUE below */ case PT_STEP: case PT_CONTINUE: case PT_DETACH: if ((unsigned)uap->data >= NSIG) return EINVAL; PHOLD(p); if (uap->req == PT_STEP) { if ((error = ptrace_single_step (p))) { PRELE(p); return error; } } if (uap->addr != (caddr_t)1) { fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); if ((error = ptrace_set_pc (p, (u_long)(uintfptr_t)uap->addr))) { PRELE(p); return error; } } PRELE(p); if (uap->req == PT_DETACH) { /* reset process parent */ if (p->p_oppid != p->p_pptr->p_pid) { struct proc *pp; pp = pfind(p->p_oppid); proc_reparent(p, pp ? pp : initproc); } p->p_flag &= ~(P_TRACED | P_WAITED); p->p_oppid = 0; /* should we send SIGCHLD? */ } sendsig: /* deliver or queue signal */ s = splhigh(); if (p->p_stat == SSTOP) { p->p_xstat = uap->data; setrunnable(p); } else if (uap->data) { psignal(p, uap->data); } splx(s); return 0; case PT_WRITE_I: case PT_WRITE_D: write = 1; /* fallthrough */ case PT_READ_I: case PT_READ_D: /* write = 0 set above */ iov.iov_base = write ? (caddr_t)&uap->data : (caddr_t)curp->p_retval; iov.iov_len = sizeof(int); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)(uintptr_t)uap->addr; uio.uio_resid = sizeof(int); uio.uio_segflg = UIO_SYSSPACE; /* ie: the uap */ uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_procp = p; error = procfs_domem(curp, p, NULL, &uio); if (uio.uio_resid != 0) { /* * XXX procfs_domem() doesn't currently return ENOSPC, * so I think write() can bogusly return 0. * XXX what happens for short writes? We don't want * to write partial data. * XXX procfs_domem() returns EPERM for other invalid * addresses. Convert this to EINVAL. Does this * clobber returns of EPERM for other reasons? */ if (error == 0 || error == ENOSPC || error == EPERM) error = EINVAL; /* EOF */ } return (error); case PT_READ_U: if ((uintptr_t)uap->addr > UPAGES * PAGE_SIZE - sizeof(int)) { return EFAULT; } if ((uintptr_t)uap->addr & (sizeof(int) - 1)) { return EFAULT; } if (ptrace_read_u_check(p,(vm_offset_t) uap->addr, - sizeof(int)) && - !procfs_kmemaccess(curp)) { + sizeof(int))) { return EFAULT; } error = 0; PHOLD(p); /* user had damn well better be incore! */ if (p->p_flag & P_INMEM) { p->p_addr->u_kproc.kp_proc = *p; fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); curp->p_retval[0] = *(int *) ((uintptr_t)p->p_addr + (uintptr_t)uap->addr); } else { curp->p_retval[0] = 0; error = EFAULT; } PRELE(p); return error; case PT_WRITE_U: PHOLD(p); /* user had damn well better be incore! */ if (p->p_flag & P_INMEM) { p->p_addr->u_kproc.kp_proc = *p; fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); error = ptrace_write_u(p, (vm_offset_t)uap->addr, uap->data); } else { error = EFAULT; } PRELE(p); return error; case PT_KILL: uap->data = SIGKILL; goto sendsig; /* in PT_CONTINUE above */ #ifdef PT_SETREGS case PT_SETREGS: write = 1; /* fallthrough */ #endif /* PT_SETREGS */ #ifdef PT_GETREGS case PT_GETREGS: /* write = 0 above */ #endif /* PT_SETREGS */ #if defined(PT_SETREGS) || defined(PT_GETREGS) if (!procfs_validregs(p)) /* no P_SYSTEM procs please */ return EINVAL; else { iov.iov_base = uap->addr; iov.iov_len = sizeof(struct reg); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = sizeof(struct reg); uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_procp = curp; return (procfs_doregs(curp, p, NULL, &uio)); } #endif /* defined(PT_SETREGS) || defined(PT_GETREGS) */ #ifdef PT_SETFPREGS case PT_SETFPREGS: write = 1; /* fallthrough */ #endif /* PT_SETFPREGS */ #ifdef PT_GETFPREGS case PT_GETFPREGS: /* write = 0 above */ #endif /* PT_SETFPREGS */ #if defined(PT_SETFPREGS) || defined(PT_GETFPREGS) if (!procfs_validfpregs(p)) /* no P_SYSTEM procs please */ return EINVAL; else { iov.iov_base = uap->addr; iov.iov_len = sizeof(struct fpreg); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = sizeof(struct fpreg); uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_procp = curp; return (procfs_dofpregs(curp, p, NULL, &uio)); } #endif /* defined(PT_SETFPREGS) || defined(PT_GETFPREGS) */ #ifdef PT_SETDBREGS case PT_SETDBREGS: write = 1; /* fallthrough */ #endif /* PT_SETDBREGS */ #ifdef PT_GETDBREGS case PT_GETDBREGS: /* write = 0 above */ #endif /* PT_SETDBREGS */ #if defined(PT_SETDBREGS) || defined(PT_GETDBREGS) if (!procfs_validdbregs(p)) /* no P_SYSTEM procs please */ return EINVAL; else { iov.iov_base = uap->addr; iov.iov_len = sizeof(struct dbreg); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = sizeof(struct dbreg); uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_procp = curp; return (procfs_dodbregs(curp, p, NULL, &uio)); } #endif /* defined(PT_SETDBREGS) || defined(PT_GETDBREGS) */ default: break; } return 0; } int trace_req(p) struct proc *p; { return 1; } /* * stopevent() * Stop a process because of a procfs event; * stay stopped until p->p_step is cleared * (cleared by PIOCCONT in procfs). */ void stopevent(struct proc *p, unsigned int event, unsigned int val) { p->p_step = 1; do { p->p_xstat = val; p->p_stype = event; /* Which event caused the stop? */ wakeup(&p->p_stype); /* Wake up any PIOCWAIT'ing procs */ tsleep(&p->p_step, PWAIT, "stopevent", 0); } while (p->p_step); } diff --git a/sys/miscfs/procfs/procfs.h b/sys/miscfs/procfs/procfs.h index bd6a5540016f..1402a7c82f2c 100644 --- a/sys/miscfs/procfs/procfs.h +++ b/sys/miscfs/procfs/procfs.h @@ -1,175 +1,162 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs.h 8.9 (Berkeley) 5/14/95 * * From: * $FreeBSD$ */ /* * The different types of node in a procfs filesystem */ typedef enum { Proot, /* the filesystem root */ Pcurproc, /* symbolic link for curproc */ Pproc, /* a process-specific sub-directory */ Pfile, /* the executable file */ Pmem, /* the process's memory image */ Pregs, /* the process's register set */ Pfpregs, /* the process's FP register set */ Pdbregs, /* the process's debug register set */ Pctl, /* process control */ Pstatus, /* process status */ Pnote, /* process notifier */ Pnotepg, /* process group notifier */ Pmap, /* memory map */ Ptype, /* executable type */ Pcmdline, /* command line */ Prlimit /* resource limits */ } pfstype; /* * control data for the proc file system. */ struct pfsnode { struct pfsnode *pfs_next; /* next on list */ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */ pfstype pfs_type; /* type of procfs node */ pid_t pfs_pid; /* associated process */ u_short pfs_mode; /* mode bits for stat() */ u_long pfs_flags; /* open flags */ u_long pfs_fileno; /* unique file id */ pid_t pfs_lockowner; /* pfs lock owner */ }; #define PROCFS_NOTELEN 64 /* max length of a note (/proc/$pid/note) */ #define PROCFS_CTLLEN 8 /* max length of a ctl msg (/proc/$pid/ctl */ #define PROCFS_NAMELEN 8 /* max length of a filename component */ /* * Kernel stuff follows */ #ifdef KERNEL #define CNEQ(cnp, s, len) \ ((cnp)->cn_namelen == (len) && \ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0)) #define KMEM_GROUP 2 -/* - * Check to see whether access to target process is allowed - * Evaluates to 1 if access is allowed. - */ -#define CHECKIO(p1, p2) \ - ((p1) == (p2) || \ - (PRISON_CHECK(p1, p2) && \ - ((((p1)->p_ucred->cr_uid == (p2)->p_cred->p_ruid) && \ - ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && \ - ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && \ - ((p2)->p_flag & P_SUGID) == 0) || \ - (suser_xxx(0, (p1), PRISON_ROOT) == 0)))) - #define PROCFS_FILENO(pid, type) \ (((type) < Pproc) ? \ ((type) + 2) : \ ((((pid)+1) << 4) + ((int) (type)))) /* * Convert between pfsnode vnode */ #define VTOPFS(vp) ((struct pfsnode *)(vp)->v_data) #define PFSTOV(pfs) ((pfs)->pfs_vnode) typedef struct vfs_namemap vfs_namemap_t; struct vfs_namemap { const char *nm_name; int nm_val; }; int vfs_getuserstr __P((struct uio *, char *, int *)); vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int)); /* */ struct reg; struct fpreg; struct dbreg; #define PFIND(pid) ((pid) ? pfind(pid) : &proc0) void procfs_exit __P((struct proc *)); int procfs_freevp __P((struct vnode *)); int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype)); struct vnode *procfs_findtextvp __P((struct proc *)); int procfs_sstep __P((struct proc *)); void procfs_fix_sstep __P((struct proc *)); int procfs_read_regs __P((struct proc *, struct reg *)); int procfs_write_regs __P((struct proc *, struct reg *)); int procfs_read_fpregs __P((struct proc *, struct fpreg *)); int procfs_write_fpregs __P((struct proc *, struct fpreg *)); int procfs_read_dbregs __P((struct proc *, struct dbreg *)); int procfs_write_dbregs __P((struct proc *, struct dbreg *)); int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dodbregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_domap __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dotype __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_docmdline __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); int procfs_dorlimit __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio)); /* Return 1 if process has special kernel digging privileges */ int procfs_kmemaccess __P((struct proc *)); /* functions to check whether or not files should be displayed */ int procfs_validfile __P((struct proc *)); int procfs_validfpregs __P((struct proc *)); int procfs_validregs __P((struct proc *)); int procfs_validdbregs __P((struct proc *)); int procfs_validmap __P((struct proc *)); int procfs_validtype __P((struct proc *)); #define PROCFS_LOCKED 0x01 #define PROCFS_WANT 0x02 extern vop_t **procfs_vnodeop_p; int procfs_root __P((struct mount *, struct vnode **)); int procfs_rw __P((struct vop_read_args *)); #endif /* KERNEL */ diff --git a/sys/miscfs/procfs/procfs_dbregs.c b/sys/miscfs/procfs/procfs_dbregs.c index ce990e983745..0df1c3e529cc 100644 --- a/sys/miscfs/procfs/procfs_dbregs.c +++ b/sys/miscfs/procfs/procfs_dbregs.c @@ -1,101 +1,101 @@ /*- * Copyright (c) 1999 Brian Scott Dean, brdean@unx.sas.com. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry under the following copyrights and conditions: * * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include int procfs_dodbregs(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { int error; struct dbreg r; char *kv; int kl; - if (!CHECKIO(curp, p)) + if (p_trespass(curp, p)) return (EPERM); kl = sizeof(r); kv = (char *) &r; kv += uio->uio_offset; kl -= uio->uio_offset; if (kl > uio->uio_resid) kl = uio->uio_resid; PHOLD(p); if (kl < 0) error = EINVAL; else error = procfs_read_dbregs(p, &r); if (error == 0) error = uiomove(kv, kl, uio); if (error == 0 && uio->uio_rw == UIO_WRITE) { if (p->p_stat != SSTOP) error = EBUSY; else error = procfs_write_dbregs(p, &r); } PRELE(p); uio->uio_offset = 0; return (error); } int procfs_validdbregs(p) struct proc *p; { return ((p->p_flag & P_SYSTEM) == 0); } diff --git a/sys/miscfs/procfs/procfs_fpregs.c b/sys/miscfs/procfs/procfs_fpregs.c index b3331e5a726e..d4a4cfee5390 100644 --- a/sys/miscfs/procfs/procfs_fpregs.c +++ b/sys/miscfs/procfs/procfs_fpregs.c @@ -1,98 +1,98 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_fpregs.c 8.2 (Berkeley) 6/15/94 * * From: * $FreeBSD$ */ #include #include #include #include #include #include #include int procfs_dofpregs(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { int error; struct fpreg r; char *kv; int kl; - if (!CHECKIO(curp, p)) + if (p_trespass(curp, p)) return EPERM; kl = sizeof(r); kv = (char *) &r; kv += uio->uio_offset; kl -= uio->uio_offset; if (kl > uio->uio_resid) kl = uio->uio_resid; PHOLD(p); if (kl < 0) error = EINVAL; else error = procfs_read_fpregs(p, &r); if (error == 0) error = uiomove(kv, kl, uio); if (error == 0 && uio->uio_rw == UIO_WRITE) { if (p->p_stat != SSTOP) error = EBUSY; else error = procfs_write_fpregs(p, &r); } PRELE(p); uio->uio_offset = 0; return (error); } int procfs_validfpregs(p) struct proc *p; { return ((p->p_flag & P_SYSTEM) == 0); } diff --git a/sys/miscfs/procfs/procfs_mem.c b/sys/miscfs/procfs/procfs_mem.c index 521bd5bbc551..e075a7adbc81 100644 --- a/sys/miscfs/procfs/procfs_mem.c +++ b/sys/miscfs/procfs/procfs_mem.c @@ -1,356 +1,356 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 Sean Eric Fagan * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry and Sean Eric Fagan. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_mem.c 8.5 (Berkeley) 6/15/94 * * $FreeBSD$ */ /* * This is a lightly hacked and merged version * of sef's pread/pwrite functions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int procfs_rwmem __P((struct proc *curp, struct proc *p, struct uio *uio)); static int procfs_rwmem(curp, p, uio) struct proc *curp; struct proc *p; struct uio *uio; { int error; int writing; struct vmspace *vm; vm_map_t map; vm_object_t object = NULL; vm_offset_t pageno = 0; /* page number */ vm_prot_t reqprot; vm_offset_t kva; /* * if the vmspace is in the midst of being deallocated or the * process is exiting, don't try to grab anything. The page table * usage in that process can be messed up. */ vm = p->p_vmspace; if ((p->p_flag & P_WEXIT) || (vm->vm_refcnt < 1)) return EFAULT; ++vm->vm_refcnt; /* * The map we want... */ map = &vm->vm_map; writing = uio->uio_rw == UIO_WRITE; reqprot = writing ? (VM_PROT_WRITE | VM_PROT_OVERRIDE_WRITE) : VM_PROT_READ; kva = kmem_alloc_pageable(kernel_map, PAGE_SIZE); /* * Only map in one page at a time. We don't have to, but it * makes things easier. This way is trivial - right? */ do { vm_map_t tmap; vm_offset_t uva; int page_offset; /* offset into page */ vm_map_entry_t out_entry; vm_prot_t out_prot; boolean_t wired; vm_pindex_t pindex; u_int len; vm_page_t m; object = NULL; uva = (vm_offset_t) uio->uio_offset; /* * Get the page number of this segment. */ pageno = trunc_page(uva); page_offset = uva - pageno; /* * How many bytes to copy */ len = min(PAGE_SIZE - page_offset, uio->uio_resid); if (uva >= VM_MAXUSER_ADDRESS) { vm_offset_t tkva; if (writing || uva >= VM_MAXUSER_ADDRESS + UPAGES * PAGE_SIZE || (ptrace_read_u_check(p, uva - (vm_offset_t) VM_MAXUSER_ADDRESS, (size_t) len) && !procfs_kmemaccess(curp))) { error = 0; break; } /* we are reading the "U area", force it into core */ PHOLD(p); /* sanity check */ if (!(p->p_flag & P_INMEM)) { /* aiee! */ PRELE(p); error = EFAULT; break; } /* populate the ptrace/procfs area */ p->p_addr->u_kproc.kp_proc = *p; fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); /* locate the in-core address */ tkva = (uintptr_t)p->p_addr + uva - VM_MAXUSER_ADDRESS; /* transfer it */ error = uiomove((caddr_t)tkva, len, uio); /* let the pages go */ PRELE(p); continue; } /* * Fault the page on behalf of the process */ error = vm_fault(map, pageno, reqprot, VM_FAULT_NORMAL); if (error) { error = EFAULT; break; } /* * Now we need to get the page. out_entry, out_prot, wired, * and single_use aren't used. One would think the vm code * would be a *bit* nicer... We use tmap because * vm_map_lookup() can change the map argument. */ tmap = map; error = vm_map_lookup(&tmap, pageno, reqprot, &out_entry, &object, &pindex, &out_prot, &wired); if (error) { error = EFAULT; /* * Make sure that there is no residue in 'object' from * an error return on vm_map_lookup. */ object = NULL; break; } m = vm_page_lookup(object, pindex); /* Allow fallback to backing objects if we are reading */ while (m == NULL && !writing && object->backing_object) { pindex += OFF_TO_IDX(object->backing_object_offset); object = object->backing_object; m = vm_page_lookup(object, pindex); } if (m == NULL) { error = EFAULT; /* * Make sure that there is no residue in 'object' from * an error return on vm_map_lookup. */ object = NULL; vm_map_lookup_done(tmap, out_entry); break; } /* * Wire the page into memory */ vm_page_wire(m); /* * We're done with tmap now. * But reference the object first, so that we won't loose * it. */ vm_object_reference(object); vm_map_lookup_done(tmap, out_entry); pmap_kenter(kva, VM_PAGE_TO_PHYS(m)); /* * Now do the i/o move. */ error = uiomove((caddr_t)(kva + page_offset), len, uio); pmap_kremove(kva); /* * release the page and the object */ vm_page_unwire(m, 1); vm_object_deallocate(object); object = NULL; } while (error == 0 && uio->uio_resid > 0); if (object) vm_object_deallocate(object); kmem_free(kernel_map, kva, PAGE_SIZE); vmspace_free(vm); return (error); } /* * Copy data in and out of the target process. * We do this by mapping the process's page into * the kernel and then doing a uiomove direct * from the kernel address space. */ int procfs_domem(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { if (uio->uio_resid == 0) return (0); /* * XXX * We need to check for KMEM_GROUP because ps is sgid kmem; * not allowing it here causes ps to not work properly. Arguably, * this is a bug with what ps does. We only need to do this * for Pmem nodes, and only if it's reading. This is still not * good, as it may still be possible to grab illicit data if * a process somehow gets to be KMEM_GROUP. Note that this also * means that KMEM_GROUP can't change without editing procfs.h! * All in all, quite yucky. */ - if (!CHECKIO(curp, p) && + if (p_trespass(curp, p) && !(uio->uio_rw == UIO_READ && procfs_kmemaccess(curp))) return EPERM; return (procfs_rwmem(curp, p, uio)); } /* * Given process (p), find the vnode from which * its text segment is being executed. * * It would be nice to grab this information from * the VM system, however, there is no sure-fire * way of doing that. Instead, fork(), exec() and * wait() all maintain the p_textvp field in the * process proc structure which contains a held * reference to the exec'ed vnode. * * XXX - Currently, this is not not used, as the * /proc/pid/file object exposes an information leak * that shouldn't happen. Using a mount option would * make it configurable on a per-system (or, at least, * per-mount) basis; however, that's not really best. * The best way to do it, I think, would be as an * ioctl; this would restrict it to the uid running * program, or root, which seems a reasonable compromise. * However, the number of applications for this is * minimal, if it can't be seen in the filesytem space, * and doint it as an ioctl makes it somewhat less * useful due to the, well, inelegance. * */ struct vnode * procfs_findtextvp(p) struct proc *p; { return (p->p_textvp); } int procfs_kmemaccess(curp) struct proc *curp; { int i; struct ucred *cred; cred = curp->p_ucred; if (suser(curp)) return 1; /* XXX: Why isn't this done with file-perms ??? */ for (i = 0; i < cred->cr_ngroups; i++) if (cred->cr_groups[i] == KMEM_GROUP) return 1; return 0; } diff --git a/sys/miscfs/procfs/procfs_regs.c b/sys/miscfs/procfs/procfs_regs.c index 0edb9ad60639..88f85d3d1857 100644 --- a/sys/miscfs/procfs/procfs_regs.c +++ b/sys/miscfs/procfs/procfs_regs.c @@ -1,98 +1,98 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_regs.c 8.4 (Berkeley) 6/15/94 * * From: * $FreeBSD$ */ #include #include #include #include #include #include #include int procfs_doregs(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { int error; struct reg r; char *kv; int kl; - if (!CHECKIO(curp, p)) + if (p_trespass(curp, p)) return EPERM; kl = sizeof(r); kv = (char *) &r; kv += uio->uio_offset; kl -= uio->uio_offset; if (kl > uio->uio_resid) kl = uio->uio_resid; PHOLD(p); if (kl < 0) error = EINVAL; else error = procfs_read_regs(p, &r); if (error == 0) error = uiomove(kv, kl, uio); if (error == 0 && uio->uio_rw == UIO_WRITE) { if (p->p_stat != SSTOP) error = EBUSY; else error = procfs_write_regs(p, &r); } PRELE(p); uio->uio_offset = 0; return (error); } int procfs_validregs(p) struct proc *p; { return ((p->p_flag & P_SYSTEM) == 0); } diff --git a/sys/miscfs/procfs/procfs_status.c b/sys/miscfs/procfs/procfs_status.c index 0eb38581eae9..e63a12bcbeb5 100644 --- a/sys/miscfs/procfs/procfs_status.c +++ b/sys/miscfs/procfs/procfs_status.c @@ -1,229 +1,229 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_status.c 8.4 (Berkeley) 6/15/94 * * From: * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include int procfs_dostatus(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { struct session *sess; struct tty *tp; struct ucred *cr; char *ps; char *sep; int pid, ppid, pgid, sid; int i; int xlen; int error; char psbuf[256]; /* XXX - conservative */ if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); pid = p->p_pid; ppid = p->p_pptr ? p->p_pptr->p_pid : 0, pgid = p->p_pgrp->pg_id; sess = p->p_pgrp->pg_session; sid = sess->s_leader ? sess->s_leader->p_pid : 0; /* comm pid ppid pgid sid maj,min ctty,sldr start ut st wmsg euid ruid rgid,egid,groups[1 .. NGROUPS] */ ps = psbuf; bcopy(p->p_comm, ps, MAXCOMLEN); ps[MAXCOMLEN] = '\0'; ps += strlen(ps); ps += sprintf(ps, " %d %d %d %d ", pid, ppid, pgid, sid); if ((p->p_flag&P_CONTROLT) && (tp = sess->s_ttyp)) ps += sprintf(ps, "%d,%d ", major(tp->t_dev), minor(tp->t_dev)); else ps += sprintf(ps, "%d,%d ", -1, -1); sep = ""; if (sess->s_ttyvp) { ps += sprintf(ps, "%sctty", sep); sep = ","; } if (SESS_LEADER(p)) { ps += sprintf(ps, "%ssldr", sep); sep = ","; } if (*sep != ',') ps += sprintf(ps, "noflags"); if (p->p_flag & P_INMEM) { struct timeval ut, st; calcru(p, &ut, &st, (struct timeval *) NULL); ps += sprintf(ps, " %ld,%ld %ld,%ld %ld,%ld", p->p_stats->p_start.tv_sec, p->p_stats->p_start.tv_usec, ut.tv_sec, ut.tv_usec, st.tv_sec, st.tv_usec); } else ps += sprintf(ps, " -1,-1 -1,-1 -1,-1"); ps += sprintf(ps, " %s", (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "nochan"); cr = p->p_ucred; ps += sprintf(ps, " %lu %lu %lu", (u_long)cr->cr_uid, (u_long)p->p_cred->p_ruid, (u_long)p->p_cred->p_rgid); /* egid (p->p_cred->p_svgid) is equal to cr_ngroups[0] see also getegid(2) in /sys/kern/kern_prot.c */ for (i = 0; i < cr->cr_ngroups; i++) ps += sprintf(ps, ",%lu", (u_long)cr->cr_groups[i]); if (p->p_prison) ps += sprintf(ps, " %s", p->p_prison->pr_host); else ps += sprintf(ps, " -"); ps += sprintf(ps, "\n"); xlen = ps - psbuf; xlen -= uio->uio_offset; ps = psbuf + uio->uio_offset; xlen = imin(xlen, uio->uio_resid); if (xlen <= 0) error = 0; else error = uiomove(ps, xlen, uio); return (error); } int procfs_docmdline(curp, p, pfs, uio) struct proc *curp; struct proc *p; struct pfsnode *pfs; struct uio *uio; { char *ps; int xlen; int error; char *buf, *bp; int buflen; struct ps_strings pstr; int i; size_t bytes_left, done; if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); /* * If we are using the ps/cmdline caching, use that. Otherwise * revert back to the old way which only implements full cmdline * for the currept process and just p->p_comm for all other * processes. * Note that if the argv is no longer available, we deliberately * don't fall back on p->p_comm or return an error: the authentic * Linux behaviour is to return zero-length in this case. */ - if (p->p_args) { + if (p->p_args && !p_trespass(curp, p)) { bp = p->p_args->ar_args; buflen = p->p_args->ar_length; buf = 0; } else if (p != curp) { bp = p->p_comm; buflen = MAXCOMLEN; buf = 0; } else { buflen = 256; MALLOC(buf, char *, buflen + 1, M_TEMP, M_WAITOK); bp = buf; ps = buf; error = copyin((void*)PS_STRINGS, &pstr, sizeof(pstr)); if (error) { FREE(buf, M_TEMP); return (error); } bytes_left = buflen; for (i = 0; bytes_left && (i < pstr.ps_nargvstr); i++) { error = copyinstr(pstr.ps_argvstr[i], ps, bytes_left, &done); /* If too long or malformed, just truncate */ if (error) { error = 0; break; } ps += done; bytes_left -= done; } buflen = ps - buf; } buflen -= uio->uio_offset; ps = bp + uio->uio_offset; xlen = min(buflen, uio->uio_resid); if (xlen <= 0) error = 0; else error = uiomove(ps, xlen, uio); if (buf) FREE(buf, M_TEMP); return (error); } diff --git a/sys/miscfs/procfs/procfs_vnops.c b/sys/miscfs/procfs/procfs_vnops.c index c579d73d8374..17075db6bf82 100644 --- a/sys/miscfs/procfs/procfs_vnops.c +++ b/sys/miscfs/procfs/procfs_vnops.c @@ -1,1031 +1,1031 @@ /* * Copyright (c) 1993, 1995 Jan-Simon Pendry * Copyright (c) 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 * * $FreeBSD$ */ /* * procfs vnode interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int procfs_abortop __P((struct vop_abortop_args *)); static int procfs_access __P((struct vop_access_args *)); static int procfs_badop __P((void)); static int procfs_bmap __P((struct vop_bmap_args *)); static int procfs_close __P((struct vop_close_args *)); static int procfs_getattr __P((struct vop_getattr_args *)); static int procfs_inactive __P((struct vop_inactive_args *)); static int procfs_ioctl __P((struct vop_ioctl_args *)); static int procfs_lookup __P((struct vop_lookup_args *)); static int procfs_open __P((struct vop_open_args *)); static int procfs_print __P((struct vop_print_args *)); static int procfs_readdir __P((struct vop_readdir_args *)); static int procfs_readlink __P((struct vop_readlink_args *)); static int procfs_reclaim __P((struct vop_reclaim_args *)); static int procfs_setattr __P((struct vop_setattr_args *)); /* * This is a list of the valid names in the * process-specific sub-directories. It is * used in procfs_lookup and procfs_readdir */ static struct proc_target { u_char pt_type; u_char pt_namlen; char *pt_name; pfstype pt_pfstype; int (*pt_valid) __P((struct proc *p)); } proc_targets[] = { #define N(s) sizeof(s)-1, s /* name type validp */ { DT_DIR, N("."), Pproc, NULL }, { DT_DIR, N(".."), Proot, NULL }, { DT_REG, N("mem"), Pmem, NULL }, { DT_REG, N("regs"), Pregs, procfs_validregs }, { DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs }, { DT_REG, N("dbregs"), Pdbregs, procfs_validdbregs }, { DT_REG, N("ctl"), Pctl, NULL }, { DT_REG, N("status"), Pstatus, NULL }, { DT_REG, N("note"), Pnote, NULL }, { DT_REG, N("notepg"), Pnotepg, NULL }, { DT_REG, N("map"), Pmap, procfs_validmap }, { DT_REG, N("etype"), Ptype, procfs_validtype }, { DT_REG, N("cmdline"), Pcmdline, NULL }, { DT_REG, N("rlimit"), Prlimit, NULL }, #undef N }; static const int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]); static pid_t atopid __P((const char *, u_int)); /* * set things up for doing i/o on * the pfsnode (vp). (vp) is locked * on entry, and should be left locked * on exit. * * for procfs we don't need to do anything * in particular for i/o. all that is done * is to support exclusive open on process * memory images. */ static int procfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p1, *p2; p2 = PFIND(pfs->pfs_pid); if (p2 == NULL) return (ENOENT); if (pfs->pfs_pid && !PRISON_CHECK(ap->a_p, p2)) return (ENOENT); switch (pfs->pfs_type) { case Pmem: if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) || ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) return (EBUSY); p1 = ap->a_p; - if (!CHECKIO(p1, p2) && + if (p_trespass(p1, p2) && !procfs_kmemaccess(p1)) return (EPERM); if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); return (0); default: break; } return (0); } /* * close the pfsnode (vp) after doing i/o. * (vp) is not locked on entry or exit. * * nothing to do for procfs other than undo * any exclusive open flag (see _open above). */ static int procfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p; switch (pfs->pfs_type) { case Pmem: if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) pfs->pfs_flags &= ~(FWRITE|O_EXCL); /* * This rather complicated-looking code is trying to * determine if this was the last close on this particular * vnode. While one would expect v_usecount to be 1 at * that point, it seems that (according to John Dyson) * the VM system will bump up the usecount. So: if the * usecount is 2, and VOBJBUF is set, then this is really * the last close. Otherwise, if the usecount is < 2 * then it is definitely the last close. * If this is the last close, then it checks to see if * the target process has PF_LINGER set in p_pfsflags, * if this is *not* the case, then the process' stop flags * are cleared, and the process is woken up. This is * to help prevent the case where a process has been * told to stop on an event, but then the requesting process * has gone away or forgotten about it. */ if ((ap->a_vp->v_usecount < 2) && (p = pfind(pfs->pfs_pid)) && !(p->p_pfsflags & PF_LINGER)) { p->p_stops = 0; p->p_step = 0; wakeup(&p->p_step); } break; default: break; } return (0); } /* * do an ioctl operation on a pfsnode (vp). * (vp) is not locked on entry or exit. */ static int procfs_ioctl(ap) struct vop_ioctl_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *procp, *p; int error; int signo; struct procfs_status *psp; unsigned char flags; p = ap->a_p; procp = pfind(pfs->pfs_pid); if (procp == NULL) { return ENOTTY; } - if (!CHECKIO(p, procp)) + if (p_trespass(p, procp)) return EPERM; switch (ap->a_command) { case PIOCBIS: procp->p_stops |= *(unsigned int*)ap->a_data; break; case PIOCBIC: procp->p_stops &= ~*(unsigned int*)ap->a_data; break; case PIOCSFL: /* * NFLAGS is "non-suser_xxx flags" -- currently, only * PFS_ISUGID ("ignore set u/g id"); */ #define NFLAGS (PF_ISUGID) flags = (unsigned char)*(unsigned int*)ap->a_data; if (flags & NFLAGS && (error = suser(p))) return error; procp->p_pfsflags = flags; break; case PIOCGFL: *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags; case PIOCSTATUS: psp = (struct procfs_status *)ap->a_data; psp->state = (procp->p_step == 0); psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; if (procp->p_step) { psp->why = procp->p_stype; psp->val = procp->p_xstat; } else { psp->why = psp->val = 0; /* Not defined values */ } break; case PIOCWAIT: psp = (struct procfs_status *)ap->a_data; if (procp->p_step == 0) { error = tsleep(&procp->p_stype, PWAIT | PCATCH, "piocwait", 0); if (error) return error; } psp->state = 1; /* It stopped */ psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; psp->why = procp->p_stype; /* why it stopped */ psp->val = procp->p_xstat; /* any extra info */ break; case PIOCCONT: /* Restart a proc */ if (procp->p_step == 0) return EINVAL; /* Can only start a stopped process */ if ((signo = *(int*)ap->a_data) != 0) { if (signo >= NSIG || signo <= 0) return EINVAL; psignal(procp, signo); } procp->p_step = 0; wakeup(&procp->p_step); break; default: return (ENOTTY); } return 0; } /* * do block mapping for pfsnode (vp). * since we don't use the buffer cache * for procfs this function should never * be called. in any case, it's not clear * what part of the kernel ever makes use * of this function. for sanity, this is the * usual no-op bmap, although returning * (EIO) would be a reasonable alternative. */ static int procfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; return (0); } /* * procfs_inactive is called when the pfsnode * is vrele'd and the reference count goes * to zero. (vp) will be on the vnode free * list, so to get it back vget() must be * used. * * (vp) is locked on entry, but must be unlocked on exit. */ static int procfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; VOP_UNLOCK(vp, 0, ap->a_p); return (0); } /* * _reclaim is called when getnewvnode() * wants to make use of an entry on the vnode * free list. at this time the filesystem needs * to free any private data and remove the node * from any private lists. */ static int procfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { return (procfs_freevp(ap->a_vp)); } /* * _print is used for debugging. * just print a readable description * of (vp). */ static int procfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); printf("tag VT_PROCFS, type %d, pid %ld, mode %x, flags %lx\n", pfs->pfs_type, (long)pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags); return (0); } /* * _abortop is called when operations such as * rename and create fail. this entry is responsible * for undoing any side-effects caused by the lookup. * this will always include freeing the pathname buffer. */ static int procfs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) zfree(namei_zone, ap->a_cnp->cn_pnbuf); return (0); } /* * generic entry point for unsupported operations */ static int procfs_badop() { return (EIO); } /* * Invent attributes for pfsnode (vp) and store * them in (vap). * Directories lengths are returned as zero since * any real length would require the genuine size * to be computed, and nothing cares anyway. * * this is relatively minimal for procfs. */ static int procfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct vattr *vap = ap->a_vap; struct proc *procp; int error; /* * First make sure that the process and its credentials * still exist. */ switch (pfs->pfs_type) { case Proot: case Pcurproc: procp = 0; break; default: procp = PFIND(pfs->pfs_pid); if (procp == 0 || procp->p_cred == NULL || procp->p_ucred == NULL) return (ENOENT); } error = 0; /* start by zeroing out the attributes */ VATTR_NULL(vap); /* next do all the common fields */ vap->va_type = ap->a_vp->v_type; vap->va_mode = pfs->pfs_mode; vap->va_fileid = pfs->pfs_fileno; vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; /* * Make all times be current TOD. * It would be possible to get the process start * time from the p_stat structure, but there's * no "file creation" time stamp anyway, and the * p_stat structure is not addressible if u. gets * swapped out for that process. */ nanotime(&vap->va_ctime); vap->va_atime = vap->va_mtime = vap->va_ctime; /* * If the process has exercised some setuid or setgid * privilege, then rip away read/write permission so * that only root can gain access. */ switch (pfs->pfs_type) { case Pctl: case Pregs: case Pfpregs: case Pdbregs: if (procp->p_flag & P_SUGID) vap->va_mode &= ~((VREAD|VWRITE)| ((VREAD|VWRITE)>>3)| ((VREAD|VWRITE)>>6)); break; case Pmem: /* Retain group kmem readablity. */ if (procp->p_flag & P_SUGID) vap->va_mode &= ~(VREAD|VWRITE); break; default: break; } /* * now do the object specific fields * * The size could be set from struct reg, but it's hardly * worth the trouble, and it puts some (potentially) machine * dependent data into this machine-independent code. If it * becomes important then this function should break out into * a per-file stat function in the corresponding .c file. */ vap->va_nlink = 1; if (procp) { vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; } switch (pfs->pfs_type) { case Proot: /* * Set nlink to 1 to tell fts(3) we don't actually know. */ vap->va_nlink = 1; vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pcurproc: { char buf[16]; /* should be enough */ vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = snprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid); break; } case Pproc: vap->va_nlink = nproc_targets; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pfile: error = EOPNOTSUPP; break; case Pmem: /* * If we denied owner access earlier, then we have to * change the owner to root - otherwise 'ps' and friends * will break even though they are setgid kmem. *SIGH* */ if (procp->p_flag & P_SUGID) vap->va_uid = 0; else vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = KMEM_GROUP; break; case Pregs: vap->va_bytes = vap->va_size = sizeof(struct reg); break; case Pfpregs: vap->va_bytes = vap->va_size = sizeof(struct fpreg); break; case Pdbregs: vap->va_bytes = vap->va_size = sizeof(struct dbreg); break; case Ptype: case Pmap: case Pctl: case Pstatus: case Pnote: case Pnotepg: case Pcmdline: case Prlimit: break; default: panic("procfs_getattr"); } return (error); } static int procfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { if (ap->a_vap->va_flags != VNOVAL) return (EOPNOTSUPP); /* * just fake out attribute setting * it's not good to generate an error * return, otherwise things like creat() * will fail when they try to set the * file length to 0. worse, this means * that echo $note > /proc/$pid/note will fail. */ return (0); } /* * implement access checking. * * something very similar to this code is duplicated * throughout the 4bsd kernel and should be moved * into kern/vfs_subr.c sometime. * * actually, the check for super-user is slightly * broken since it will allow read access to write-only * objects. this doesn't cause any particular trouble * but does mean that the i/o entry points need to check * that the operation really does make sense. */ static int procfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vattr *vap; struct vattr vattr; int error; /* * If you're the super-user, * you always get access. */ if (ap->a_cred->cr_uid == 0) return (0); vap = &vattr; error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p); if (error) return (error); /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (ap->a_cred->cr_uid != vap->va_uid) { gid_t *gp; int i; ap->a_mode >>= 3; gp = ap->a_cred->cr_groups; for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; ap->a_mode >>= 3; found: ; } if ((vap->va_mode & ap->a_mode) == ap->a_mode) return (0); return (EACCES); } /* * lookup. this is incredibly complicated in the * general case, however for most pseudo-filesystems * very little needs to be done. * * unless you want to get a migraine, just make sure your * filesystem doesn't do any locking of its own. otherwise * read and inwardly digest ufs_lookup(). */ static int procfs_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct proc *curp = cnp->cn_proc; struct proc_target *pt; struct vnode *fvp; pid_t pid; struct pfsnode *pfs; struct proc *p; int i; *vpp = NULL; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /* vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, curp); */ return (0); } pfs = VTOPFS(dvp); switch (pfs->pfs_type) { case Proot: if (cnp->cn_flags & ISDOTDOT) return (EIO); if (CNEQ(cnp, "curproc", 7)) return (procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc)); pid = atopid(pname, cnp->cn_namelen); if (pid == NO_PID) break; p = PFIND(pid); if (p == 0) break; return (procfs_allocvp(dvp->v_mount, vpp, pid, Pproc)); case Pproc: if (cnp->cn_flags & ISDOTDOT) return (procfs_root(dvp->v_mount, vpp)); p = PFIND(pfs->pfs_pid); if (p == 0) break; for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) { if (cnp->cn_namelen == pt->pt_namlen && bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && (pt->pt_valid == NULL || (*pt->pt_valid)(p))) goto found; } break; found: if (pt->pt_pfstype == Pfile) { fvp = procfs_findtextvp(p); /* We already checked that it exists. */ VREF(fvp); vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, curp); *vpp = fvp; return (0); } return (procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, pt->pt_pfstype)); default: return (ENOTDIR); } return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS); } /* * Does this process have a text file? */ int procfs_validfile(p) struct proc *p; { return (procfs_findtextvp(p) != NULLVP); } /* * readdir() returns directory entries from pfsnode (vp). * * We generate just one directory entry at a time, as it would probably * not pay off to buffer several entries locally to save uiomove calls. */ static int procfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long **a_cookies; } */ *ap; { struct uio *uio = ap->a_uio; struct dirent d; struct dirent *dp = &d; struct pfsnode *pfs; int count, error, i, off; static u_int delen; if (!delen) { d.d_namlen = PROCFS_NAMELEN; delen = GENERIC_DIRSIZ(&d); } pfs = VTOPFS(ap->a_vp); off = (int)uio->uio_offset; if (off != uio->uio_offset || off < 0 || off % delen != 0 || uio->uio_resid < delen) return (EINVAL); error = 0; count = 0; i = off / delen; switch (pfs->pfs_type) { /* * this is for the process-specific sub-directories. * all that is needed to is copy out all the entries * from the procent[] table (top of this file). */ case Pproc: { struct proc *p; struct proc_target *pt; p = PFIND(pfs->pfs_pid); if (p == NULL) break; if (!PRISON_CHECK(curproc, p)) break; for (pt = &proc_targets[i]; uio->uio_resid >= delen && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(p) == 0) continue; dp->d_reclen = delen; dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype); dp->d_namlen = pt->pt_namlen; bcopy(pt->pt_name, dp->d_name, pt->pt_namlen + 1); dp->d_type = pt->pt_type; if ((error = uiomove((caddr_t)dp, delen, uio)) != 0) break; } break; } /* * this is for the root of the procfs filesystem * what is needed is a special entry for "curproc" * followed by an entry for each process on allproc #ifdef PROCFS_ZOMBIE * and zombproc. #endif */ case Proot: { #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif int pcnt = 0; volatile struct proc *p = allproc.lh_first; for (; p && uio->uio_resid >= delen; i++, pcnt++) { bzero((char *) dp, delen); dp->d_reclen = delen; switch (i) { case 0: /* `.' */ case 1: /* `..' */ dp->d_fileno = PROCFS_FILENO(0, Proot); dp->d_namlen = i + 1; bcopy("..", dp->d_name, dp->d_namlen); dp->d_name[i + 1] = '\0'; dp->d_type = DT_DIR; break; case 2: dp->d_fileno = PROCFS_FILENO(0, Pcurproc); dp->d_namlen = 7; bcopy("curproc", dp->d_name, 8); dp->d_type = DT_LNK; break; default: while (pcnt < i) { p = p->p_list.le_next; if (!p) goto done; if (!PRISON_CHECK(curproc, p)) continue; pcnt++; } while (!PRISON_CHECK(curproc, p)) { p = p->p_list.le_next; if (!p) goto done; } dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); dp->d_namlen = sprintf(dp->d_name, "%ld", (long)p->p_pid); dp->d_type = DT_REG; p = p->p_list.le_next; break; } if ((error = uiomove((caddr_t)dp, delen, uio)) != 0) break; } done: #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = zombproc.lh_first; goto again; } #endif break; } default: error = ENOTDIR; break; } uio->uio_offset = i * delen; return (error); } /* * readlink reads the link of `curproc' */ static int procfs_readlink(ap) struct vop_readlink_args *ap; { char buf[16]; /* should be enough */ int len; if (VTOPFS(ap->a_vp)->pfs_fileno != PROCFS_FILENO(0, Pcurproc)) return (EINVAL); len = snprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid); return (uiomove((caddr_t)buf, len, ap->a_uio)); } /* * convert decimal ascii to pid_t */ static pid_t atopid(b, len) const char *b; u_int len; { pid_t p = 0; while (len--) { char c = *b++; if (c < '0' || c > '9') return (NO_PID); p = 10 * p + (c - '0'); if (p > PID_MAX) return (NO_PID); } return (p); } /* * procfs vnode operations. */ vop_t **procfs_vnodeop_p; static struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_abortop_desc, (vop_t *) procfs_abortop }, { &vop_access_desc, (vop_t *) procfs_access }, { &vop_advlock_desc, (vop_t *) procfs_badop }, { &vop_bmap_desc, (vop_t *) procfs_bmap }, { &vop_close_desc, (vop_t *) procfs_close }, { &vop_create_desc, (vop_t *) procfs_badop }, { &vop_getattr_desc, (vop_t *) procfs_getattr }, { &vop_inactive_desc, (vop_t *) procfs_inactive }, { &vop_link_desc, (vop_t *) procfs_badop }, { &vop_lookup_desc, (vop_t *) procfs_lookup }, { &vop_mkdir_desc, (vop_t *) procfs_badop }, { &vop_mknod_desc, (vop_t *) procfs_badop }, { &vop_open_desc, (vop_t *) procfs_open }, { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, { &vop_print_desc, (vop_t *) procfs_print }, { &vop_read_desc, (vop_t *) procfs_rw }, { &vop_readdir_desc, (vop_t *) procfs_readdir }, { &vop_readlink_desc, (vop_t *) procfs_readlink }, { &vop_reclaim_desc, (vop_t *) procfs_reclaim }, { &vop_remove_desc, (vop_t *) procfs_badop }, { &vop_rename_desc, (vop_t *) procfs_badop }, { &vop_rmdir_desc, (vop_t *) procfs_badop }, { &vop_setattr_desc, (vop_t *) procfs_setattr }, { &vop_symlink_desc, (vop_t *) procfs_badop }, { &vop_write_desc, (vop_t *) procfs_rw }, { &vop_ioctl_desc, (vop_t *) procfs_ioctl }, { NULL, NULL } }; static struct vnodeopv_desc procfs_vnodeop_opv_desc = { &procfs_vnodeop_p, procfs_vnodeop_entries }; VNODEOP_SET(procfs_vnodeop_opv_desc); diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 9373d5d290e0..6b1ae41e7b6a 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,430 +1,431 @@ /*- * Copyright (c) 1986, 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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 University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)proc.h 8.15 (Berkeley) 5/19/95 * $FreeBSD$ */ #ifndef _SYS_PROC_H_ #define _SYS_PROC_H_ #include /* Machine-dependent proc substruct. */ #include /* For struct callout_handle. */ #include #include #include /* For struct rtprio. */ #include #ifndef KERNEL #include /* For structs itimerval, timeval. */ #endif #include /* * One structure allocated per session. */ struct session { int s_count; /* Ref cnt; pgrps in session. */ struct proc *s_leader; /* Session leader. */ struct vnode *s_ttyvp; /* Vnode of controlling terminal. */ struct tty *s_ttyp; /* Controlling terminal. */ pid_t s_sid; /* Session ID */ char s_login[roundup(MAXLOGNAME, sizeof(long))]; /* Setlogin() name. */ }; /* * One structure allocated per process group. */ struct pgrp { LIST_ENTRY(pgrp) pg_hash; /* Hash chain. */ LIST_HEAD(, proc) pg_members; /* Pointer to pgrp members. */ struct session *pg_session; /* Pointer to session. */ struct sigiolst pg_sigiolst; /* List of sigio sources. */ pid_t pg_id; /* Pgrp id. */ int pg_jobc; /* # procs qualifying pgrp for job control */ }; struct procsig { sigset_t ps_sigignore; /* Signals being ignored. */ sigset_t ps_sigcatch; /* Signals being caught by user. */ int ps_flag; struct sigacts *ps_sigacts; int ps_refcnt; }; #define PS_NOCLDWAIT 0x0001 /* No zombies if child dies */ #define PS_NOCLDSTOP 0x0002 /* No SIGCHLD when children stop. */ /* * pasleep structure, used by asleep() syscall to hold requested priority * and timeout values for await(). */ struct pasleep { int as_priority; /* Async priority. */ int as_timo; /* Async timeout. */ }; /* * pargs, used to hold a copy of the command line, if it had a sane * length */ struct pargs { u_int ar_ref; /* Reference count */ u_int ar_length; /* Length */ u_char ar_args[0]; /* Arguments */ }; /* * Description of a process. * * This structure contains the information needed to manage a thread of * control, known in UN*X as a process; it has references to substructures * containing descriptions of things that the process uses, but may share * with related processes. The process structure and the substructures * are always addressable except for those marked "(PROC ONLY)" below, * which might be addressable only on a processor on which the process * is running. */ struct jail; struct proc { TAILQ_ENTRY(proc) p_procq; /* run/sleep queue. */ LIST_ENTRY(proc) p_list; /* List of all processes. */ /* substructures: */ struct pcred *p_cred; /* Process owner's identity. */ struct filedesc *p_fd; /* Ptr to open files structure. */ struct pstats *p_stats; /* Accounting/statistics (PROC ONLY). */ struct plimit *p_limit; /* Process limits. */ struct vm_object *p_upages_obj;/* Upages object */ struct procsig *p_procsig; #define p_sigacts p_procsig->ps_sigacts #define p_sigignore p_procsig->ps_sigignore #define p_sigcatch p_procsig->ps_sigcatch #define p_ucred p_cred->pc_ucred #define p_rlimit p_limit->pl_rlimit int p_flag; /* P_* flags. */ char p_stat; /* S* process status. */ char p_pad1[3]; pid_t p_pid; /* Process identifier. */ LIST_ENTRY(proc) p_hash; /* Hash chain. */ LIST_ENTRY(proc) p_pglist; /* List of processes in pgrp. */ struct proc *p_pptr; /* Pointer to parent process. */ LIST_ENTRY(proc) p_sibling; /* List of sibling processes. */ LIST_HEAD(, proc) p_children; /* Pointer to list of children. */ struct callout_handle p_ithandle; /* * Callout handle for scheduling * p_realtimer. */ /* The following fields are all zeroed upon creation in fork. */ #define p_startzero p_oppid pid_t p_oppid; /* Save parent pid during ptrace. XXX */ int p_dupfd; /* Sideways return value from fdopen. XXX */ struct vmspace *p_vmspace; /* Address space. */ /* scheduling */ u_int p_estcpu; /* Time averaged value of p_cpticks. */ int p_cpticks; /* Ticks of cpu time. */ fixpt_t p_pctcpu; /* %cpu for this process during p_swtime */ void *p_wchan; /* Sleep address. */ const char *p_wmesg; /* Reason for sleep. */ u_int p_swtime; /* Time swapped in or out. */ u_int p_slptime; /* Time since last blocked. */ struct itimerval p_realtimer; /* Alarm timer. */ u_int64_t p_runtime; /* Real time in microsec. */ u_quad_t p_uticks; /* Statclock hits in user mode. */ u_quad_t p_sticks; /* Statclock hits in system mode. */ u_quad_t p_iticks; /* Statclock hits processing intr. */ int p_traceflag; /* Kernel trace points. */ struct vnode *p_tracep; /* Trace to vnode. */ sigset_t p_siglist; /* Signals arrived but not delivered. */ struct vnode *p_textvp; /* Vnode of executable. */ char p_lock; /* Process lock (prevent swap) count. */ u_char p_oncpu; /* Which cpu we are on */ u_char p_lastcpu; /* Last cpu we were on */ char p_rqindex; /* Run queue index */ short p_locks; /* DEBUG: lockmgr count of held locks */ short p_simple_locks; /* DEBUG: count of held simple locks */ unsigned int p_stops; /* procfs event bitmask */ unsigned int p_stype; /* procfs stop event type */ char p_step; /* procfs stop *once* flag */ unsigned char p_pfsflags; /* procfs flags */ char p_pad3[2]; /* padding for alignment */ register_t p_retval[2]; /* syscall aux returns */ struct sigiolst p_sigiolst; /* list of sigio sources */ int p_sigparent; /* signal to parent on exit */ sigset_t p_oldsigmask; /* saved mask from before sigpause */ int p_sig; /* for core dump/debugger XXX */ u_long p_code; /* for core dump/debugger XXX */ /* End area that is zeroed on creation. */ #define p_endzero p_startcopy /* The following fields are all copied upon creation in fork. */ #define p_startcopy p_sigmask sigset_t p_sigmask; /* Current signal mask. */ stack_t p_sigstk; /* sp & on stack state variable */ u_char p_priority; /* Process priority. */ u_char p_usrpri; /* User-priority based on p_cpu and p_nice. */ char p_nice; /* Process "nice" value. */ char p_comm[MAXCOMLEN+1]; struct pgrp *p_pgrp; /* Pointer to process group. */ struct sysentvec *p_sysent; /* System call dispatch information. */ struct rtprio p_rtprio; /* Realtime priority. */ struct prison *p_prison; struct pargs *p_args; /* End area that is copied on creation. */ #define p_endcopy p_addr struct user *p_addr; /* Kernel virtual addr of u-area (PROC ONLY). */ struct mdproc p_md; /* Any machine-dependent fields. */ u_short p_xstat; /* Exit status for wait; also stop signal. */ u_short p_acflag; /* Accounting flags. */ struct rusage *p_ru; /* Exit information. XXX */ int p_nthreads; /* number of threads (only in leader) */ void *p_aioinfo; /* ASYNC I/O info */ int p_wakeup; /* thread id */ struct proc *p_peers; struct proc *p_leader; struct pasleep p_asleep; /* Used by asleep()/await(). */ void *p_emuldata; /* process-specific emulator state data */ }; #define p_session p_pgrp->pg_session #define p_pgid p_pgrp->pg_id /* Status values. */ #define SIDL 1 /* Process being created by fork. */ #define SRUN 2 /* Currently runnable. */ #define SSLEEP 3 /* Sleeping on an address. */ #define SSTOP 4 /* Process debugging or suspension. */ #define SZOMB 5 /* Awaiting collection by parent. */ /* These flags are kept in p_flags. */ #define P_ADVLOCK 0x00001 /* Process may hold a POSIX advisory lock. */ #define P_CONTROLT 0x00002 /* Has a controlling terminal. */ #define P_INMEM 0x00004 /* Loaded into memory. */ #define P_PPWAIT 0x00010 /* Parent is waiting for child to exec/exit. */ #define P_PROFIL 0x00020 /* Has started profiling. */ #define P_SELECT 0x00040 /* Selecting; wakeup/waiting danger. */ #define P_SINTR 0x00080 /* Sleep is interruptible. */ #define P_SUGID 0x00100 /* Had set id privileges since last exec. */ #define P_SYSTEM 0x00200 /* System proc: no sigs, stats or swapping. */ #define P_TIMEOUT 0x00400 /* Timing out during sleep. */ #define P_TRACED 0x00800 /* Debugged process being traced. */ #define P_WAITED 0x01000 /* Debugging process has waited for child. */ #define P_WEXIT 0x02000 /* Working on exiting. */ #define P_EXEC 0x04000 /* Process called exec. */ /* Should probably be changed into a hold count. */ /* was P_NOSWAP 0x08000 was: Do not swap upages; p->p_hold */ /* was P_PHYSIO 0x10000 was: Doing physical I/O; use p->p_hold */ /* Should be moved to machine-dependent areas. */ #define P_OWEUPC 0x20000 /* Owe process an addupc() call at next ast. */ #define P_SWAPPING 0x40000 /* Process is being swapped. */ #define P_SWAPINREQ 0x80000 /* Swapin request due to wakeup */ /* Marked a kernel thread */ #define P_BUFEXHAUST 0x100000 /* dirty buffers flush is in progress */ #define P_KTHREADP 0x200000 /* Process is really a kernel thread */ #define P_DEADLKTREAT 0x800000 /* lock aquisition - deadlock treatment */ #define P_JAILED 0x1000000 /* Process is in jail */ #define P_OLDMASK 0x2000000 /* need to restore mask before pause */ #define P_ALTSTACK 0x4000000 /* have alternate signal stack */ /* * MOVE TO ucred.h? * * Shareable process credentials (always resident). This includes a reference * to the current user credentials as well as real and saved ids that may be * used to change ids. */ struct pcred { struct ucred *pc_ucred; /* Current credentials. */ uid_t p_ruid; /* Real user id. */ uid_t p_svuid; /* Saved effective user id. */ gid_t p_rgid; /* Real group id. */ gid_t p_svgid; /* Saved effective group id. */ int p_refcnt; /* Number of references. */ }; #ifdef KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_SESSION); MALLOC_DECLARE(M_SUBPROC); MALLOC_DECLARE(M_ZOMBIE); MALLOC_DECLARE(M_PARGS); #endif /* flags for suser_xxx() */ #define PRISON_ROOT 1 /* Handy macro to determine of p1 can mangle p2 */ #define PRISON_CHECK(p1, p2) \ ((!(p1)->p_prison) || (p1)->p_prison == (p2)->p_prison) /* * We use process IDs <= PID_MAX; PID_MAX + 1 must also fit in a pid_t, * as it is used to represent "no process group". */ #define PID_MAX 99999 #define NO_PID 100000 #define SESS_LEADER(p) ((p)->p_session->s_leader == (p)) #define SESSHOLD(s) ((s)->s_count++) #define SESSRELE(s) { \ if (--(s)->s_count == 0) \ FREE(s, M_SESSION); \ } extern void stopevent(struct proc*, unsigned int, unsigned int); #define STOPEVENT(p,e,v) do { \ if ((p)->p_stops & (e)) stopevent(p,e,v); } while (0) /* hold process U-area in memory, normally for ptrace/procfs work */ #define PHOLD(p) { \ if ((p)->p_lock++ == 0 && ((p)->p_flag & P_INMEM) == 0) \ faultin(p); \ } #define PRELE(p) (--(p)->p_lock) #define PIDHASH(pid) (&pidhashtbl[(pid) & pidhash]) extern LIST_HEAD(pidhashhead, proc) *pidhashtbl; extern u_long pidhash; #define PGRPHASH(pgid) (&pgrphashtbl[(pgid) & pgrphash]) extern LIST_HEAD(pgrphashhead, pgrp) *pgrphashtbl; extern u_long pgrphash; #ifndef SET_CURPROC #define SET_CURPROC(p) (curproc = (p)) #endif #ifndef curproc extern struct proc *curproc; /* Current running proc. */ extern int switchticks; /* `ticks' at last context switch. */ extern struct timeval switchtime; /* Uptime at last context switch */ #endif extern struct proc proc0; /* Process slot for swapper. */ extern int hogticks; /* Limit on kernel cpu hogs. */ extern int nprocs, maxproc; /* Current and max number of procs. */ extern int maxprocperuid; /* Max procs per uid. */ extern int sched_quantum; /* Scheduling quantum in ticks */ LIST_HEAD(proclist, proc); extern struct proclist allproc; /* List of all processes. */ extern struct proclist zombproc; /* List of zombie processes. */ extern struct proc *initproc, *pageproc, *updateproc; /* Process slots for init, pager. */ #define NQS 32 /* 32 run queues. */ TAILQ_HEAD(rq, proc); extern struct rq queues[]; extern struct rq rtqueues[]; extern struct rq idqueues[]; extern int whichqs; /* Bit mask summary of non-empty Q's. */ extern int whichrtqs; /* Bit mask summary of non-empty Q's. */ extern int whichidqs; /* Bit mask summary of non-empty Q's. */ extern u_long ps_arg_cache_limit; struct proc *pfind __P((pid_t)); /* Find process by id. */ struct pgrp *pgfind __P((pid_t)); /* Find process group by id. */ struct vm_zone; extern struct vm_zone *proc_zone; int chgproccnt __P((uid_t uid, int diff)); rlim_t chgsbsize __P((uid_t uid, rlim_t diff)); int enterpgrp __P((struct proc *p, pid_t pgid, int mksess)); void fixjobc __P((struct proc *p, struct pgrp *pgrp, int entering)); int inferior __P((struct proc *p)); int leavepgrp __P((struct proc *p)); void mi_switch __P((void)); void procinit __P((void)); +int p_trespass __P((struct proc *p1, struct proc *p2)); void resetpriority __P((struct proc *)); int roundrobin_interval __P((void)); void setrunnable __P((struct proc *)); void setrunqueue __P((struct proc *)); void sleepinit __P((void)); int suser __P((struct proc *)); int suser_xxx __P((struct ucred *cred, struct proc *proc, int flag)); void remrunqueue __P((struct proc *)); void cpu_switch __P((struct proc *)); void unsleep __P((struct proc *)); void wakeup_one __P((void *chan)); void cpu_exit __P((struct proc *)) __dead2; void exit1 __P((struct proc *, int)) __dead2; void cpu_fork __P((struct proc *, struct proc *)); void cpu_set_fork_handler __P((struct proc *, void (*)(void *), void *)); int fork1 __P((struct proc *, int, struct proc **)); int trace_req __P((struct proc *)); void cpu_wait __P((struct proc *)); int cpu_coredump __P((struct proc *, struct vnode *, struct ucred *)); void setsugid __P((struct proc *p)); void faultin __P((struct proc *p)); struct proc * chooseproc __P((void)); u_int32_t procrunnable __P((void)); #endif /* KERNEL */ #endif /* !_SYS_PROC_H_ */