X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..5d5c5d0d5b79ade9a973d55186ffda2638ba2b6e:/bsd/kern/kern_sysctl.c diff --git a/bsd/kern/kern_sysctl.c b/bsd/kern/kern_sysctl.c index c7332ad05..b441586ec 100644 --- a/bsd/kern/kern_sysctl.c +++ b/bsd/kern/kern_sysctl.c @@ -1,23 +1,31 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the + * License may not be used to create, or enable the creation or + * redistribution of, unlawful or unlicensed copies of an Apple operating + * system, or to circumvent, violate, or enable the circumvention or + * violation of, any terms of an Apple operating system software license + * agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ /*- @@ -66,37 +74,51 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include #include +#include #include #include #include #include +#include +#include + +#include + #include #include #include #include +#include #include +#include #include extern vm_map_t bsd_pageable_map; -#include +#include #include +#include #include #include -#if __ppc__ -#include +#include +#include + +#include + +#ifdef __i386__ +#include #endif sysctlfn kern_sysctl; -sysctlfn hw_sysctl; #ifdef DEBUG sysctlfn debug_sysctl; #endif @@ -104,61 +126,159 @@ extern sysctlfn vm_sysctl; extern sysctlfn vfs_sysctl; extern sysctlfn net_sysctl; extern sysctlfn cpu_sysctl; - - -int -userland_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t - *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval); - -void -fill_proc(struct proc *p,struct kinfo_proc *kp, int doingzomb); - -void +extern int aio_max_requests; +extern int aio_max_requests_per_process; +extern int aio_worker_threads; +extern int maxfilesperproc; +extern int lowpri_IO_window_msecs; +extern int lowpri_IO_delay_msecs; +extern int nx_enabled; + +static void +fill_eproc(struct proc *p, struct eproc *ep); +static void fill_externproc(struct proc *p, struct extern_proc *exp); - +static void +fill_user_eproc(struct proc *p, struct user_eproc *ep); +static void +fill_user_proc(struct proc *p, struct user_kinfo_proc *kp); +static void +fill_user_externproc(struct proc *p, struct user_extern_proc *exp); +extern int +kdbg_control(int *name, u_int namelen, user_addr_t where, size_t * sizep); +int +kdebug_ops(int *name, u_int namelen, user_addr_t where, size_t *sizep, struct proc *p); +#if NFSCLIENT +extern int +netboot_root(void); +#endif +int +pcsamples_ops(int *name, u_int namelen, user_addr_t where, size_t *sizep, + struct proc *p); +__private_extern__ kern_return_t +reset_vmobjectcache(unsigned int val1, unsigned int val2); +extern int +resize_namecache(u_int newsize); +static int +sysctl_aiomax(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, size_t newlen); +static int +sysctl_aioprocmax(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, size_t newlen); +static int +sysctl_aiothreads(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, size_t newlen); +extern int +sysctl_clockrate(user_addr_t where, size_t *sizep); +int +sysctl_doproc(int *name, u_int namelen, user_addr_t where, size_t *sizep); +int +sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen); +int +sysctl_file(user_addr_t where, size_t *sizep); +static void +fill_proc(struct proc *p, struct kinfo_proc *kp); +static int +sysctl_maxfilesperproc(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen); +static int +sysctl_maxprocperuid(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen); +static int +sysctl_maxproc(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen); +int +sysctl_procargs(int *name, u_int namelen, user_addr_t where, + size_t *sizep, struct proc *cur_proc); +static int +sysctl_procargs2(int *name, u_int namelen, user_addr_t where, size_t *sizep, + struct proc *cur_proc); +static int +sysctl_procargsx(int *name, u_int namelen, user_addr_t where, size_t *sizep, + struct proc *cur_proc, int argc_yes); +int +sysctl_struct(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, + size_t newlen, void *sp, int len); +extern int +sysctl_vnode(user_addr_t where, size_t *sizep); /* * temporary location for vm_sysctl. This should be machine independant */ -vm_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) - int *name; - u_int namelen; - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; - struct proc *p; + +extern uint32_t mach_factor[3]; + +static void +loadavg32to64(struct loadavg *la32, struct user_loadavg *la64) { - int error, level, inthostid; - extern long avenrun[3], mach_factor[3]; - struct loadavg loadinfo; + la64->ldavg[0] = la32->ldavg[0]; + la64->ldavg[1] = la32->ldavg[1]; + la64->ldavg[2] = la32->ldavg[2]; + la64->fscale = (user_long_t)la32->fscale; +} - //if (namelen != 1 && !(name[0] == VM_LOADAVG)) - //return (ENOTDIR); /* overloaded */ +int +vm_sysctl(int *name, __unused u_int namelen, user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, __unused struct proc *p) +{ + struct loadavg loadinfo; switch (name[0]) { case VM_LOADAVG: - loadinfo.ldavg[0] = avenrun[0]; - loadinfo.ldavg[1] = avenrun[1]; - loadinfo.ldavg[2] = avenrun[2]; - loadinfo.fscale = LSCALE; - return (sysctl_struct(oldp, oldlenp, newp, newlen, &loadinfo, sizeof(struct loadavg))); + if (proc_is64bit(p)) { + struct user_loadavg loadinfo64; + loadavg32to64(&averunnable, &loadinfo64); + return (sysctl_struct(oldp, oldlenp, newp, newlen, + &loadinfo64, sizeof(loadinfo64))); + } else { + return (sysctl_struct(oldp, oldlenp, newp, newlen, + &averunnable, sizeof(struct loadavg))); + } case VM_MACHFACTOR: loadinfo.ldavg[0] = mach_factor[0]; loadinfo.ldavg[1] = mach_factor[1]; loadinfo.ldavg[2] = mach_factor[2]; loadinfo.fscale = LSCALE; - return (sysctl_struct(oldp, oldlenp, newp, newlen, &loadinfo, sizeof(struct loadavg))); + if (proc_is64bit(p)) { + struct user_loadavg loadinfo64; + loadavg32to64(&loadinfo, &loadinfo64); + return (sysctl_struct(oldp, oldlenp, newp, newlen, + &loadinfo64, sizeof(loadinfo64))); + } else { + return (sysctl_struct(oldp, oldlenp, newp, newlen, + &loadinfo, sizeof(struct loadavg))); + } + case VM_SWAPUSAGE: { + int error; + uint64_t swap_total; + uint64_t swap_avail; + uint32_t swap_pagesize; + boolean_t swap_encrypted; + struct xsw_usage xsu; + + error = macx_swapinfo(&swap_total, + &swap_avail, + &swap_pagesize, + &swap_encrypted); + if (error) + return error; + + xsu.xsu_total = swap_total; + xsu.xsu_avail = swap_avail; + xsu.xsu_used = swap_total - swap_avail; + xsu.xsu_pagesize = swap_pagesize; + xsu.xsu_encrypted = swap_encrypted; + return sysctl_struct(oldp, oldlenp, newp, newlen, + &xsu, sizeof (struct xsw_usage)); + } case VM_METER: - return (EOPNOTSUPP); + return (ENOTSUP); case VM_MAXID: - return (EOPNOTSUPP); + return (ENOTSUP); default: - return (EOPNOTSUPP); + return (ENOTSUP); } /* NOTREACHED */ - return (EOPNOTSUPP); + return (ENOTSUP); } /* @@ -170,83 +290,91 @@ static struct sysctl_lock { int sl_locked; } memlock; -struct __sysctl_args { - int *name; - u_int namelen; - void *old; - size_t *oldlenp; - void *new; - size_t newlen; -}; int -__sysctl(p, uap, retval) - struct proc *p; - register struct __sysctl_args *uap; - register_t *retval; +__sysctl(struct proc *p, struct __sysctl_args *uap, __unused register_t *retval) { int error, dolock = 1; - size_t savelen, oldlen = 0; - sysctlfn *fn; + size_t savelen = 0, oldlen = 0, newlen; + sysctlfn *fnp = NULL; int name[CTL_MAXNAME]; int i; + int error1; /* * all top-level sysctl names are non-terminal */ if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) return (EINVAL); - if (error = - copyin(uap->name, &name, uap->namelen * sizeof(int))) + error = copyin(uap->name, &name[0], uap->namelen * sizeof(int)); + if (error) return (error); + + AUDIT_ARG(ctlname, name, uap->namelen); + + if (proc_is64bit(p)) { + /* uap->newlen is a size_t value which grows to 64 bits + * when coming from a 64-bit process. since it's doubtful we'll + * have a sysctl newp buffer greater than 4GB we shrink it to size_t + */ + newlen = CAST_DOWN(size_t, uap->newlen); + } + else { + newlen = uap->newlen; + } /* CTL_UNSPEC is used to get oid to AUTO_OID */ - if (uap->new != NULL && - (((name[0] == CTL_KERN) && (name[1] != KERN_IPC)) || - (name[0] == CTL_HW) || (name[0] == CTL_VM) || - (name[0] == CTL_VFS)) && - (error = suser(p->p_ucred, &p->p_acflag))) + if (uap->new != USER_ADDR_NULL + && ((name[0] == CTL_KERN + && !(name[1] == KERN_IPC || name[1] == KERN_PANICINFO || name[1] == KERN_PROCDELAYTERM || + name[1] == KERN_PROC_LOW_PRI_IO || name[1] == KERN_PROCNAME || name[1] == KERN_THALTSTACK)) + || (name[0] == CTL_HW) + || (name[0] == CTL_VM) + || (name[0] == CTL_VFS)) + && (error = suser(kauth_cred_get(), &p->p_acflag))) return (error); switch (name[0]) { case CTL_KERN: - fn = kern_sysctl; - if (name[1] != KERN_VNODE) /* XXX */ + fnp = kern_sysctl; + if ((name[1] != KERN_VNODE) && (name[1] != KERN_FILE) + && (name[1] != KERN_PROC)) dolock = 0; break; - case CTL_HW: - fn = hw_sysctl; - break; case CTL_VM: - fn = vm_sysctl; + fnp = vm_sysctl; break; case CTL_VFS: - fn = vfs_sysctl; - break; -#if FIXME /* [ */ - case CTL_MACHDEP: - fn = cpu_sysctl; + fnp = vfs_sysctl; break; -#endif /* FIXME ] */ #ifdef DEBUG case CTL_DEBUG: - fn = debug_sysctl; + fnp = debug_sysctl; break; #endif default: - fn = 0; + fnp = NULL; } - if (uap->oldlenp && - (error = copyin(uap->oldlenp, &oldlen, sizeof(oldlen)))) - return (error); + if (uap->oldlenp != USER_ADDR_NULL) { + uint64_t oldlen64 = fuulong(uap->oldlenp); - if (uap->old != NULL) { - if (!useracc(uap->old, oldlen, B_WRITE)) + oldlen = CAST_DOWN(size_t, oldlen64); + /* + * If more than 4G, clamp to 4G - useracc() below will catch + * with an EFAULT, if it's actually necessary. + */ + if (oldlen64 > 0x00000000ffffffffULL) + oldlen = 0xffffffffUL; + } + + if (uap->old != USER_ADDR_NULL) { + if (!useracc(uap->old, (user_size_t)oldlen, B_WRITE)) return (EFAULT); /* The pc sampling mechanism does not need to take this lock */ - if (name[1] != KERN_PCSAMPLES) { + if ((name[1] != KERN_PCSAMPLES) && + (!((name[1] == KERN_KDEBUG) && (name[2] == KERN_KDGETENTROPY)))) { while (memlock.sl_lock) { memlock.sl_want = 1; sleep((caddr_t)&memlock, PRIBIO+1); @@ -255,25 +383,40 @@ __sysctl(p, uap, retval) memlock.sl_lock = 1; } - if (dolock) - vslock(uap->old, oldlen); + if (dolock && oldlen && + (error = vslock(uap->old, (user_size_t)oldlen))) { + if ((name[1] != KERN_PCSAMPLES) && + (! ((name[1] == KERN_KDEBUG) && (name[2] == KERN_KDGETENTROPY)))) { + memlock.sl_lock = 0; + if (memlock.sl_want) { + memlock.sl_want = 0; + wakeup((caddr_t)&memlock); + } + } + return(error); + } savelen = oldlen; } - if (fn) - error = (*fn)(name + 1, uap->namelen - 1, uap->old, - &oldlen, uap->new, uap->newlen, p); + if (fnp) { + error = (*fnp)(name + 1, uap->namelen - 1, uap->old, + &oldlen, uap->new, newlen, p); + } else - error = EOPNOTSUPP; + error = ENOTSUP; - if ( (name[0] != CTL_VFS) && (error == EOPNOTSUPP)) - error = userland_sysctl(p, name, uap->namelen, - uap->old, uap->oldlenp, 0, - uap->new, uap->newlen, &oldlen); + if ( (name[0] != CTL_VFS) && (error == ENOTSUP)) { + size_t tmp = oldlen; + error = userland_sysctl(p, name, uap->namelen, uap->old, &tmp, + 1, uap->new, newlen, &oldlen); + } - if (uap->old != NULL) { - if (dolock) - vsunlock(uap->old, savelen, B_WRITE); + if (uap->old != USER_ADDR_NULL) { + if (dolock && savelen) { + error1 = vsunlock(uap->old, (user_size_t)savelen, B_WRITE); + if (!error && error1) + error = error1; + } if (name[1] != KERN_PCSAMPLES) { memlock.sl_lock = 0; if (memlock.sl_want) { @@ -285,8 +428,8 @@ __sysctl(p, uap, retval) if ((error) && (error != ENOMEM)) return (error); - if (uap->oldlenp) { - i = copyout(&oldlen, uap->oldlenp, sizeof(oldlen)); + if (uap->oldlenp != USER_ADDR_NULL) { + i = suulong(uap->oldlenp, oldlen); if (i) return i; } @@ -297,42 +440,209 @@ __sysctl(p, uap, retval) /* * Attributes stored in the kernel. */ -extern char hostname[MAXHOSTNAMELEN]; /* defined in bsd/kern/init_main.c */ -extern int hostnamelen; -extern char domainname[MAXHOSTNAMELEN]; -extern int domainnamelen; -extern long hostid; +__private_extern__ char corefilename[MAXPATHLEN+1]; +__private_extern__ int do_coredump; +__private_extern__ int sugid_coredump; + + #ifdef INSECURE int securelevel = -1; #else int securelevel; #endif -int get_kernel_symfile( struct proc *p, char **symfile ); +static int +sysctl_affinity( + int *name, + u_int namelen, + user_addr_t oldBuf, + size_t *oldSize, + user_addr_t newBuf, + __unused size_t newSize, + struct proc *cur_proc) +{ + if (namelen < 1) + return (ENOTSUP); + + if (name[0] == 0 && 1 == namelen) { + return sysctl_rdint(oldBuf, oldSize, newBuf, + (cur_proc->p_flag & P_AFFINITY) ? 1 : 0); + } else if (name[0] == 1 && 2 == namelen) { + if (name[1] == 0) { + cur_proc->p_flag &= ~P_AFFINITY; + } else { + cur_proc->p_flag |= P_AFFINITY; + } + return 0; + } + return (ENOTSUP); +} + + +static int +sysctl_translate( + int *name, + u_int namelen, + user_addr_t oldBuf, + size_t *oldSize, + user_addr_t newBuf, + __unused size_t newSize, + struct proc *cur_proc) +{ + struct proc *p; + + if (namelen != 1) + return (ENOTSUP); + + p = pfind(name[0]); + if (p == NULL) + return (EINVAL); + + if ((kauth_cred_getuid(p->p_ucred) != kauth_cred_getuid(kauth_cred_get())) + && suser(kauth_cred_get(), &cur_proc->p_acflag)) + return (EPERM); + + return sysctl_rdint(oldBuf, oldSize, newBuf, + (p->p_flag & P_TRANSLATED) ? 1 : 0); +} + +int +set_archhandler(struct proc *p, int arch) +{ + int error; + struct nameidata nd; + struct vnode_attr va; + struct vfs_context context; + char *archhandler; + + switch(arch) { + case CPU_TYPE_POWERPC: + archhandler = exec_archhandler_ppc.path; + break; + default: + return (EBADARCH); + } + + context.vc_proc = p; + context.vc_ucred = kauth_cred_get(); + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32, + CAST_USER_ADDR_T(archhandler), &context); + error = namei(&nd); + if (error) + return (error); + nameidone(&nd); + + /* Check mount point */ + if ((nd.ni_vp->v_mount->mnt_flag & MNT_NOEXEC) || + (nd.ni_vp->v_type != VREG)) { + vnode_put(nd.ni_vp); + return (EACCES); + } + + VATTR_INIT(&va); + VATTR_WANTED(&va, va_fsid); + VATTR_WANTED(&va, va_fileid); + error = vnode_getattr(nd.ni_vp, &va, &context); + if (error) { + vnode_put(nd.ni_vp); + return (error); + } + vnode_put(nd.ni_vp); + + exec_archhandler_ppc.fsid = va.va_fsid; + exec_archhandler_ppc.fileid = (u_long)va.va_fileid; + return 0; +} + +static int +sysctl_exec_archhandler_ppc( + __unused int *name, + __unused u_int namelen, + user_addr_t oldBuf, + size_t *oldSize, + user_addr_t newBuf, + size_t newSize, + struct proc *p) +{ + int error; + size_t len; + struct nameidata nd; + struct vnode_attr va; + char handler[sizeof(exec_archhandler_ppc.path)]; + struct vfs_context context; + + context.vc_proc = p; + context.vc_ucred = kauth_cred_get(); + + if (oldSize) { + len = strlen(exec_archhandler_ppc.path) + 1; + if (oldBuf) { + if (*oldSize < len) + return (ENOMEM); + error = copyout(exec_archhandler_ppc.path, oldBuf, len); + if (error) + return (error); + } + *oldSize = len - 1; + } + if (newBuf) { + error = suser(context.vc_ucred, &p->p_acflag); + if (error) + return (error); + if (newSize >= sizeof(exec_archhandler_ppc.path)) + return (ENAMETOOLONG); + error = copyin(newBuf, handler, newSize); + if (error) + return (error); + handler[newSize] = 0; + strcpy(exec_archhandler_ppc.path, handler); + error = set_archhandler(p, CPU_TYPE_POWERPC); + if (error) + return (error); + } + return 0; +} + +SYSCTL_NODE(_kern, KERN_EXEC, exec, CTLFLAG_RD, 0, ""); + +SYSCTL_NODE(_kern_exec, OID_AUTO, archhandler, CTLFLAG_RD, 0, ""); + +SYSCTL_STRING(_kern_exec_archhandler, OID_AUTO, powerpc, CTLFLAG_RD, + exec_archhandler_ppc.path, 0, ""); + +extern int get_kernel_symfile( struct proc *, char **); +__private_extern__ int +sysctl_dopanicinfo(int *, u_int, user_addr_t, size_t *, user_addr_t, + size_t, struct proc *); /* * kernel related system variables. */ -kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) - int *name; - u_int namelen; - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; - struct proc *p; +int +kern_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, struct proc *p) { - int error, level, inthostid; + int error, level, inthostid, tmp; unsigned int oldval=0; - extern char ostype[], osrelease[], version[]; - - /* all sysctl names at this level are terminal */ - if (namelen != 1 && !(name[0] == KERN_PROC || name[0] == KERN_PROF - || name[0] == KERN_KDEBUG - || name[0] == KERN_PROCARGS - || name[0] == KERN_PCSAMPLES - || name[0] == KERN_IPC - )) + char *str; + /* all sysctl names not listed below are terminal at this level */ + if (namelen != 1 + && !(name[0] == KERN_PROC + || name[0] == KERN_PROF + || name[0] == KERN_KDEBUG + || name[0] == KERN_PROCARGS + || name[0] == KERN_PROCARGS2 + || name[0] == KERN_PCSAMPLES + || name[0] == KERN_IPC + || name[0] == KERN_SYSV + || name[0] == KERN_AFFINITY + || name[0] == KERN_TRANSLATE + || name[0] == KERN_EXEC + || name[0] == KERN_PANICINFO + || name[0] == KERN_POSIX + || name[0] == KERN_TFP) + ) return (ENOTDIR); /* overloaded */ switch (name[0]) { @@ -349,24 +659,29 @@ kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) error = sysctl_int(oldp, oldlenp, newp, newlen, &desiredvnodes); reset_vmobjectcache(oldval, desiredvnodes); + resize_namecache(desiredvnodes); return(error); case KERN_MAXPROC: - return (sysctl_int(oldp, oldlenp, newp, newlen, &maxproc)); + return (sysctl_maxproc(oldp, oldlenp, newp, newlen)); case KERN_MAXFILES: return (sysctl_int(oldp, oldlenp, newp, newlen, &maxfiles)); + case KERN_MAXPROCPERUID: + return( sysctl_maxprocperuid( oldp, oldlenp, newp, newlen ) ); + case KERN_MAXFILESPERPROC: + return( sysctl_maxfilesperproc( oldp, oldlenp, newp, newlen ) ); case KERN_ARGMAX: return (sysctl_rdint(oldp, oldlenp, newp, ARG_MAX)); case KERN_SECURELVL: level = securelevel; if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &level)) || - newp == NULL) + newp == USER_ADDR_NULL) return (error); if (level < securelevel && p->p_pid != 1) return (EPERM); securelevel = level; return (0); case KERN_HOSTNAME: - error = sysctl_string(oldp, oldlenp, newp, newlen, + error = sysctl_trstring(oldp, oldlenp, newp, newlen, hostname, sizeof(hostname)); if (newp && !error) hostnamelen = newlen; @@ -385,8 +700,15 @@ kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) case KERN_CLOCKRATE: return (sysctl_clockrate(oldp, oldlenp)); case KERN_BOOTTIME: - return (sysctl_rdstruct(oldp, oldlenp, newp, &boottime, + { + struct timeval t; + + t.tv_sec = boottime_sec(); + t.tv_usec = 0; + + return (sysctl_rdstruct(oldp, oldlenp, newp, &t, sizeof(struct timeval))); + } case KERN_VNODE: return (sysctl_vnode(oldp, oldlenp)); case KERN_PROC: @@ -410,121 +732,237 @@ kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) #else return (sysctl_rdint(oldp, oldlenp, newp, 0)); #endif -#if FIXME /* [ */ - case KERN_MAXPARTITIONS: - return (sysctl_rdint(oldp, oldlenp, newp, MAXPARTITIONS)); -#endif /* FIXME ] */ case KERN_KDEBUG: return (kdebug_ops(name + 1, namelen - 1, oldp, oldlenp, p)); case KERN_PCSAMPLES: return (pcsamples_ops(name + 1, namelen - 1, oldp, oldlenp, p)); case KERN_PROCARGS: /* new one as it does not use kinfo_proc */ - return (sysctl_procargs(name + 1, namelen - 1, oldp, oldlenp)); - case KERN_SYMFILE: - { - char *str; - error = get_kernel_symfile( p, &str ); - if ( error ) return error; - return (sysctl_rdstring(oldp, oldlenp, newp, str)); - } - default: - return (EOPNOTSUPP); + return (sysctl_procargs(name + 1, namelen - 1, oldp, oldlenp, p)); + case KERN_PROCARGS2: + /* new one as it does not use kinfo_proc */ + return (sysctl_procargs2(name + 1, namelen - 1, oldp, oldlenp, p)); + case KERN_SYMFILE: + error = get_kernel_symfile( p, &str ); + if ( error ) + return error; + return (sysctl_rdstring(oldp, oldlenp, newp, str)); +#if NFSCLIENT + case KERN_NETBOOT: + return (sysctl_rdint(oldp, oldlenp, newp, netboot_root())); +#endif + case KERN_PANICINFO: + return(sysctl_dopanicinfo(name + 1, namelen - 1, oldp, oldlenp, + newp, newlen, p)); + case KERN_AFFINITY: + return sysctl_affinity(name+1, namelen-1, oldp, oldlenp, + newp, newlen, p); + case KERN_TRANSLATE: + return sysctl_translate(name+1, namelen-1, oldp, oldlenp, newp, + newlen, p); + case KERN_CLASSICHANDLER: + return sysctl_exec_archhandler_ppc(name+1, namelen-1, oldp, + oldlenp, newp, newlen, p); + case KERN_AIOMAX: + return( sysctl_aiomax( oldp, oldlenp, newp, newlen ) ); + case KERN_AIOPROCMAX: + return( sysctl_aioprocmax( oldp, oldlenp, newp, newlen ) ); + case KERN_AIOTHREADS: + return( sysctl_aiothreads( oldp, oldlenp, newp, newlen ) ); + case KERN_USRSTACK: + return (sysctl_rdint(oldp, oldlenp, newp, (uintptr_t)p->user_stack)); + case KERN_USRSTACK64: + return (sysctl_rdquad(oldp, oldlenp, newp, p->user_stack)); + case KERN_COREFILE: + error = sysctl_string(oldp, oldlenp, newp, newlen, + corefilename, sizeof(corefilename)); + return (error); + case KERN_COREDUMP: + tmp = do_coredump; + error = sysctl_int(oldp, oldlenp, newp, newlen, &do_coredump); + if (!error && ((do_coredump < 0) || (do_coredump > 1))) { + do_coredump = tmp; + error = EINVAL; + } + return (error); + case KERN_SUGID_COREDUMP: + tmp = sugid_coredump; + error = sysctl_int(oldp, oldlenp, newp, newlen, &sugid_coredump); + if (!error && ((sugid_coredump < 0) || (sugid_coredump > 1))) { + sugid_coredump = tmp; + error = EINVAL; + } + return (error); + case KERN_PROCDELAYTERM: + { + int old_value, new_value; + + error = 0; + if (oldp && *oldlenp < sizeof(int)) + return (ENOMEM); + if ( newp && newlen != sizeof(int) ) + return(EINVAL); + *oldlenp = sizeof(int); + old_value = (p->p_lflag & P_LDELAYTERM)? 1: 0; + if (oldp && (error = copyout( &old_value, oldp, sizeof(int)))) + return(error); + if (error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(int) ); + if (error == 0 && newp) { + if (new_value) + p->p_lflag |= P_LDELAYTERM; + else + p->p_lflag &= ~P_LDELAYTERM; + } + return(error); } - /* NOTREACHED */ -} + case KERN_PROC_LOW_PRI_IO: + { + int old_value, new_value; -/* - * hardware related system variables. - */ -hw_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) - int *name; - u_int namelen; - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; - struct proc *p; -{ - char dummy[65]; - int epochTemp; - extern int vm_page_wire_count; -#if __ppc__ - ml_ppc_cpu_info_t cpu_info; + error = 0; + if (oldp && *oldlenp < sizeof(int)) + return (ENOMEM); + if ( newp && newlen != sizeof(int) ) + return(EINVAL); + *oldlenp = sizeof(int); + + old_value = (p->p_lflag & P_LLOW_PRI_IO)? 0x01: 0; + if (p->p_lflag & P_LBACKGROUND_IO) + old_value |= 0x02; + + if (oldp && (error = copyout( &old_value, oldp, sizeof(int)))) + return(error); + if (error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(int) ); + if (error == 0 && newp) { + if (new_value & 0x01) + p->p_lflag |= P_LLOW_PRI_IO; + else if (new_value & 0x02) + p->p_lflag |= P_LBACKGROUND_IO; + else if (new_value == 0) + p->p_lflag &= ~(P_LLOW_PRI_IO | P_LBACKGROUND_IO); + } + return(error); + } + case KERN_LOW_PRI_WINDOW: + { + int old_value, new_value; - ml_ppc_get_info(&cpu_info); -#endif + error = 0; + if (oldp && *oldlenp < sizeof(old_value) ) + return (ENOMEM); + if ( newp && newlen != sizeof(new_value) ) + return(EINVAL); + *oldlenp = sizeof(old_value); - /* all sysctl names at this level are terminal */ - if (namelen != 1) - return (ENOTDIR); /* overloaded */ + old_value = lowpri_IO_window_msecs; - switch (name[0]) { - case HW_MACHINE: - if(!PEGetMachineName(dummy,64)) + if (oldp && (error = copyout( &old_value, oldp, *oldlenp))) + return(error); + if (error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(newlen) ); + if (error == 0 && newp) { + lowpri_IO_window_msecs = new_value; + } + return(error); + } + case KERN_LOW_PRI_DELAY: + { + int old_value, new_value; + + error = 0; + if (oldp && *oldlenp < sizeof(old_value) ) + return (ENOMEM); + if ( newp && newlen != sizeof(new_value) ) return(EINVAL); - return (sysctl_rdstring(oldp, oldlenp, newp, dummy)); - case HW_MODEL: - if(!PEGetModelName(dummy,64)) + *oldlenp = sizeof(old_value); + + old_value = lowpri_IO_delay_msecs; + + if (oldp && (error = copyout( &old_value, oldp, *oldlenp))) + return(error); + if (error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(newlen) ); + if (error == 0 && newp) { + lowpri_IO_delay_msecs = new_value; + } + return(error); + } + case KERN_NX_PROTECTION: + { + int old_value, new_value; + + error = 0; + if (oldp && *oldlenp < sizeof(old_value) ) + return (ENOMEM); + if ( newp && newlen != sizeof(new_value) ) return(EINVAL); - return (sysctl_rdstring(oldp, oldlenp, newp, dummy)); - case HW_NCPU: { - int numcpus=1; - host_basic_info_data_t hinfo; - kern_return_t kret; - int count= HOST_BASIC_INFO_COUNT; -#define BSD_HOST 1 - - kret = host_info(BSD_HOST, HOST_BASIC_INFO, &hinfo, &count); - if (kret == KERN_SUCCESS) { - numcpus = hinfo.avail_cpus; - return (sysctl_rdint(oldp, oldlenp, newp, numcpus)); - } else { - return(EINVAL); - } + *oldlenp = sizeof(old_value); + + old_value = nx_enabled; + + if (oldp && (error = copyout( &old_value, oldp, *oldlenp))) + return(error); +#ifdef __i386__ + /* + * Only allow setting if NX is supported on the chip + */ + if (cpuid_extfeatures() & CPUID_EXTFEATURE_XD) { +#endif + if (error == 0 && newp) + error = copyin(newp, &new_value, + sizeof(newlen)); + if (error == 0 && newp) + nx_enabled = new_value; +#ifdef __i386__ + } else if (newp) { + error = ENOTSUP; } - case HW_BYTEORDER: - return (sysctl_rdint(oldp, oldlenp, newp, BYTE_ORDER)); - case HW_PHYSMEM: - return (sysctl_rdint(oldp, oldlenp, newp, mem_size)); - case HW_USERMEM: - return (sysctl_rdint(oldp, oldlenp, newp, - (mem_size - vm_page_wire_count * page_size))); - case HW_PAGESIZE: - return (sysctl_rdint(oldp, oldlenp, newp, page_size)); - case HW_EPOCH: - epochTemp = PEGetPlatformEpoch(); - if (epochTemp == -1) return(EINVAL); - return (sysctl_rdint(oldp, oldlenp, newp, epochTemp)); - case HW_BUS_FREQ: - return (sysctl_rdint(oldp, oldlenp, newp, gPEClockFrequencyInfo.bus_clock_rate_hz)); - case HW_CPU_FREQ: - return (sysctl_rdint(oldp, oldlenp, newp, gPEClockFrequencyInfo.cpu_clock_rate_hz)); -#if __ppc__ - case HW_VECTORUNIT: - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.vector_unit)); - case HW_CACHELINE: - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.cache_line_size)); - case HW_L1ICACHESIZE: - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.l1_icache_size)); - case HW_L1DCACHESIZE: - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.l1_dcache_size)); - case HW_L2SETTINGS: - if (cpu_info.l2_cache_size == 0xFFFFFFFF) return(EINVAL); - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.l2_settings)); - case HW_L2CACHESIZE: - if (cpu_info.l2_cache_size == 0xFFFFFFFF) return(EINVAL); - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.l2_cache_size)); - case HW_L3SETTINGS: - if (cpu_info.l3_cache_size == 0xFFFFFFFF) return(EINVAL); - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.l3_settings)); - case HW_L3CACHESIZE: - if (cpu_info.l3_cache_size == 0xFFFFFFFF) return(EINVAL); - return (sysctl_rdint(oldp, oldlenp, newp, cpu_info.l3_cache_size)); #endif + return(error); + } + case KERN_SHREG_PRIVATIZABLE: + /* this kernel does implement shared_region_make_private_np() */ + return (sysctl_rdint(oldp, oldlenp, newp, 1)); + case KERN_PROCNAME: + error = sysctl_trstring(oldp, oldlenp, newp, newlen, + &p->p_name[0], (2*MAXCOMLEN+1)); + return (error); + case KERN_THALTSTACK: + { + int old_value, new_value; + + error = 0; + if (oldp && *oldlenp < sizeof(int)) + return (ENOMEM); + if ( newp && newlen != sizeof(int) ) + return(EINVAL); + *oldlenp = sizeof(int); + old_value = (p->p_lflag & P_LTHSIGSTACK)? 1: 0; + if (oldp && (error = copyout( &old_value, oldp, sizeof(int)))) + return(error); + if (error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(int) ); + if (error == 0 && newp) { + if (new_value) { + /* we cannot swich midstream if inuse */ + if ((p->p_sigacts->ps_flags & SAS_ALTSTACK) == SAS_ALTSTACK) + return(EPERM); + p->p_lflag |= P_LTHSIGSTACK; + } else { + /* we cannot swich midstream */ + if ((p->p_lflag & P_LTHSIGSTACK) == P_LTHSIGSTACK) + return(EPERM); + p->p_lflag &= ~P_LTHSIGSTACK; + } + } + return(error); + } default: - return (EOPNOTSUPP); + return (ENOTSUP); } + /* NOTREACHED */ } #ifdef DEBUG @@ -546,14 +984,8 @@ static struct ctldebug *debugvars[CTL_DEBUG_MAXID] = { &debug15, &debug16, &debug17, &debug18, &debug19, }; int -debug_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) - int *name; - u_int namelen; - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; - struct proc *p; +debug_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, struct proc *p) { struct ctldebug *cdp; @@ -562,14 +994,14 @@ debug_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) return (ENOTDIR); /* overloaded */ cdp = debugvars[name[0]]; if (cdp->debugname == 0) - return (EOPNOTSUPP); + return (ENOTSUP); switch (name[1]) { case CTL_DEBUG_NAME: return (sysctl_rdstring(oldp, oldlenp, newp, cdp->debugname)); case CTL_DEBUG_VALUE: return (sysctl_int(oldp, oldlenp, newp, newlen, cdp->debugvar)); default: - return (EOPNOTSUPP); + return (ENOTSUP); } /* NOTREACHED */ } @@ -579,15 +1011,14 @@ debug_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) * Validate parameters and get old / set new parameters * for an integer-valued sysctl function. */ -sysctl_int(oldp, oldlenp, newp, newlen, valp) - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; - int *valp; +int +sysctl_int(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, int *valp) { int error = 0; + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); if (oldp && *oldlenp < sizeof(int)) return (ENOMEM); if (newp && newlen != sizeof(int)) @@ -595,22 +1026,23 @@ sysctl_int(oldp, oldlenp, newp, newlen, valp) *oldlenp = sizeof(int); if (oldp) error = copyout(valp, oldp, sizeof(int)); - if (error == 0 && newp) + if (error == 0 && newp) { error = copyin(newp, valp, sizeof(int)); + AUDIT_ARG(value, *valp); + } return (error); } /* * As above, but read-only. */ -sysctl_rdint(oldp, oldlenp, newp, val) - void *oldp; - size_t *oldlenp; - void *newp; - int val; +int +sysctl_rdint(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, int val) { int error = 0; + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); if (oldp && *oldlenp < sizeof(int)) return (ENOMEM); if (newp) @@ -623,30 +1055,120 @@ sysctl_rdint(oldp, oldlenp, newp, val) /* * Validate parameters and get old / set new parameters - * for a string-valued sysctl function. + * for an quad(64bit)-valued sysctl function. */ -sysctl_string(oldp, oldlenp, newp, newlen, str, maxlen) +int +sysctl_quad(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, quad_t *valp) +{ + int error = 0; + + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); + if (oldp && *oldlenp < sizeof(quad_t)) + return (ENOMEM); + if (newp && newlen != sizeof(quad_t)) + return (EINVAL); + *oldlenp = sizeof(quad_t); + if (oldp) + error = copyout(valp, oldp, sizeof(quad_t)); + if (error == 0 && newp) + error = copyin(newp, valp, sizeof(quad_t)); + return (error); +} + +/* + * As above, but read-only. + */ +int +sysctl_rdquad(oldp, oldlenp, newp, val) void *oldp; size_t *oldlenp; void *newp; - size_t newlen; - char *str; - int maxlen; + quad_t val; +{ + int error = 0; + + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); + if (oldp && *oldlenp < sizeof(quad_t)) + return (ENOMEM); + if (newp) + return (EPERM); + *oldlenp = sizeof(quad_t); + if (oldp) + error = copyout((caddr_t)&val, CAST_USER_ADDR_T(oldp), sizeof(quad_t)); + return (error); +} + +/* + * Validate parameters and get old / set new parameters + * for a string-valued sysctl function. Unlike sysctl_string, if you + * give it a too small (but larger than 0 bytes) buffer, instead of + * returning ENOMEM, it truncates the returned string to the buffer + * size. This preserves the semantics of some library routines + * implemented via sysctl, which truncate their returned data, rather + * than simply returning an error. The returned string is always NUL + * terminated. + */ +int +sysctl_trstring(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, char *str, int maxlen) +{ + int len, copylen, error = 0; + + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); + copylen = len = strlen(str) + 1; + if (oldp && (len < 0 || *oldlenp < 1)) + return (ENOMEM); + if (oldp && (*oldlenp < (size_t)len)) + copylen = *oldlenp + 1; + if (newp && (maxlen < 0 || newlen >= (size_t)maxlen)) + return (EINVAL); + *oldlenp = copylen - 1; /* deal with NULL strings correctly */ + if (oldp) { + error = copyout(str, oldp, copylen); + if (!error) { + unsigned char c = 0; + /* NUL terminate */ + oldp += *oldlenp; + error = copyout((void *)&c, oldp, sizeof(char)); + } + } + if (error == 0 && newp) { + error = copyin(newp, str, newlen); + str[newlen] = 0; + AUDIT_ARG(text, (char *)str); + } + return (error); +} + +/* + * Validate parameters and get old / set new parameters + * for a string-valued sysctl function. + */ +int +sysctl_string(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, char *str, int maxlen) { int len, error = 0; + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); len = strlen(str) + 1; - if (oldp && *oldlenp < len) + if (oldp && (len < 0 || *oldlenp < (size_t)len)) return (ENOMEM); - if (newp && newlen >= maxlen) + if (newp && (maxlen < 0 || newlen >= (size_t)maxlen)) return (EINVAL); + *oldlenp = len -1; /* deal with NULL strings correctly */ if (oldp) { - *oldlenp = len; error = copyout(str, oldp, len); } if (error == 0 && newp) { error = copyin(newp, str, newlen); str[newlen] = 0; + AUDIT_ARG(text, (char *)str); } return (error); } @@ -654,16 +1176,16 @@ sysctl_string(oldp, oldlenp, newp, newlen, str, maxlen) /* * As above, but read-only. */ -sysctl_rdstring(oldp, oldlenp, newp, str) - void *oldp; - size_t *oldlenp; - void *newp; - char *str; +int +sysctl_rdstring(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, char *str) { int len, error = 0; + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); len = strlen(str) + 1; - if (oldp && *oldlenp < len) + if (oldp && *oldlenp < (size_t)len) return (ENOMEM); if (newp) return (EPERM); @@ -677,19 +1199,17 @@ sysctl_rdstring(oldp, oldlenp, newp, str) * Validate parameters and get old / set new parameters * for a structure oriented sysctl function. */ -sysctl_struct(oldp, oldlenp, newp, newlen, sp, len) - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; - void *sp; - int len; +int +sysctl_struct(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen, void *sp, int len) { int error = 0; - if (oldp && *oldlenp < len) + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); + if (oldp && (len < 0 || *oldlenp < (size_t)len)) return (ENOMEM); - if (newp && newlen > len) + if (newp && (len < 0 || newlen > (size_t)len)) return (EINVAL); if (oldp) { *oldlenp = len; @@ -704,15 +1224,15 @@ sysctl_struct(oldp, oldlenp, newp, newlen, sp, len) * Validate parameters and get old parameters * for a structure oriented sysctl function. */ -sysctl_rdstruct(oldp, oldlenp, newp, sp, len) - void *oldp; - size_t *oldlenp; - void *newp, *sp; - int len; +int +sysctl_rdstruct(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, void *sp, int len) { int error = 0; - if (oldp && *oldlenp < len) + if (oldp != USER_ADDR_NULL && oldlenp == NULL) + return (EFAULT); + if (oldp && (len < 0 || *oldlenp < (size_t)len)) return (ENOMEM); if (newp) return (EPERM); @@ -725,31 +1245,32 @@ sysctl_rdstruct(oldp, oldlenp, newp, sp, len) /* * Get file structures. */ -sysctl_file(where, sizep) - char *where; - size_t *sizep; +int +sysctl_file(user_addr_t where, size_t *sizep) { int buflen, error; - struct file *fp; - char *start = where; + struct fileglob *fg; + user_addr_t start = where; + struct extern_file nef; buflen = *sizep; - if (where == NULL) { + if (where == USER_ADDR_NULL) { /* * overestimate by 10 files */ - *sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file); + *sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct extern_file); return (0); } /* * first copyout filehead */ - if (buflen < sizeof(filehead)) { + if (buflen < 0 || (size_t)buflen < sizeof(filehead)) { *sizep = 0; return (0); } - if (error = copyout((caddr_t)&filehead, where, sizeof(filehead))) + error = copyout((caddr_t)&filehead, where, sizeof(filehead)); + if (error) return (error); buflen -= sizeof(filehead); where += sizeof(filehead); @@ -757,17 +1278,28 @@ sysctl_file(where, sizep) /* * followed by an array of file structures */ - for (fp = filehead.lh_first; fp != 0; fp = fp->f_list.le_next) { - if (buflen < sizeof(struct file)) { + for (fg = filehead.lh_first; fg != 0; fg = fg->f_list.le_next) { + if (buflen < 0 || (size_t)buflen < sizeof(struct extern_file)) { *sizep = where - start; return (ENOMEM); } - if (error = copyout((caddr_t)fp, where, sizeof (struct file))) + nef.f_list.le_next = (struct extern_file *)fg->f_list.le_next; + nef.f_list.le_prev = (struct extern_file **)fg->f_list.le_prev; + nef.f_flag = (fg->fg_flag & FMASK); + nef.f_type = fg->fg_type; + nef.f_count = fg->fg_count; + nef.f_msgcount = fg->fg_msgcount; + nef.f_cred = fg->fg_cred; + nef.f_ops = fg->fg_ops; + nef.f_offset = fg->fg_offset; + nef.f_data = fg->fg_data; + error = copyout((caddr_t)&nef, where, sizeof (struct extern_file)); + if (error) return (error); - buflen -= sizeof(struct file); - where += sizeof(struct file); + buflen -= sizeof(struct extern_file); + where += sizeof(struct extern_file); } - *sizep = where - start; + *sizep = where - start; return (0); } @@ -776,24 +1308,34 @@ sysctl_file(where, sizep) */ #define KERN_PROCSLOP (5 * sizeof (struct kinfo_proc)) -sysctl_doproc(name, namelen, where, sizep) - int *name; - u_int namelen; - char *where; - size_t *sizep; +int +sysctl_doproc(int *name, u_int namelen, user_addr_t where, size_t *sizep) { - register struct proc *p; - register struct kinfo_proc *dp = (struct kinfo_proc *)where; - register int needed = 0; - int buflen = where != NULL ? *sizep : 0; + struct proc *p; + user_addr_t dp = where; + size_t needed = 0; + int buflen = where != USER_ADDR_NULL ? *sizep : 0; int doingzomb; - struct kinfo_proc kproc; int error = 0; + boolean_t is_64_bit = FALSE; + struct kinfo_proc kproc; + struct user_kinfo_proc user_kproc; + int sizeof_kproc; + caddr_t kprocp; if (namelen != 2 && !(namelen == 1 && name[0] == KERN_PROC_ALL)) return (EINVAL); p = allproc.lh_first; doingzomb = 0; + is_64_bit = proc_is64bit(current_proc()); + if (is_64_bit) { + sizeof_kproc = sizeof(user_kproc); + kprocp = (caddr_t) &user_kproc; + } + else { + sizeof_kproc = sizeof(kproc); + kprocp = (caddr_t) &kproc; + } again: for (; p != 0; p = p->p_list.le_next) { /* @@ -820,39 +1362,48 @@ again: break; case KERN_PROC_TTY: - if ( doingzomb || (p->p_flag & P_CONTROLT) == 0 || + if ((p->p_flag & P_CONTROLT) == 0 || + (p->p_session == NULL) || p->p_session->s_ttyp == NULL || p->p_session->s_ttyp->t_dev != (dev_t)name[1]) continue; break; case KERN_PROC_UID: - if (doingzomb || (p->p_ucred->cr_uid != (uid_t)name[1])) + if ((p->p_ucred == NULL) || + (kauth_cred_getuid(p->p_ucred) != (uid_t)name[1])) continue; break; case KERN_PROC_RUID: - if ( doingzomb || (p->p_cred->p_ruid != (uid_t)name[1])) + if ((p->p_ucred == NULL) || + (p->p_ucred->cr_ruid != (uid_t)name[1])) continue; break; } - if (buflen >= sizeof(struct kinfo_proc)) { - fill_proc(p, &kproc, doingzomb); - if (error = copyout((caddr_t)&kproc, &dp->kp_proc, - sizeof(struct kinfo_proc))) + if (buflen >= sizeof_kproc) { + bzero(kprocp, sizeof_kproc); + if (is_64_bit) { + fill_user_proc(p, (struct user_kinfo_proc *) kprocp); + } + else { + fill_proc(p, (struct kinfo_proc *) kprocp); + } + error = copyout(kprocp, dp, sizeof_kproc); + if (error) return (error); - dp++; - buflen -= sizeof(struct kinfo_proc); + dp += sizeof_kproc; + buflen -= sizeof_kproc; } - needed += sizeof(struct kinfo_proc); + needed += sizeof_kproc; } if (doingzomb == 0) { p = zombproc.lh_first; doingzomb++; goto again; } - if (where != NULL) { - *sizep = (caddr_t)dp - where; + if (where != USER_ADDR_NULL) { + *sizep = dp - where; if (needed > *sizep) return (ENOMEM); } else { @@ -862,20 +1413,10 @@ again: return (0); } -void -fill_proc(p,kp, doingzomb) - register struct proc *p; - register struct kinfo_proc *kp; - int doingzomb; -{ - fill_externproc(p, &kp->kp_proc); - if (!doingzomb) - fill_eproc(p, &kp->kp_eproc); -} /* * Fill in an eproc structure for the specified process. */ -void +static void fill_eproc(p, ep) register struct proc *p; register struct eproc *ep; @@ -883,41 +1424,117 @@ fill_eproc(p, ep) register struct tty *tp; ep->e_paddr = p; - ep->e_sess = p->p_pgrp->pg_session; - ep->e_pcred = *p->p_cred; - ep->e_ucred = *p->p_ucred; + if (p->p_pgrp) { + ep->e_sess = p->p_pgrp->pg_session; + ep->e_pgid = p->p_pgrp->pg_id; + ep->e_jobc = p->p_pgrp->pg_jobc; + if (ep->e_sess && ep->e_sess->s_ttyvp) + ep->e_flag = EPROC_CTTY; + } else { + ep->e_sess = (struct session *)0; + ep->e_pgid = 0; + ep->e_jobc = 0; + } + ep->e_ppid = (p->p_pptr) ? p->p_pptr->p_pid : 0; + /* Pre-zero the fake historical pcred */ + bzero(&ep->e_pcred, sizeof(struct _pcred)); + if (p->p_ucred) { + /* XXX not ref-counted */ + + /* A fake historical pcred */ + ep->e_pcred.p_ruid = p->p_ucred->cr_ruid; + ep->e_pcred.p_svuid = p->p_ucred->cr_svuid; + ep->e_pcred.p_rgid = p->p_ucred->cr_rgid; + ep->e_pcred.p_svgid = p->p_ucred->cr_svgid; + + /* A fake historical *kauth_cred_t */ + ep->e_ucred.cr_ref = p->p_ucred->cr_ref; + ep->e_ucred.cr_uid = kauth_cred_getuid(p->p_ucred); + ep->e_ucred.cr_ngroups = p->p_ucred->cr_ngroups; + bcopy(p->p_ucred->cr_groups, ep->e_ucred.cr_groups, NGROUPS*sizeof(gid_t)); + + } if (p->p_stat == SIDL || p->p_stat == SZOMB) { - ep->e_vm.vm_rssize = 0; ep->e_vm.vm_tsize = 0; ep->e_vm.vm_dsize = 0; ep->e_vm.vm_ssize = 0; - /* ep->e_vm.vm_pmap = XXX; */ - } else { -#if FIXME /* [ */ - register vm_map_t vm = ((task_t)p->task)->map; - - ep->e_vm.vm_rssize = pmap_resident_count(vm->pmap); /*XXX*/ -// ep->e_vm.vm_tsize = vm->vm_tsize; -// ep->e_vm.vm_dsize = vm->vm_dsize; -// ep->e_vm.vm_ssize = vm->vm_ssize; -#else /* FIXME ][ */ - ep->e_vm.vm_rssize = 0; /*XXX*/ -#endif /* FIXME ] */ - } - if (p->p_pptr) - ep->e_ppid = p->p_pptr->p_pid; - else - ep->e_ppid = 0; - ep->e_pgid = p->p_pgrp->pg_id; - ep->e_jobc = p->p_pgrp->pg_jobc; - if ((p->p_flag & P_CONTROLT) && + } + ep->e_vm.vm_rssize = 0; + + if ((p->p_flag & P_CONTROLT) && (ep->e_sess) && (tp = ep->e_sess->s_ttyp)) { ep->e_tdev = 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 = NODEV; - ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0; + + if (SESS_LEADER(p)) + ep->e_flag |= EPROC_SLEADER; + if (p->p_wmesg) + strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN); + ep->e_xsize = ep->e_xrssize = 0; + ep->e_xccount = ep->e_xswrss = 0; +} + +/* + * Fill in an LP64 version of eproc structure for the specified process. + */ +static void +fill_user_eproc(register struct proc *p, register struct user_eproc *ep) +{ + register struct tty *tp; + struct session *sessionp = NULL; + + ep->e_paddr = CAST_USER_ADDR_T(p); + if (p->p_pgrp) { + sessionp = p->p_pgrp->pg_session; + ep->e_sess = CAST_USER_ADDR_T(sessionp); + ep->e_pgid = p->p_pgrp->pg_id; + ep->e_jobc = p->p_pgrp->pg_jobc; + if (sessionp) { + if (sessionp->s_ttyvp) + ep->e_flag = EPROC_CTTY; + } + } else { + ep->e_sess = USER_ADDR_NULL; + ep->e_pgid = 0; + ep->e_jobc = 0; + } + ep->e_ppid = (p->p_pptr) ? p->p_pptr->p_pid : 0; + /* Pre-zero the fake historical pcred */ + bzero(&ep->e_pcred, sizeof(ep->e_pcred)); + if (p->p_ucred) { + /* XXX not ref-counted */ + + /* A fake historical pcred */ + ep->e_pcred.p_ruid = p->p_ucred->cr_ruid; + ep->e_pcred.p_svuid = p->p_ucred->cr_svuid; + ep->e_pcred.p_rgid = p->p_ucred->cr_rgid; + ep->e_pcred.p_svgid = p->p_ucred->cr_svgid; + + /* A fake historical *kauth_cred_t */ + ep->e_ucred.cr_ref = p->p_ucred->cr_ref; + ep->e_ucred.cr_uid = kauth_cred_getuid(p->p_ucred); + ep->e_ucred.cr_ngroups = p->p_ucred->cr_ngroups; + bcopy(p->p_ucred->cr_groups, ep->e_ucred.cr_groups, NGROUPS*sizeof(gid_t)); + + } + if (p->p_stat == SIDL || p->p_stat == SZOMB) { + ep->e_vm.vm_tsize = 0; + ep->e_vm.vm_dsize = 0; + ep->e_vm.vm_ssize = 0; + } + ep->e_vm.vm_rssize = 0; + + if ((p->p_flag & P_CONTROLT) && (sessionp) && + (tp = sessionp->s_ttyp)) { + ep->e_tdev = tp->t_dev; + ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; + ep->e_tsess = CAST_USER_ADDR_T(tp->t_session); + } else + ep->e_tdev = NODEV; + if (SESS_LEADER(p)) ep->e_flag |= EPROC_SLEADER; if (p->p_wmesg) @@ -925,15 +1542,18 @@ fill_eproc(p, ep) ep->e_xsize = ep->e_xrssize = 0; ep->e_xccount = ep->e_xswrss = 0; } + /* * Fill in an eproc structure for the specified process. */ -void +static void fill_externproc(p, exp) register struct proc *p; register struct extern_proc *exp; { exp->p_forw = exp->p_back = NULL; + if (p->p_stats) + exp->p_starttime = p->p_stats->p_start; exp->p_vmspace = NULL; exp->p_sigacts = p->p_sigacts; exp->p_flag = p->p_flag; @@ -942,7 +1562,7 @@ fill_externproc(p, exp) exp->p_oppid = p->p_oppid ; exp->p_dupfd = p->p_dupfd ; /* Mach related */ - exp->user_stack = p->user_stack ; + exp->user_stack = CAST_DOWN(caddr_t, p->user_stack); exp->exit_thread = p->exit_thread ; exp->p_debugger = p->p_debugger ; exp->sigwait = p->sigwait ; @@ -961,10 +1581,10 @@ fill_externproc(p, exp) exp->p_iticks = p->p_iticks ; exp->p_traceflag = p->p_traceflag ; exp->p_tracep = p->p_tracep ; - exp->p_siglist = p->p_siglist ; + exp->p_siglist = 0 ; /* No longer relevant */ exp->p_textvp = p->p_textvp ; exp->p_holdcnt = 0 ; - exp->p_sigmask = p->p_sigmask ; + exp->p_sigmask = 0 ; /* no longer avaialable */ exp->p_sigignore = p->p_sigignore ; exp->p_sigcatch = p->p_sigcatch ; exp->p_priority = p->p_priority ; @@ -976,22 +1596,94 @@ fill_externproc(p, exp) exp->p_addr = NULL; exp->p_xstat = p->p_xstat ; exp->p_acflag = p->p_acflag ; - exp->p_ru = p->p_ru ; + exp->p_ru = p->p_ru ; /* XXX may be NULL */ } -kdebug_ops(name, namelen, where, sizep, p) -int *name; -u_int namelen; -char *where; -size_t *sizep; -struct proc *p; +/* + * Fill in an LP64 version of extern_proc structure for the specified process. + */ +static void +fill_user_externproc(register struct proc *p, register struct user_extern_proc *exp) { -int size=*sizep; -int ret=0; -extern int kdbg_control(int *name, u_int namelen, char * where,size_t * sizep); + exp->p_forw = exp->p_back = USER_ADDR_NULL; + if (p->p_stats) { + exp->p_starttime.tv_sec = p->p_stats->p_start.tv_sec; + exp->p_starttime.tv_usec = p->p_stats->p_start.tv_usec; + } + exp->p_vmspace = USER_ADDR_NULL; + exp->p_sigacts = CAST_USER_ADDR_T(p->p_sigacts); + exp->p_flag = p->p_flag; + exp->p_stat = p->p_stat ; + exp->p_pid = p->p_pid ; + exp->p_oppid = p->p_oppid ; + exp->p_dupfd = p->p_dupfd ; + /* Mach related */ + exp->user_stack = p->user_stack; + exp->exit_thread = CAST_USER_ADDR_T(p->exit_thread); + exp->p_debugger = p->p_debugger ; + exp->sigwait = p->sigwait ; + /* scheduling */ + exp->p_estcpu = p->p_estcpu ; + exp->p_cpticks = p->p_cpticks ; + exp->p_pctcpu = p->p_pctcpu ; + exp->p_wchan = CAST_USER_ADDR_T(p->p_wchan); + exp->p_wmesg = CAST_USER_ADDR_T(p->p_wmesg); + exp->p_swtime = p->p_swtime ; + exp->p_slptime = p->p_slptime ; + exp->p_realtimer.it_interval.tv_sec = p->p_realtimer.it_interval.tv_sec; + exp->p_realtimer.it_interval.tv_usec = p->p_realtimer.it_interval.tv_usec; + exp->p_realtimer.it_value.tv_sec = p->p_realtimer.it_value.tv_sec; + exp->p_realtimer.it_value.tv_usec = p->p_realtimer.it_value.tv_usec; + exp->p_rtime.tv_sec = p->p_rtime.tv_sec; + exp->p_rtime.tv_usec = p->p_rtime.tv_usec; + exp->p_uticks = p->p_uticks ; + exp->p_sticks = p->p_sticks ; + exp->p_iticks = p->p_iticks ; + exp->p_traceflag = p->p_traceflag ; + exp->p_tracep = CAST_USER_ADDR_T(p->p_tracep); + exp->p_siglist = 0 ; /* No longer relevant */ + exp->p_textvp = CAST_USER_ADDR_T(p->p_textvp); + exp->p_holdcnt = 0 ; + exp->p_sigmask = 0 ; /* no longer avaialable */ + exp->p_sigignore = p->p_sigignore ; + exp->p_sigcatch = p->p_sigcatch ; + exp->p_priority = p->p_priority ; + exp->p_usrpri = p->p_usrpri ; + exp->p_nice = p->p_nice ; + bcopy(&p->p_comm, &exp->p_comm,MAXCOMLEN); + exp->p_comm[MAXCOMLEN] = '\0'; + exp->p_pgrp = CAST_USER_ADDR_T(p->p_pgrp); + exp->p_addr = USER_ADDR_NULL; + exp->p_xstat = p->p_xstat ; + exp->p_acflag = p->p_acflag ; + exp->p_ru = CAST_USER_ADDR_T(p->p_ru); /* XXX may be NULL */ +} - if (ret = suser(p->p_ucred, &p->p_acflag)) - return(ret); +static void +fill_proc(p, kp) + register struct proc *p; + register struct kinfo_proc *kp; +{ + fill_externproc(p, &kp->kp_proc); + fill_eproc(p, &kp->kp_eproc); +} + +static void +fill_user_proc(register struct proc *p, register struct user_kinfo_proc *kp) +{ + fill_user_externproc(p, &kp->kp_proc); + fill_user_eproc(p, &kp->kp_eproc); +} + +int +kdebug_ops(int *name, u_int namelen, user_addr_t where, + size_t *sizep, struct proc *p) +{ + int ret=0; + + ret = suser(kauth_cred_get(), &p->p_acflag); + if (ret) + return(ret); switch(name[0]) { case KERN_KDEFLAGS: @@ -1008,27 +1700,28 @@ extern int kdbg_control(int *name, u_int namelen, char * where,size_t * sizep); case KERN_KDPIDEX: case KERN_KDSETRTCDEC: case KERN_KDSETBUF: + case KERN_KDGETENTROPY: ret = kdbg_control(name, namelen, where, sizep); break; default: - ret= EOPNOTSUPP; + ret= ENOTSUP; break; } return(ret); } -pcsamples_ops(name, namelen, where, sizep, p) -int *name; -u_int namelen; -char *where; -size_t *sizep; -struct proc *p; +extern int pcsamples_control(int *name, u_int namelen, user_addr_t where, + size_t * sizep); + +int +pcsamples_ops(int *name, u_int namelen, user_addr_t where, + size_t *sizep, struct proc *p) { -int ret=0; -extern int pcsamples_control(int *name, u_int namelen, char * where,size_t * sizep); + int ret=0; - if (ret = suser(p->p_ucred, &p->p_acflag)) - return(ret); + ret = suser(kauth_cred_get(), &p->p_acflag); + if (ret) + return(ret); switch(name[0]) { case KERN_PCDISABLE: @@ -1042,44 +1735,56 @@ extern int pcsamples_control(int *name, u_int namelen, char * where,size_t * siz ret = pcsamples_control(name, namelen, where, sizep); break; default: - ret= EOPNOTSUPP; + ret= ENOTSUP; break; } return(ret); } /* - * Returns the top N bytes of the user stack, with - * everything below the first argument character - * zeroed for security reasons. - * Odd data structure is for compatibility. + * Return the top *sizep bytes of the user stack, or the entire area of the + * user stack down through the saved exec_path, whichever is smaller. */ -sysctl_procargs(name, namelen, where, sizep) - int *name; - u_int namelen; - char *where; - size_t *sizep; +int +sysctl_procargs(int *name, u_int namelen, user_addr_t where, + size_t *sizep, struct proc *cur_proc) { - register struct proc *p; - register int needed = 0; - int buflen = where != NULL ? *sizep : 0; + return sysctl_procargsx( name, namelen, where, sizep, cur_proc, 0); +} + +static int +sysctl_procargs2(int *name, u_int namelen, user_addr_t where, + size_t *sizep, struct proc *cur_proc) +{ + return sysctl_procargsx( name, namelen, where, sizep, cur_proc, 1); +} + +static int +sysctl_procargsx(int *name, __unused u_int namelen, user_addr_t where, + size_t *sizep, struct proc *cur_proc, int argc_yes) +{ + struct proc *p; + int buflen = where != USER_ADDR_NULL ? *sizep : 0; int error = 0; struct vm_map *proc_map; struct task * task; vm_map_copy_t tmp; - vm_offset_t arg_addr; - vm_size_t arg_size; + user_addr_t arg_addr; + size_t arg_size; caddr_t data; - unsigned size; + int size; vm_offset_t copy_start, copy_end; - vm_offset_t dealloc_start; /* area to remove from kernel map */ - vm_offset_t dealloc_end; - int *ip; kern_return_t ret; int pid; + if (argc_yes) + buflen -= sizeof(int); /* reserve first word to return argc */ - if ((buflen <= 0) || (buflen > (PAGE_SIZE << 1))) { + /* we only care about buflen when where (oldp from sysctl) is not NULL. */ + /* when where (oldp from sysctl) is NULL and sizep (oldlenp from sysctl */ + /* is not NULL then the caller wants us to return the length needed to */ + /* hold the data we would return */ + if (where != USER_ADDR_NULL && (buflen <= 0 || buflen > ARG_MAX)) { return(EINVAL); } arg_size = buflen; @@ -1088,7 +1793,6 @@ sysctl_procargs(name, namelen, where, sizep) * Lookup process by pid */ pid = name[0]; - p = pfind(pid); if (p == NULL) { return(EINVAL); @@ -1107,7 +1811,35 @@ sysctl_procargs(name, namelen, where, sizep) if (!p->user_stack) return(EINVAL); - arg_addr = (vm_offset_t)(p->user_stack - arg_size); + if (where == USER_ADDR_NULL) { + /* caller only wants to know length of proc args data */ + if (sizep == NULL) + return(EFAULT); + + size = p->p_argslen; + if (argc_yes) { + size += sizeof(int); + } + else { + /* + * old PROCARGS will return the executable's path and plus some + * extra space for work alignment and data tags + */ + size += PATH_MAX + (6 * sizeof(int)); + } + size += (size & (sizeof(int) - 1)) ? (sizeof(int) - (size & (sizeof(int) - 1))) : 0; + *sizep = size; + return (0); + } + + if ((kauth_cred_getuid(p->p_ucred) != kauth_cred_getuid(kauth_cred_get())) + && suser(kauth_cred_get(), &cur_proc->p_acflag)) + return (EINVAL); + + if ((u_int)arg_size > p->p_argslen) + arg_size = round_page(p->p_argslen); + + arg_addr = p->user_stack - arg_size; /* @@ -1119,20 +1851,31 @@ sysctl_procargs(name, namelen, where, sizep) if (task == NULL) return(EINVAL); + /* + * Once we have a task reference we can convert that into a + * map reference, which we will use in the calls below. The + * task/process may change its map after we take this reference + * (see execve), but the worst that will happen then is a return + * of stale info (which is always a possibility). + */ task_reference(task); + proc_map = get_task_map_reference(task); + task_deallocate(task); + if (proc_map == NULL) + return(EINVAL); + ret = kmem_alloc(kernel_map, ©_start, round_page(arg_size)); if (ret != KERN_SUCCESS) { - task_deallocate(task); + vm_map_deallocate(proc_map); return(ENOMEM); } - proc_map = get_task_map(task); copy_end = round_page(copy_start + arg_size); - if( vm_map_copyin(proc_map, trunc_page(arg_addr), round_page(arg_size), - FALSE, &tmp) != KERN_SUCCESS) { - task_deallocate(task); + if( vm_map_copyin(proc_map, (vm_map_address_t)arg_addr, + (vm_map_size_t)arg_size, FALSE, &tmp) != KERN_SUCCESS) { + vm_map_deallocate(proc_map); kmem_free(kernel_map, copy_start, round_page(arg_size)); return (EIO); @@ -1142,72 +1885,413 @@ sysctl_procargs(name, namelen, where, sizep) * Now that we've done the copyin from the process' * map, we can release the reference to it. */ - task_deallocate(task); + vm_map_deallocate(proc_map); - if( vm_map_copy_overwrite(kernel_map, copy_start, - tmp, FALSE) != KERN_SUCCESS) { + if( vm_map_copy_overwrite(kernel_map, + (vm_map_address_t)copy_start, + tmp, FALSE) != KERN_SUCCESS) { kmem_free(kernel_map, copy_start, round_page(arg_size)); return (EIO); } - data = (caddr_t) (copy_end - arg_size); - ip = (int *) copy_end; - size = arg_size; - - /* - * Now look down the stack for the bottom of the - * argument list. Since this call is otherwise - * unprotected, we can't let the nosy user see - * anything else on the stack. - * - * The arguments are pushed on the stack by - * execve() as: - * - * .long 0 - * arg 0 (null-terminated) - * arg 1 - * ... - * arg N - * .long 0 - * - */ - - ip -= 2; /*skip trailing 0 word and assume at least one - argument. The last word of argN may be just - the trailing 0, in which case we'd stop - there */ - while (*--ip) - if (ip == (int *)data) - break; - /* - * To account for saved path name and not having a null after that - * Run the sweep again. If we have already sweeped entire range skip this - */ - if (ip != (int *)data) { - while (*--ip) - if (ip == (int *)data) - break; - } - - bzero(data, (unsigned) ((int)ip - (int)data)); - - dealloc_start = copy_start; - dealloc_end = copy_end; + if (arg_size > p->p_argslen) { + data = (caddr_t) (copy_end - p->p_argslen); + size = p->p_argslen; + } else { + data = (caddr_t) (copy_end - arg_size); + size = arg_size; + } + if (argc_yes) { + /* Put processes argc as the first word in the copyout buffer */ + suword(where, p->p_argc); + error = copyout(data, (where + sizeof(int)), size); + size += sizeof(int); + } else { + error = copyout(data, where, size); - size = MIN(size, buflen); - error = copyout(data, where, size); + /* + * Make the old PROCARGS work to return the executable's path + * But, only if there is enough space in the provided buffer + * + * on entry: data [possibily] points to the beginning of the path + * + * Note: we keep all pointers&sizes aligned to word boundries + */ + if ( (! error) && (buflen > 0 && (u_int)buflen > p->p_argslen) ) + { + int binPath_sz, alignedBinPath_sz = 0; + int extraSpaceNeeded, addThis; + user_addr_t placeHere; + char * str = (char *) data; + int max_len = size; + + /* Some apps are really bad about messing up their stacks + So, we have to be extra careful about getting the length + of the executing binary. If we encounter an error, we bail. + */ + + /* Limit ourselves to PATH_MAX paths */ + if ( max_len > PATH_MAX ) max_len = PATH_MAX; + + binPath_sz = 0; + + while ( (binPath_sz < max_len-1) && (*str++ != 0) ) + binPath_sz++; + + /* If we have a NUL terminator, copy it, too */ + if (binPath_sz < max_len-1) binPath_sz += 1; + + /* Pre-Flight the space requiremnts */ + + /* Account for the padding that fills out binPath to the next word */ + alignedBinPath_sz += (binPath_sz & (sizeof(int)-1)) ? (sizeof(int)-(binPath_sz & (sizeof(int)-1))) : 0; + + placeHere = where + size; + + /* Account for the bytes needed to keep placeHere word aligned */ + addThis = (placeHere & (sizeof(int)-1)) ? (sizeof(int)-(placeHere & (sizeof(int)-1))) : 0; + + /* Add up all the space that is needed */ + extraSpaceNeeded = alignedBinPath_sz + addThis + binPath_sz + (4 * sizeof(int)); + + /* is there is room to tack on argv[0]? */ + if ( (buflen & ~(sizeof(int)-1)) >= ( p->p_argslen + extraSpaceNeeded )) + { + placeHere += addThis; + suword(placeHere, 0); + placeHere += sizeof(int); + suword(placeHere, 0xBFFF0000); + placeHere += sizeof(int); + suword(placeHere, 0); + placeHere += sizeof(int); + error = copyout(data, placeHere, binPath_sz); + if ( ! error ) + { + placeHere += binPath_sz; + suword(placeHere, 0); + size += extraSpaceNeeded; + } + } + } + } - if (dealloc_start != (vm_offset_t) 0) { - kmem_free(kernel_map, dealloc_start, - dealloc_end - dealloc_start); + if (copy_start != (vm_offset_t) 0) { + kmem_free(kernel_map, copy_start, copy_end - copy_start); } if (error) { return(error); } - if (where != NULL) + if (where != USER_ADDR_NULL) *sizep = size; return (0); } + + +/* + * Validate parameters and get old / set new parameters + * for max number of concurrent aio requests. Makes sure + * the system wide limit is greater than the per process + * limit. + */ +static int +sysctl_aiomax(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, size_t newlen) +{ + int error = 0; + int new_value; + + if ( oldp && *oldlenp < sizeof(int) ) + return (ENOMEM); + if ( newp && newlen != sizeof(int) ) + return (EINVAL); + + *oldlenp = sizeof(int); + if ( oldp ) + error = copyout( &aio_max_requests, oldp, sizeof(int) ); + if ( error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(int) ); + if ( error == 0 && newp ) { + if ( new_value >= aio_max_requests_per_process ) + aio_max_requests = new_value; + else + error = EINVAL; + } + return( error ); + +} /* sysctl_aiomax */ + + +/* + * Validate parameters and get old / set new parameters + * for max number of concurrent aio requests per process. + * Makes sure per process limit is less than the system wide + * limit. + */ +static int +sysctl_aioprocmax(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, size_t newlen ) +{ + int error = 0; + int new_value = 0; + + if ( oldp && *oldlenp < sizeof(int) ) + return (ENOMEM); + if ( newp && newlen != sizeof(int) ) + return (EINVAL); + + *oldlenp = sizeof(int); + if ( oldp ) + error = copyout( &aio_max_requests_per_process, oldp, sizeof(int) ); + if ( error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(int) ); + if ( error == 0 && newp ) { + if ( new_value <= aio_max_requests && new_value >= AIO_LISTIO_MAX ) + aio_max_requests_per_process = new_value; + else + error = EINVAL; + } + return( error ); + +} /* sysctl_aioprocmax */ + + +/* + * Validate parameters and get old / set new parameters + * for max number of async IO worker threads. + * We only allow an increase in the number of worker threads. + */ +static int +sysctl_aiothreads(user_addr_t oldp, size_t *oldlenp, user_addr_t newp, size_t newlen) +{ + int error = 0; + int new_value; + + if ( oldp && *oldlenp < sizeof(int) ) + return (ENOMEM); + if ( newp && newlen != sizeof(int) ) + return (EINVAL); + + *oldlenp = sizeof(int); + if ( oldp ) + error = copyout( &aio_worker_threads, oldp, sizeof(int) ); + if ( error == 0 && newp ) + error = copyin( newp, &new_value, sizeof(int) ); + if ( error == 0 && newp ) { + if (new_value > aio_worker_threads ) { + _aio_create_worker_threads( (new_value - aio_worker_threads) ); + aio_worker_threads = new_value; + } + else + error = EINVAL; + } + return( error ); + +} /* sysctl_aiothreads */ + + +/* + * Validate parameters and get old / set new parameters + * for max number of processes per UID. + * Makes sure per UID limit is less than the system wide limit. + */ +static int +sysctl_maxprocperuid(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen) +{ + int error = 0; + int new_value; + + if ( oldp != USER_ADDR_NULL && *oldlenp < sizeof(int) ) + return (ENOMEM); + if ( newp != USER_ADDR_NULL && newlen != sizeof(int) ) + return (EINVAL); + + *oldlenp = sizeof(int); + if ( oldp != USER_ADDR_NULL ) + error = copyout( &maxprocperuid, oldp, sizeof(int) ); + if ( error == 0 && newp != USER_ADDR_NULL ) { + error = copyin( newp, &new_value, sizeof(int) ); + if ( error == 0 ) { + AUDIT_ARG(value, new_value); + if ( new_value <= maxproc && new_value > 0 ) + maxprocperuid = new_value; + else + error = EINVAL; + } + else + error = EINVAL; + } + return( error ); + +} /* sysctl_maxprocperuid */ + + +/* + * Validate parameters and get old / set new parameters + * for max number of files per process. + * Makes sure per process limit is less than the system-wide limit. + */ +static int +sysctl_maxfilesperproc(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen) +{ + int error = 0; + int new_value; + + if ( oldp != USER_ADDR_NULL && *oldlenp < sizeof(int) ) + return (ENOMEM); + if ( newp != USER_ADDR_NULL && newlen != sizeof(int) ) + return (EINVAL); + + *oldlenp = sizeof(int); + if ( oldp != USER_ADDR_NULL ) + error = copyout( &maxfilesperproc, oldp, sizeof(int) ); + if ( error == 0 && newp != USER_ADDR_NULL ) { + error = copyin( newp, &new_value, sizeof(int) ); + if ( error == 0 ) { + AUDIT_ARG(value, new_value); + if ( new_value < maxfiles && new_value > 0 ) + maxfilesperproc = new_value; + else + error = EINVAL; + } + else + error = EINVAL; + } + return( error ); + +} /* sysctl_maxfilesperproc */ + + +/* + * Validate parameters and get old / set new parameters + * for the system-wide limit on the max number of processes. + * Makes sure the system-wide limit is less than the configured hard + * limit set at kernel compilation. + */ +static int +sysctl_maxproc(user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen ) +{ + int error = 0; + int new_value; + + if ( oldp != USER_ADDR_NULL && *oldlenp < sizeof(int) ) + return (ENOMEM); + if ( newp != USER_ADDR_NULL && newlen != sizeof(int) ) + return (EINVAL); + + *oldlenp = sizeof(int); + if ( oldp != USER_ADDR_NULL ) + error = copyout( &maxproc, oldp, sizeof(int) ); + if ( error == 0 && newp != USER_ADDR_NULL ) { + error = copyin( newp, &new_value, sizeof(int) ); + if ( error == 0 ) { + AUDIT_ARG(value, new_value); + if ( new_value <= hard_maxproc && new_value > 0 ) + maxproc = new_value; + else + error = EINVAL; + } + else + error = EINVAL; + } + return( error ); + +} /* sysctl_maxproc */ + +#if __i386__ +static int +sysctl_sysctl_exec_affinity SYSCTL_HANDLER_ARGS +{ + struct proc *cur_proc = req->p; + int error; + + if (req->oldptr != USER_ADDR_NULL) { + cpu_type_t oldcputype = (cur_proc->p_flag & P_AFFINITY) ? CPU_TYPE_POWERPC : CPU_TYPE_I386; + if ((error = SYSCTL_OUT(req, &oldcputype, sizeof(oldcputype)))) + return error; + } + + if (req->newptr != USER_ADDR_NULL) { + cpu_type_t newcputype; + if ((error = SYSCTL_IN(req, &newcputype, sizeof(newcputype)))) + return error; + if (newcputype == CPU_TYPE_I386) + cur_proc->p_flag &= ~P_AFFINITY; + else if (newcputype == CPU_TYPE_POWERPC) + cur_proc->p_flag |= P_AFFINITY; + else + return (EINVAL); + } + + return 0; +} +SYSCTL_PROC(_sysctl, OID_AUTO, proc_exec_affinity, CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0, sysctl_sysctl_exec_affinity ,"I","proc_exec_affinity"); +#endif + +static int +fetch_process_cputype( + struct proc *cur_proc, + int *name, + u_int namelen, + cpu_type_t *cputype) +{ + struct proc *p = NULL; + cpu_type_t ret = 0; + + if (namelen == 0) + p = cur_proc; + else if (namelen == 1) { + p = pfind(name[0]); + if (p == NULL) + return (EINVAL); + if ((kauth_cred_getuid(p->p_ucred) != kauth_cred_getuid(kauth_cred_get())) + && suser(kauth_cred_get(), &cur_proc->p_acflag)) + return (EPERM); + } else { + return EINVAL; + } + +#if __i386__ + if (p->p_flag & P_TRANSLATED) { + ret = CPU_TYPE_POWERPC; + } + else +#endif + { + ret = cpu_type(); + if (IS_64BIT_PROCESS(p)) + ret |= CPU_ARCH_ABI64; + } + *cputype = ret; + + return 0; +} + +static int +sysctl_sysctl_native SYSCTL_HANDLER_ARGS +{ + int error; + cpu_type_t proc_cputype = 0; + if ((error = fetch_process_cputype(req->p, (int *)arg1, arg2, &proc_cputype)) != 0) + return error; + int res = 1; + if ((proc_cputype & ~CPU_ARCH_MASK) != (cpu_type() & ~CPU_ARCH_MASK)) + res = 0; + return SYSCTL_OUT(req, &res, sizeof(res)); +} +SYSCTL_PROC(_sysctl, OID_AUTO, proc_native, CTLTYPE_NODE|CTLFLAG_RD, 0, 0, sysctl_sysctl_native ,"I","proc_native"); + +static int +sysctl_sysctl_cputype SYSCTL_HANDLER_ARGS +{ + int error; + cpu_type_t proc_cputype = 0; + if ((error = fetch_process_cputype(req->p, (int *)arg1, arg2, &proc_cputype)) != 0) + return error; + return SYSCTL_OUT(req, &proc_cputype, sizeof(proc_cputype)); +} +SYSCTL_PROC(_sysctl, OID_AUTO, proc_cputype, CTLTYPE_NODE|CTLFLAG_RD, 0, 0, sysctl_sysctl_cputype ,"I","proc_cputype"); +