/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
- *
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* 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. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ * 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
* Please see the License for the specific language governing rights and
* limitations under the License.
*
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Mach Operating System
* All rights reserved. The CMU software License Agreement specifies
* the terms and conditions for use and redistribution.
*/
-
/*
+ * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce
+ * support for mandatory and extensible security protections. This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
*/
-
#include <meta_features.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/debug.h>
#include <kern/lock.h>
+#include <mach/mach_traps.h>
+#include <mach/port.h>
+#include <mach/task.h>
+#include <mach/task_access.h>
+#include <mach/task_special_ports.h>
#include <mach/time_value.h>
+#include <mach/vm_map.h>
#include <mach/vm_param.h>
#include <mach/vm_prot.h>
-#include <mach/port.h>
+#include <sys/file_internal.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dir.h>
#include <sys/namei.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
+#include <sys/kauth.h>
#include <sys/vm.h>
#include <sys/file.h>
-#include <sys/vnode.h>
-#include <sys/buf.h>
+#include <sys/vnode_internal.h>
#include <sys/mount.h>
#include <sys/trace.h>
#include <sys/kernel.h>
-#include <sys/ubc.h>
+#include <sys/ubc_internal.h>
+#include <sys/user.h>
+#include <sys/syslog.h>
#include <sys/stat.h>
+#include <sys/sysproto.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+
+#include <security/audit/audit.h>
+#include <bsm/audit_kevents.h>
#include <kern/kalloc.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
+#include <vm/vm_pageout.h>
#include <machine/spl.h>
-#include <mach/shared_memory_server.h>
-#include <vm/vm_shared_memory_server.h>
+#include <mach/shared_region.h>
+#include <vm/vm_shared_region.h>
+#include <vm/vm_protos.h>
-extern zone_t lsf_zone;
+/*
+ * Sysctl's related to data/stack execution. See osfmk/vm/vm_map.c
+ */
+
+#ifndef SECURE_KERNEL
+extern int allow_stack_exec, allow_data_exec;
+
+SYSCTL_INT(_vm, OID_AUTO, allow_stack_exec, CTLFLAG_RW, &allow_stack_exec, 0, "");
+SYSCTL_INT(_vm, OID_AUTO, allow_data_exec, CTLFLAG_RW, &allow_data_exec, 0, "");
+#endif /* !SECURE_KERNEL */
+
+static const char *prot_values[] = {
+ "none",
+ "read-only",
+ "write-only",
+ "read-write",
+ "execute-only",
+ "read-execute",
+ "write-execute",
+ "read-write-execute"
+};
-useracc(addr, len, prot)
- caddr_t addr;
- u_int len;
- int prot;
+void
+log_stack_execution_failure(addr64_t vaddr, vm_prot_t prot)
+{
+ printf("Data/Stack execution not permitted: %s[pid %d] at virtual address 0x%qx, protections were %s\n",
+ current_proc()->p_comm, current_proc()->p_pid, vaddr, prot_values[prot & VM_PROT_ALL]);
+}
+
+int shared_region_unnest_logging = 1;
+
+SYSCTL_INT(_vm, OID_AUTO, shared_region_unnest_logging, CTLFLAG_RW,
+ &shared_region_unnest_logging, 0, "");
+
+int vm_shared_region_unnest_log_interval = 10;
+int shared_region_unnest_log_count_threshold = 5;
+
+/* These log rate throttling state variables aren't thread safe, but
+ * are sufficient unto the task.
+ */
+static int64_t last_unnest_log_time = 0;
+static int shared_region_unnest_log_count = 0;
+
+void log_unnest_badness(vm_map_t m, vm_map_offset_t s, vm_map_offset_t e) {
+ struct timeval tv;
+ const char *pcommstr;
+
+ if (shared_region_unnest_logging == 0)
+ return;
+
+ if (shared_region_unnest_logging == 1) {
+ microtime(&tv);
+ if ((tv.tv_sec - last_unnest_log_time) < vm_shared_region_unnest_log_interval) {
+ if (shared_region_unnest_log_count++ > shared_region_unnest_log_count_threshold)
+ return;
+ }
+ else {
+ last_unnest_log_time = tv.tv_sec;
+ shared_region_unnest_log_count = 0;
+ }
+ }
+
+ pcommstr = current_proc()->p_comm;
+
+ printf("%s (map: %p) triggered DYLD shared region unnest for map: %p, region 0x%qx->0x%qx. While not abnormal for debuggers, this increases system memory footprint until the target exits.\n", current_proc()->p_comm, get_task_map(current_proc()->task), m, (uint64_t)s, (uint64_t)e);
+}
+
+int
+useracc(
+ user_addr_t addr,
+ user_size_t len,
+ int prot)
{
return (vm_map_check_protection(
current_map(),
- trunc_page_32((unsigned int)addr), round_page_32((unsigned int)(addr+len)),
+ vm_map_trunc_page(addr), vm_map_round_page(addr+len),
prot == B_READ ? VM_PROT_READ : VM_PROT_WRITE));
}
-vslock(addr, len)
- caddr_t addr;
- int len;
+int
+vslock(
+ user_addr_t addr,
+ user_size_t len)
{
-kern_return_t kret;
- kret = vm_map_wire(current_map(), trunc_page_32((unsigned int)addr),
- round_page_32((unsigned int)(addr+len)),
+ kern_return_t kret;
+ kret = vm_map_wire(current_map(), vm_map_trunc_page(addr),
+ vm_map_round_page(addr+len),
VM_PROT_READ | VM_PROT_WRITE ,FALSE);
switch (kret) {
}
}
-vsunlock(addr, len, dirtied)
- caddr_t addr;
- int len;
- int dirtied;
+int
+vsunlock(
+ user_addr_t addr,
+ user_size_t len,
+ __unused int dirtied)
{
- pmap_t pmap;
#if FIXME /* [ */
+ pmap_t pmap;
vm_page_t pg;
+ vm_map_offset_t vaddr;
+ ppnum_t paddr;
#endif /* FIXME ] */
- vm_offset_t vaddr, paddr;
kern_return_t kret;
#if FIXME /* [ */
if (dirtied) {
pmap = get_task_pmap(current_task());
- for (vaddr = trunc_page((unsigned int)(addr)); vaddr < round_page((unsigned int)(addr+len));
+ for (vaddr = vm_map_trunc_page(addr);
+ vaddr < vm_map_round_page(addr+len);
vaddr += PAGE_SIZE) {
paddr = pmap_extract(pmap, vaddr);
pg = PHYS_TO_VM_PAGE(paddr);
#ifdef lint
dirtied++;
#endif /* lint */
- kret = vm_map_unwire(current_map(), trunc_page_32((unsigned int)(addr)),
- round_page_32((unsigned int)(addr+len)), FALSE);
+ kret = vm_map_unwire(current_map(), vm_map_trunc_page(addr),
+ vm_map_round_page(addr+len), FALSE);
switch (kret) {
case KERN_SUCCESS:
return (0);
}
}
-#if defined(sun) || BALANCE || defined(m88k)
-#else /*defined(sun) || BALANCE || defined(m88k)*/
-subyte(addr, byte)
- void * addr;
- int byte;
+int
+subyte(
+ user_addr_t addr,
+ int byte)
{
char character;
return (copyout((void *)&(character), addr, sizeof(char)) == 0 ? 0 : -1);
}
-suibyte(addr, byte)
- void * addr;
- int byte;
+int
+suibyte(
+ user_addr_t addr,
+ int byte)
{
char character;
character = (char)byte;
- return (copyout((void *) &(character), addr, sizeof(char)) == 0 ? 0 : -1);
+ return (copyout((void *)&(character), addr, sizeof(char)) == 0 ? 0 : -1);
}
-int fubyte(addr)
- void * addr;
+int fubyte(user_addr_t addr)
{
unsigned char byte;
return(byte);
}
-int fuibyte(addr)
- void * addr;
+int fuibyte(user_addr_t addr)
{
unsigned char byte;
return(byte);
}
-suword(addr, word)
- void * addr;
- long word;
+int
+suword(
+ user_addr_t addr,
+ long word)
{
return (copyout((void *) &word, addr, sizeof(int)) == 0 ? 0 : -1);
}
-long fuword(addr)
- void * addr;
+long fuword(user_addr_t addr)
{
- long word;
+ long word = 0;
if (copyin(addr, (void *) &word, sizeof(int)))
return(-1);
/* suiword and fuiword are the same as suword and fuword, respectively */
-suiword(addr, word)
- void * addr;
- long word;
+int
+suiword(
+ user_addr_t addr,
+ long word)
{
return (copyout((void *) &word, addr, sizeof(int)) == 0 ? 0 : -1);
}
-long fuiword(addr)
- void * addr;
+long fuiword(user_addr_t addr)
{
- long word;
+ long word = 0;
if (copyin(addr, (void *) &word, sizeof(int)))
return(-1);
return(word);
}
-#endif /* defined(sun) || BALANCE || defined(m88k) || defined(i386) */
+
+/*
+ * With a 32-bit kernel and mixed 32/64-bit user tasks, this interface allows the
+ * fetching and setting of process-sized size_t and pointer values.
+ */
+int
+sulong(user_addr_t addr, int64_t word)
+{
+
+ if (IS_64BIT_PROCESS(current_proc())) {
+ return(copyout((void *)&word, addr, sizeof(word)) == 0 ? 0 : -1);
+ } else {
+ return(suiword(addr, (long)word));
+ }
+}
+
+int64_t
+fulong(user_addr_t addr)
+{
+ int64_t longword;
+
+ if (IS_64BIT_PROCESS(current_proc())) {
+ if (copyin(addr, (void *)&longword, sizeof(longword)) != 0)
+ return(-1);
+ return(longword);
+ } else {
+ return((int64_t)fuiword(addr));
+ }
+}
int
-swapon()
+suulong(user_addr_t addr, uint64_t uword)
{
- return(EOPNOTSUPP);
+
+ if (IS_64BIT_PROCESS(current_proc())) {
+ return(copyout((void *)&uword, addr, sizeof(uword)) == 0 ? 0 : -1);
+ } else {
+ return(suiword(addr, (uint32_t)uword));
+ }
+}
+
+uint64_t
+fuulong(user_addr_t addr)
+{
+ uint64_t ulongword;
+
+ if (IS_64BIT_PROCESS(current_proc())) {
+ if (copyin(addr, (void *)&ulongword, sizeof(ulongword)) != 0)
+ return(-1ULL);
+ return(ulongword);
+ } else {
+ return((uint64_t)fuiword(addr));
+ }
}
+int
+swapon(__unused proc_t procp, __unused struct swapon_args *uap, __unused int *retval)
+{
+ return(ENOTSUP);
+}
+/*
+ * pid_for_task
+ *
+ * Find the BSD process ID for the Mach task associated with the given Mach port
+ * name
+ *
+ * Parameters: args User argument descriptor (see below)
+ *
+ * Indirect parameters: args->t Mach port name
+ * args->pid Process ID (returned value; see below)
+ *
+ * Returns: KERL_SUCCESS Success
+ * KERN_FAILURE Not success
+ *
+ * Implicit returns: args->pid Process ID
+ *
+ */
kern_return_t
-pid_for_task(t, x)
- mach_port_t t;
- int *x;
+pid_for_task(
+ struct pid_for_task_args *args)
{
- struct proc * p;
+ mach_port_name_t t = args->t;
+ user_addr_t pid_addr = args->pid;
+ proc_t p;
task_t t1;
- extern task_t port_name_to_task(mach_port_t t);
int pid = -1;
kern_return_t err = KERN_SUCCESS;
- boolean_t funnel_state;
- funnel_state = thread_funnel_set(kernel_flock, TRUE);
+ AUDIT_MACH_SYSCALL_ENTER(AUE_PIDFORTASK);
+ AUDIT_ARG(mach_port1, t);
+
t1 = port_name_to_task(t);
if (t1 == TASK_NULL) {
} else {
p = get_bsdtask_info(t1);
if (p) {
- pid = p->p_pid;
+ pid = proc_pid(p);
err = KERN_SUCCESS;
} else {
err = KERN_FAILURE;
}
task_deallocate(t1);
pftout:
- (void) copyout((char *) &pid, (char *) x, sizeof(*x));
- thread_funnel_set(kernel_flock, funnel_state);
+ AUDIT_ARG(pid, pid);
+ (void) copyout((char *) &pid, pid_addr, sizeof(int));
+ AUDIT_MACH_SYSCALL_EXIT(err);
return(err);
}
+/*
+ *
+ * tfp_policy = KERN_TFP_POLICY_DENY; Deny Mode: None allowed except for self
+ * tfp_policy = KERN_TFP_POLICY_DEFAULT; default mode: all posix checks and upcall via task port for authentication
+ *
+ */
+static int tfp_policy = KERN_TFP_POLICY_DEFAULT;
+
/*
- * Routine: task_for_pid
+ * Routine: task_for_pid_posix_check
* Purpose:
- * Get the task port for another "process", named by its
- * process ID on the same host as "target_task".
+ * Verify that the current process should be allowed to
+ * get the target process's task port. This is only
+ * permitted if:
+ * - The current process is root
+ * OR all of the following are true:
+ * - The target process's real, effective, and saved uids
+ * are the same as the current proc's euid,
+ * - The target process's group set is a subset of the
+ * calling process's group set, and
+ * - The target process hasn't switched credentials.
*
- * Only permitted to privileged processes, or processes
- * with the same user ID.
+ * Returns: TRUE: permitted
+ * FALSE: denied
*/
-kern_return_t
-task_for_pid(target_tport, pid, t)
- mach_port_t target_tport;
- int pid;
- mach_port_t *t;
+static int
+task_for_pid_posix_check(proc_t target)
{
- struct proc *p;
- struct proc *p1;
- task_t t1;
- mach_port_t tret;
- extern task_t port_name_to_task(mach_port_t tp);
- void * sright;
- int error = 0;
- boolean_t funnel_state;
+ kauth_cred_t targetcred, mycred;
+ uid_t myuid;
+ int allowed;
- t1 = port_name_to_task(target_tport);
- if (t1 == TASK_NULL) {
- (void ) copyout((char *)&t1, (char *)t, sizeof(mach_port_t));
- return(KERN_FAILURE);
- }
-
- funnel_state = thread_funnel_set(kernel_flock, TRUE);
-
- restart:
- p1 = get_bsdtask_info(t1);
- if (
- ((p = pfind(pid)) != (struct proc *) 0)
- && (p1 != (struct proc *) 0)
- && (((p->p_ucred->cr_uid == p1->p_ucred->cr_uid) &&
- ((p->p_cred->p_ruid == p1->p_cred->p_ruid)))
- || !(suser(p1->p_ucred, &p1->p_acflag)))
- && (p->p_stat != SZOMB)
- ) {
- if (p->task != TASK_NULL) {
- if (!task_reference_try(p->task)) {
- mutex_pause(); /* temp loss of funnel */
- goto restart;
- }
- sright = (void *)convert_task_to_port(p->task);
- tret = (void *)
- ipc_port_copyout_send(sright,
- get_task_ipcspace(current_task()));
- } else
- tret = MACH_PORT_NULL;
- (void ) copyout((char *)&tret, (char *) t, sizeof(mach_port_t));
- task_deallocate(t1);
- error = KERN_SUCCESS;
- goto tfpout;
+ /* No task_for_pid on bad targets */
+ if (target == PROC_NULL || target->p_stat == SZOMB) {
+ return FALSE;
}
- task_deallocate(t1);
- tret = MACH_PORT_NULL;
- (void) copyout((char *) &tret, (char *) t, sizeof(mach_port_t));
- error = KERN_FAILURE;
-tfpout:
- thread_funnel_set(kernel_flock, funnel_state);
- return(error);
-}
+ mycred = kauth_cred_get();
+ myuid = kauth_cred_getuid(mycred);
-struct load_shared_file_args {
- char *filename;
- caddr_t mfa;
- u_long mfs;
- caddr_t *ba;
- int map_cnt;
- sf_mapping_t *mappings;
- int *flags;
-};
+ /* If we're running as root, the check passes */
+ if (kauth_cred_issuser(mycred))
+ return TRUE;
-int ws_disabled = 1;
+ /* We're allowed to get our own task port */
+ if (target == current_proc())
+ return TRUE;
-int
-load_shared_file(
- struct proc *p,
- struct load_shared_file_args *uap,
- register *retval)
-{
- caddr_t mapped_file_addr=uap->mfa;
- u_long mapped_file_size=uap->mfs;
- caddr_t *base_address=uap->ba;
- int map_cnt=uap->map_cnt;
- sf_mapping_t *mappings=uap->mappings;
- char *filename=uap->filename;
- int *flags=uap->flags;
- struct vnode *vp = 0;
- struct nameidata nd, *ndp;
- char *filename_str;
- register int error;
- kern_return_t kr;
-
- struct vattr vattr;
- memory_object_control_t file_control;
- sf_mapping_t *map_list;
- caddr_t local_base;
- int local_flags;
- int caller_flags;
- int i;
- int default_regions = 0;
- vm_size_t dummy;
- kern_return_t kret;
-
- shared_region_mapping_t shared_region;
- struct shared_region_task_mappings task_mapping_info;
- shared_region_mapping_t next;
-
- ndp = &nd;
-
-
- /* Retrieve the base address */
- if (error = copyin(base_address, &local_base, sizeof (caddr_t))) {
- goto lsf_bailout;
- }
- if (error = copyin(flags, &local_flags, sizeof (int))) {
- goto lsf_bailout;
- }
-
- if(local_flags & QUERY_IS_SYSTEM_REGION) {
- shared_region_mapping_t default_shared_region;
- vm_get_shared_region(current_task(), &shared_region);
- task_mapping_info.self = (vm_offset_t)shared_region;
-
- shared_region_mapping_info(shared_region,
- &(task_mapping_info.text_region),
- &(task_mapping_info.text_size),
- &(task_mapping_info.data_region),
- &(task_mapping_info.data_size),
- &(task_mapping_info.region_mappings),
- &(task_mapping_info.client_base),
- &(task_mapping_info.alternate_base),
- &(task_mapping_info.alternate_next),
- &(task_mapping_info.fs_base),
- &(task_mapping_info.system),
- &(task_mapping_info.flags), &next);
-
- default_shared_region =
- lookup_default_shared_region(
- ENV_DEFAULT_ROOT,
- task_mapping_info.system);
- if (shared_region == default_shared_region) {
- local_flags = SYSTEM_REGION_BACKED;
- } else {
- local_flags = 0;
- }
- shared_region_mapping_dealloc(default_shared_region);
- error = 0;
- error = copyout(&local_flags, flags, sizeof (int));
- goto lsf_bailout;
+ /*
+ * Under DENY, only root can get another proc's task port,
+ * so no more checks are needed.
+ */
+ if (tfp_policy == KERN_TFP_POLICY_DENY) {
+ return FALSE;
}
- caller_flags = local_flags;
- kret = kmem_alloc(kernel_map, (vm_offset_t *)&filename_str,
- (vm_size_t)(MAXPATHLEN));
- if (kret != KERN_SUCCESS) {
- error = ENOMEM;
- goto lsf_bailout;
- }
- kret = kmem_alloc(kernel_map, (vm_offset_t *)&map_list,
- (vm_size_t)(map_cnt*sizeof(sf_mapping_t)));
- if (kret != KERN_SUCCESS) {
- kmem_free(kernel_map, (vm_offset_t)filename_str,
- (vm_size_t)(MAXPATHLEN));
- error = ENOMEM;
- goto lsf_bailout;
- }
- if (error =
- copyin(mappings, map_list, (map_cnt*sizeof(sf_mapping_t)))) {
- goto lsf_bailout_free;
- }
+ targetcred = kauth_cred_proc_ref(target);
+ allowed = TRUE;
- if (error = copyinstr(filename,
- filename_str, MAXPATHLEN, (size_t *)&dummy)) {
- goto lsf_bailout_free;
+ /* Do target's ruid, euid, and saved uid match my euid? */
+ if ((kauth_cred_getuid(targetcred) != myuid) ||
+ (targetcred->cr_ruid != myuid) ||
+ (targetcred->cr_svuid != myuid)) {
+ allowed = FALSE;
+ goto out;
}
- /*
- * Get a vnode for the target file
- */
- NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
- filename_str, p);
-
- if ((error = namei(ndp))) {
- goto lsf_bailout_free;
+ /* Are target's groups a subset of my groups? */
+ if (kauth_cred_gid_subset(targetcred, mycred, &allowed) ||
+ allowed == 0) {
+ allowed = FALSE;
+ goto out;
}
- vp = ndp->ni_vp;
-
- if (vp->v_type != VREG) {
- error = EINVAL;
- goto lsf_bailout_free_vput;
+ /* Has target switched credentials? */
+ if (target->p_flag & P_SUGID) {
+ allowed = FALSE;
+ goto out;
}
+
+out:
+ kauth_cred_unref(&targetcred);
+ return allowed;
+}
- UBCINFOCHECK("load_shared_file", vp);
+/*
+ * Routine: task_for_pid
+ * Purpose:
+ * Get the task port for another "process", named by its
+ * process ID on the same host as "target_task".
+ *
+ * Only permitted to privileged processes, or processes
+ * with the same user ID.
+ *
+ * Note: if pid == 0, an error is return no matter who is calling.
+ *
+ * XXX This should be a BSD system call, not a Mach trap!!!
+ */
+kern_return_t
+task_for_pid(
+ struct task_for_pid_args *args)
+{
+ mach_port_name_t target_tport = args->target_tport;
+ int pid = args->pid;
+ user_addr_t task_addr = args->t;
+ proc_t p = PROC_NULL;
+ task_t t1 = TASK_NULL;
+ mach_port_name_t tret = MACH_PORT_NULL;
+ ipc_port_t tfpport;
+ void * sright;
+ int error = 0;
+
+ AUDIT_MACH_SYSCALL_ENTER(AUE_TASKFORPID);
+ AUDIT_ARG(pid, pid);
+ AUDIT_ARG(mach_port1, target_tport);
- if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) {
- goto lsf_bailout_free_vput;
+ /* Always check if pid == 0 */
+ if (pid == 0) {
+ (void ) copyout((char *)&t1, task_addr, sizeof(mach_port_name_t));
+ AUDIT_MACH_SYSCALL_EXIT(KERN_FAILURE);
+ return(KERN_FAILURE);
}
+ t1 = port_name_to_task(target_tport);
+ if (t1 == TASK_NULL) {
+ (void) copyout((char *)&t1, task_addr, sizeof(mach_port_name_t));
+ AUDIT_MACH_SYSCALL_EXIT(KERN_FAILURE);
+ return(KERN_FAILURE);
+ }
- file_control = ubc_getobject(vp, UBC_HOLDOBJECT);
- if (file_control == MEMORY_OBJECT_CONTROL_NULL) {
- error = EINVAL;
- goto lsf_bailout_free_vput;
- }
-#ifdef notdef
- if(vattr.va_size != mapped_file_size) {
- error = EINVAL;
- goto lsf_bailout_free_vput;
- }
+ p = proc_find(pid);
+#if CONFIG_AUDIT
+ if (p != PROC_NULL)
+ AUDIT_ARG(process, p);
#endif
- if(p->p_flag & P_NOSHLIB) {
- p->p_flag = p->p_flag & ~P_NOSHLIB;
- }
- /* load alternate regions if the caller has requested. */
- /* Note: the new regions are "clean slates" */
- if (local_flags & NEW_LOCAL_SHARED_REGIONS) {
- error = clone_system_shared_regions(FALSE, ENV_DEFAULT_ROOT);
- if (error) {
- goto lsf_bailout_free_vput;
- }
+ if (!(task_for_pid_posix_check(p))) {
+ error = KERN_FAILURE;
+ goto tfpout;
}
- vm_get_shared_region(current_task(), &shared_region);
- task_mapping_info.self = (vm_offset_t)shared_region;
-
- shared_region_mapping_info(shared_region,
- &(task_mapping_info.text_region),
- &(task_mapping_info.text_size),
- &(task_mapping_info.data_region),
- &(task_mapping_info.data_size),
- &(task_mapping_info.region_mappings),
- &(task_mapping_info.client_base),
- &(task_mapping_info.alternate_base),
- &(task_mapping_info.alternate_next),
- &(task_mapping_info.fs_base),
- &(task_mapping_info.system),
- &(task_mapping_info.flags), &next);
-
- {
- shared_region_mapping_t default_shared_region;
- default_shared_region =
- lookup_default_shared_region(
- ENV_DEFAULT_ROOT,
- task_mapping_info.system);
- if(shared_region == default_shared_region) {
- default_regions = 1;
- }
- shared_region_mapping_dealloc(default_shared_region);
- }
- /* If we are running on a removable file system we must not */
- /* be in a set of shared regions or the file system will not */
- /* be removable. */
- if(((vp->v_mount != rootvnode->v_mount) && (default_regions))
- && (lsf_mapping_pool_gauge() < 75)) {
- /* We don't want to run out of shared memory */
- /* map entries by starting too many private versions */
- /* of the shared library structures */
- int error;
- if(p->p_flag & P_NOSHLIB) {
- error = clone_system_shared_regions(FALSE, ENV_DEFAULT_ROOT);
- } else {
- error = clone_system_shared_regions(TRUE, ENV_DEFAULT_ROOT);
- }
- if (error) {
- goto lsf_bailout_free_vput;
- }
- local_flags = local_flags & ~NEW_LOCAL_SHARED_REGIONS;
- vm_get_shared_region(current_task(), &shared_region);
- shared_region_mapping_info(shared_region,
- &(task_mapping_info.text_region),
- &(task_mapping_info.text_size),
- &(task_mapping_info.data_region),
- &(task_mapping_info.data_size),
- &(task_mapping_info.region_mappings),
- &(task_mapping_info.client_base),
- &(task_mapping_info.alternate_base),
- &(task_mapping_info.alternate_next),
- &(task_mapping_info.fs_base),
- &(task_mapping_info.system),
- &(task_mapping_info.flags), &next);
- }
+ if (p->task != TASK_NULL) {
+ /* If we aren't root and target's task access port is set... */
+ if (!kauth_cred_issuser(kauth_cred_get()) &&
+ p != current_proc() &&
+ (task_get_task_access_port(p->task, &tfpport) == 0) &&
+ (tfpport != IPC_PORT_NULL)) {
- /* This is a work-around to allow executables which have been */
- /* built without knowledge of the proper shared segment to */
- /* load. This code has been architected as a shared region */
- /* handler, the knowledge of where the regions are loaded is */
- /* problematic for the extension of shared regions as it will */
- /* not be easy to know what region an item should go into. */
- /* The code below however will get around a short term problem */
- /* with executables which believe they are loading at zero. */
-
- {
- if (((unsigned int)local_base &
- (~(task_mapping_info.text_size - 1))) !=
- task_mapping_info.client_base) {
- if(local_flags & ALTERNATE_LOAD_SITE) {
- local_base = (caddr_t)(
- (unsigned int)local_base &
- (task_mapping_info.text_size - 1));
- local_base = (caddr_t)((unsigned int)local_base
- | task_mapping_info.client_base);
- } else {
- error = EINVAL;
- goto lsf_bailout_free_vput;
+ if (tfpport == IPC_PORT_DEAD) {
+ error = KERN_PROTECTION_FAILURE;
+ goto tfpout;
}
- }
- }
+ /* Call up to the task access server */
+ error = check_task_access(tfpport, proc_selfpid(), kauth_getgid(), pid);
- if((kr = copyin_shared_file((vm_offset_t)mapped_file_addr,
- mapped_file_size,
- (vm_offset_t *)&local_base,
- map_cnt, map_list, file_control,
- &task_mapping_info, &local_flags))) {
- switch (kr) {
- case KERN_FAILURE:
- error = EINVAL;
- break;
- case KERN_INVALID_ARGUMENT:
- error = EINVAL;
- break;
- case KERN_INVALID_ADDRESS:
- error = EACCES;
- break;
- case KERN_PROTECTION_FAILURE:
- /* save EAUTH for authentication in this */
- /* routine */
- error = EPERM;
- break;
- case KERN_NO_SPACE:
- error = ENOMEM;
- break;
- default:
- error = EINVAL;
- };
- if((caller_flags & ALTERNATE_LOAD_SITE) && systemLogDiags) {
- printf("load_shared_file: Failed to load shared file! error: 0x%x, Base_address: 0x%x, number of mappings: %d, file_control 0x%x\n", error, local_base, map_cnt, file_control);
- for(i=0; i<map_cnt; i++) {
- printf("load_shared_file: Mapping%d, mapping_offset: 0x%x, size: 0x%x, file_offset: 0x%x, protection: 0x%x\n"
- , i, map_list[i].mapping_offset,
- map_list[i].size,
- map_list[i].file_offset,
- map_list[i].protection);
+ if (error != MACH_MSG_SUCCESS) {
+ if (error == MACH_RCV_INTERRUPTED)
+ error = KERN_ABORTED;
+ else
+ error = KERN_FAILURE;
+ goto tfpout;
}
}
- } else {
- if(default_regions)
- local_flags |= SYSTEM_REGION_BACKED;
- if(!(error = copyout(&local_flags, flags, sizeof (int)))) {
- error = copyout(&local_base,
- base_address, sizeof (caddr_t));
+#if CONFIG_MACF
+ error = mac_proc_check_get_task(kauth_cred_get(), p);
+ if (error) {
+ error = KERN_FAILURE;
+ goto tfpout;
}
- }
-
-lsf_bailout_free_vput:
- vput(vp);
+#endif
-lsf_bailout_free:
- kmem_free(kernel_map, (vm_offset_t)filename_str,
- (vm_size_t)(MAXPATHLEN));
- kmem_free(kernel_map, (vm_offset_t)map_list,
- (vm_size_t)(map_cnt*sizeof(sf_mapping_t)));
+ /* Grant task port access */
+ task_reference(p->task);
+ sright = (void *) convert_task_to_port(p->task);
+ tret = ipc_port_copyout_send(
+ sright,
+ get_task_ipcspace(current_task()));
+ }
+ error = KERN_SUCCESS;
-lsf_bailout:
- return error;
+tfpout:
+ task_deallocate(t1);
+ AUDIT_ARG(mach_port2, tret);
+ (void) copyout((char *) &tret, task_addr, sizeof(mach_port_name_t));
+ if (p != PROC_NULL)
+ proc_rele(p);
+ AUDIT_MACH_SYSCALL_EXIT(error);
+ return(error);
}
-struct reset_shared_file_args {
- caddr_t *ba;
- int map_cnt;
- sf_mapping_t *mappings;
-};
+/*
+ * Routine: task_name_for_pid
+ * Purpose:
+ * Get the task name port for another "process", named by its
+ * process ID on the same host as "target_task".
+ *
+ * Only permitted to privileged processes, or processes
+ * with the same user ID.
+ *
+ * XXX This should be a BSD system call, not a Mach trap!!!
+ */
-int
-reset_shared_file(
- struct proc *p,
- struct reset_shared_file_args *uap,
- register *retval)
+kern_return_t
+task_name_for_pid(
+ struct task_name_for_pid_args *args)
{
- caddr_t *base_address=uap->ba;
- int map_cnt=uap->map_cnt;
- sf_mapping_t *mappings=uap->mappings;
- register int error;
- kern_return_t kr;
+ mach_port_name_t target_tport = args->target_tport;
+ int pid = args->pid;
+ user_addr_t task_addr = args->t;
+ proc_t p = PROC_NULL;
+ task_t t1;
+ mach_port_name_t tret;
+ void * sright;
+ int error = 0, refheld = 0;
+ kauth_cred_t target_cred;
- sf_mapping_t *map_list;
- caddr_t local_base;
- vm_offset_t map_address;
- int i;
- kern_return_t kret;
+ AUDIT_MACH_SYSCALL_ENTER(AUE_TASKNAMEFORPID);
+ AUDIT_ARG(pid, pid);
+ AUDIT_ARG(mach_port1, target_tport);
- /* Retrieve the base address */
- if (error = copyin(base_address, &local_base, sizeof (caddr_t))) {
- goto rsf_bailout;
- }
+ t1 = port_name_to_task(target_tport);
+ if (t1 == TASK_NULL) {
+ (void) copyout((char *)&t1, task_addr, sizeof(mach_port_name_t));
+ AUDIT_MACH_SYSCALL_EXIT(KERN_FAILURE);
+ return(KERN_FAILURE);
+ }
- if (((unsigned int)local_base & GLOBAL_SHARED_SEGMENT_MASK)
- != GLOBAL_SHARED_TEXT_SEGMENT) {
- error = EINVAL;
- goto rsf_bailout;
- }
+ p = proc_find(pid);
+ if (p != PROC_NULL) {
+ AUDIT_ARG(process, p);
+ target_cred = kauth_cred_proc_ref(p);
+ refheld = 1;
- kret = kmem_alloc(kernel_map, (vm_offset_t *)&map_list,
- (vm_size_t)(map_cnt*sizeof(sf_mapping_t)));
- if (kret != KERN_SUCCESS) {
- error = ENOMEM;
- goto rsf_bailout;
- }
+ if ((p->p_stat != SZOMB)
+ && ((current_proc() == p)
+ || kauth_cred_issuser(kauth_cred_get())
+ || ((kauth_cred_getuid(target_cred) == kauth_cred_getuid(kauth_cred_get())) &&
+ ((target_cred->cr_ruid == kauth_cred_get()->cr_ruid))))) {
- if (error =
- copyin(mappings, map_list, (map_cnt*sizeof(sf_mapping_t)))) {
+ if (p->task != TASK_NULL) {
+ task_reference(p->task);
+#if CONFIG_MACF
+ error = mac_proc_check_get_task_name(kauth_cred_get(), p);
+ if (error) {
+ task_deallocate(p->task);
+ goto noperm;
+ }
+#endif
+ sright = (void *)convert_task_name_to_port(p->task);
+ tret = ipc_port_copyout_send(sright,
+ get_task_ipcspace(current_task()));
+ } else
+ tret = MACH_PORT_NULL;
- kmem_free(kernel_map, (vm_offset_t)map_list,
- (vm_size_t)(map_cnt*sizeof(sf_mapping_t)));
- goto rsf_bailout;
- }
- for (i = 0; i<map_cnt; i++) {
- if((map_list[i].mapping_offset
- & GLOBAL_SHARED_SEGMENT_MASK) == 0x10000000) {
- map_address = (vm_offset_t)
- (local_base + map_list[i].mapping_offset);
- vm_deallocate(current_map(),
- map_address,
- map_list[i].size);
- vm_map(current_map(), &map_address,
- map_list[i].size, 0, SHARED_LIB_ALIAS,
- shared_data_region_handle,
- ((unsigned int)local_base
- & SHARED_DATA_REGION_MASK) +
- (map_list[i].mapping_offset
- & SHARED_DATA_REGION_MASK),
- TRUE, VM_PROT_READ,
- VM_PROT_READ, VM_INHERIT_SHARE);
+ AUDIT_ARG(mach_port2, tret);
+ (void) copyout((char *)&tret, task_addr, sizeof(mach_port_name_t));
+ task_deallocate(t1);
+ error = KERN_SUCCESS;
+ goto tnfpout;
}
}
- kmem_free(kernel_map, (vm_offset_t)map_list,
- (vm_size_t)(map_cnt*sizeof(sf_mapping_t)));
-
-rsf_bailout:
- return error;
+#if CONFIG_MACF
+noperm:
+#endif
+ task_deallocate(t1);
+ tret = MACH_PORT_NULL;
+ (void) copyout((char *) &tret, task_addr, sizeof(mach_port_name_t));
+ error = KERN_FAILURE;
+tnfpout:
+ if (refheld != 0)
+ kauth_cred_unref(&target_cred);
+ if (p != PROC_NULL)
+ proc_rele(p);
+ AUDIT_MACH_SYSCALL_EXIT(error);
+ return(error);
}
-struct new_system_shared_regions_args {
- int dummy;
-};
-
-int
-new_system_shared_regions(
- struct proc *p,
- struct new_system_shared_regions_args *uap,
- register *retval)
+static int
+sysctl_settfp_policy(__unused struct sysctl_oid *oidp, void *arg1,
+ __unused int arg2, struct sysctl_req *req)
{
- shared_region_mapping_t regions;
- shared_region_mapping_t new_regions;
-
- if(!(is_suser())) {
- *retval = EINVAL;
- return EINVAL;
- }
+ int error = 0;
+ int new_value;
- /* clear all of our existing defaults */
- remove_all_shared_regions();
+ error = SYSCTL_OUT(req, arg1, sizeof(int));
+ if (error || req->newptr == USER_ADDR_NULL)
+ return(error);
- *retval = 0;
- return 0;
-}
+ if (!is_suser())
+ return(EPERM);
-
-
-int
-clone_system_shared_regions(shared_regions_active, base_vnode)
-{
- shared_region_mapping_t new_shared_region;
- shared_region_mapping_t next;
- shared_region_mapping_t old_shared_region;
- struct shared_region_task_mappings old_info;
- struct shared_region_task_mappings new_info;
-
- struct proc *p;
-
- vm_get_shared_region(current_task(), &old_shared_region);
- old_info.self = (vm_offset_t)old_shared_region;
- shared_region_mapping_info(old_shared_region,
- &(old_info.text_region),
- &(old_info.text_size),
- &(old_info.data_region),
- &(old_info.data_size),
- &(old_info.region_mappings),
- &(old_info.client_base),
- &(old_info.alternate_base),
- &(old_info.alternate_next),
- &(old_info.fs_base),
- &(old_info.system),
- &(old_info.flags), &next);
- if ((shared_regions_active) ||
- (base_vnode == ENV_DEFAULT_ROOT)) {
- if (shared_file_create_system_region(&new_shared_region))
- return (ENOMEM);
- } else {
- new_shared_region =
- lookup_default_shared_region(
- base_vnode, old_info.system);
- if(new_shared_region == NULL) {
- shared_file_boot_time_init(
- base_vnode, old_info.system);
- vm_get_shared_region(current_task(), &new_shared_region);
- } else {
- vm_set_shared_region(current_task(), new_shared_region);
- }
- if(old_shared_region)
- shared_region_mapping_dealloc(old_shared_region);
- }
- new_info.self = (vm_offset_t)new_shared_region;
- shared_region_mapping_info(new_shared_region,
- &(new_info.text_region),
- &(new_info.text_size),
- &(new_info.data_region),
- &(new_info.data_size),
- &(new_info.region_mappings),
- &(new_info.client_base),
- &(new_info.alternate_base),
- &(new_info.alternate_next),
- &(new_info.fs_base),
- &(new_info.system),
- &(new_info.flags), &next);
- if(shared_regions_active) {
- if(vm_region_clone(old_info.text_region, new_info.text_region)) {
- panic("clone_system_shared_regions: shared region mis-alignment 1");
- shared_region_mapping_dealloc(new_shared_region);
- return(EINVAL);
- }
- if (vm_region_clone(old_info.data_region, new_info.data_region)) {
- panic("clone_system_shared_regions: shared region mis-alignment 2");
- shared_region_mapping_dealloc(new_shared_region);
- return(EINVAL);
- }
- shared_region_object_chain_attach(
- new_shared_region, old_shared_region);
- }
- if (vm_map_region_replace(current_map(), old_info.text_region,
- new_info.text_region, old_info.client_base,
- old_info.client_base+old_info.text_size)) {
- panic("clone_system_shared_regions: shared region mis-alignment 3");
- shared_region_mapping_dealloc(new_shared_region);
- return(EINVAL);
- }
- if(vm_map_region_replace(current_map(), old_info.data_region,
- new_info.data_region,
- old_info.client_base + old_info.text_size,
- old_info.client_base
- + old_info.text_size + old_info.data_size)) {
- panic("clone_system_shared_regions: shared region mis-alignment 4");
- shared_region_mapping_dealloc(new_shared_region);
- return(EINVAL);
+ if ((error = SYSCTL_IN(req, &new_value, sizeof(int)))) {
+ goto out;
}
- vm_set_shared_region(current_task(), new_shared_region);
-
- /* consume the reference which wasn't accounted for in object */
- /* chain attach */
- if(!shared_regions_active)
- shared_region_mapping_dealloc(old_shared_region);
-
- return(0);
+ if ((new_value == KERN_TFP_POLICY_DENY)
+ || (new_value == KERN_TFP_POLICY_DEFAULT))
+ tfp_policy = new_value;
+ else
+ error = EINVAL;
+out:
+ return(error);
}
-extern vm_map_t bsd_pageable_map;
-
-/* header for the profile name file. The profiled app info is held */
-/* in the data file and pointed to by elements in the name file */
-
-struct profile_names_header {
- unsigned int number_of_profiles;
- unsigned int user_id;
- unsigned int version;
- off_t element_array;
- unsigned int spare1;
- unsigned int spare2;
- unsigned int spare3;
-};
-
-struct profile_element {
- off_t addr;
- vm_size_t size;
- unsigned int mod_date;
- unsigned int inode;
- char name[12];
-};
-
-struct global_profile {
- struct vnode *names_vp;
- struct vnode *data_vp;
- vm_offset_t buf_ptr;
- unsigned int user;
- unsigned int age;
- unsigned int busy;
-};
+#if defined(SECURE_KERNEL)
+static int kern_secure_kernel = 1;
+#else
+static int kern_secure_kernel = 0;
+#endif
-struct global_profile_cache {
- int max_ele;
- unsigned int age;
- struct global_profile profiles[3];
-};
+SYSCTL_INT(_kern, OID_AUTO, secure_kernel, CTLFLAG_RD, &kern_secure_kernel, 0, "");
-struct global_profile_cache global_user_profile_cache =
- {3, 0, NULL, NULL, NULL, 0, 0, 0,
- NULL, NULL, NULL, 0, 0, 0,
- NULL, NULL, NULL, 0, 0, 0 };
+SYSCTL_NODE(_kern, KERN_TFP, tfp, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "tfp");
+SYSCTL_PROC(_kern_tfp, KERN_TFP_POLICY, policy, CTLTYPE_INT | CTLFLAG_RW,
+ &tfp_policy, sizeof(uint32_t), &sysctl_settfp_policy ,"I","policy");
-/* BSD_OPEN_PAGE_CACHE_FILES: */
-/* Caller provides a user id. This id was used in */
-/* prepare_profile_database to create two unique absolute */
-/* file paths to the associated profile files. These files */
-/* are either opened or bsd_open_page_cache_files returns an */
-/* error. The header of the names file is then consulted. */
-/* The header and the vnodes for the names and data files are */
-/* returned. */
+SYSCTL_INT(_vm, OID_AUTO, shared_region_trace_level, CTLFLAG_RW,
+ &shared_region_trace_level, 0, "");
+SYSCTL_INT(_vm, OID_AUTO, shared_region_version, CTLFLAG_RD,
+ &shared_region_version, 0, "");
+SYSCTL_INT(_vm, OID_AUTO, shared_region_persistence, CTLFLAG_RW,
+ &shared_region_persistence, 0, "");
+/*
+ * shared_region_check_np:
+ *
+ * This system call is intended for dyld.
+ *
+ * dyld calls this when any process starts to see if the process's shared
+ * region is already set up and ready to use.
+ * This call returns the base address of the first mapping in the
+ * process's shared region's first mapping.
+ * dyld will then check what's mapped at that address.
+ *
+ * If the shared region is empty, dyld will then attempt to map the shared
+ * cache file in the shared region via the shared_region_map_np() system call.
+ *
+ * If something's already mapped in the shared region, dyld will check if it
+ * matches the shared cache it would like to use for that process.
+ * If it matches, evrything's ready and the process can proceed and use the
+ * shared region.
+ * If it doesn't match, dyld will unmap the shared region and map the shared
+ * cache into the process's address space via mmap().
+ *
+ * ERROR VALUES
+ * EINVAL no shared region
+ * ENOMEM shared region is empty
+ * EFAULT bad address for "start_address"
+ */
int
-bsd_open_page_cache_files(
- unsigned int user,
- struct global_profile **profile)
+shared_region_check_np(
+ __unused struct proc *p,
+ struct shared_region_check_np_args *uap,
+ __unused int *retvalp)
{
- char *cache_path = "/var/vm/app_profile/";
- struct proc *p;
- int error;
- int resid;
- off_t resid_off;
- unsigned int lru;
- vm_size_t size;
-
- struct vnode *names_vp;
- struct vnode *data_vp;
- vm_offset_t names_buf;
- vm_offset_t buf_ptr;
-
- int profile_names_length;
- int profile_data_length;
- char *profile_data_string;
- char *profile_names_string;
- char *substring;
-
- struct vattr vattr;
-
- struct profile_names_header *profile_header;
- kern_return_t ret;
-
- struct nameidata nd_names;
- struct nameidata nd_data;
-
- int i;
-
-
- p = current_proc();
-
-restart:
- for(i = 0; i<global_user_profile_cache.max_ele; i++) {
- if((global_user_profile_cache.profiles[i].user == user)
- && (global_user_profile_cache.profiles[i].data_vp
- != NULL)) {
- *profile = &global_user_profile_cache.profiles[i];
- /* already in cache, we're done */
- if ((*profile)->busy) {
- /*
- * drop funnel and wait
- */
- (void)tsleep((void *)
- *profile,
- PRIBIO, "app_profile", 0);
- goto restart;
- }
- (*profile)->busy = 1;
- (*profile)->age = global_user_profile_cache.age;
- global_user_profile_cache.age+=1;
- return 0;
- }
- }
-
- lru = global_user_profile_cache.age;
- for(i = 0; i<global_user_profile_cache.max_ele; i++) {
- if(global_user_profile_cache.profiles[i].data_vp == NULL) {
- *profile = &global_user_profile_cache.profiles[i];
- (*profile)->age = global_user_profile_cache.age;
- global_user_profile_cache.age+=1;
- break;
- }
- if(global_user_profile_cache.profiles[i].age < lru) {
- lru = global_user_profile_cache.profiles[i].age;
- *profile = &global_user_profile_cache.profiles[i];
- }
- }
+ vm_shared_region_t shared_region;
+ mach_vm_offset_t start_address;
+ int error;
+ kern_return_t kr;
- if ((*profile)->busy) {
- /*
- * drop funnel and wait
- */
- (void)tsleep((void *)
- &(global_user_profile_cache),
- PRIBIO, "app_profile", 0);
- goto restart;
- }
- (*profile)->busy = 1;
- (*profile)->user = user;
-
- if((*profile)->data_vp != NULL) {
- kmem_free(kernel_map,
- (*profile)->buf_ptr, 4 * PAGE_SIZE);
- if ((*profile)->names_vp) {
- vrele((*profile)->names_vp);
- (*profile)->names_vp = NULL;
- }
- if ((*profile)->data_vp) {
- vrele((*profile)->data_vp);
- (*profile)->data_vp = NULL;
+ SHARED_REGION_TRACE_DEBUG(
+ ("shared_region: %p [%d(%s)] -> check_np(0x%llx)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ (uint64_t)uap->start_address));
+
+ /* retrieve the current tasks's shared region */
+ shared_region = vm_shared_region_get(current_task());
+ if (shared_region != NULL) {
+ /* retrieve address of its first mapping... */
+ kr = vm_shared_region_start_address(shared_region,
+ &start_address);
+ if (kr != KERN_SUCCESS) {
+ error = ENOMEM;
+ } else {
+ /* ... and give it to the caller */
+ error = copyout(&start_address,
+ (user_addr_t) uap->start_address,
+ sizeof (start_address));
+ if (error) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] "
+ "check_np(0x%llx) "
+ "copyout(0x%llx) error %d\n",
+ current_thread(), p->p_pid, p->p_comm,
+ (uint64_t)uap->start_address, (uint64_t)start_address,
+ error));
+ }
}
+ vm_shared_region_deallocate(shared_region);
+ } else {
+ /* no shared region ! */
+ error = EINVAL;
}
- /* put dummy value in for now to get */
- /* competing request to wait above */
- /* until we are finished */
- (*profile)->data_vp = (struct vnode *)0xFFFFFFFF;
-
- /* Try to open the appropriate users profile files */
- /* If neither file is present, try to create them */
- /* If one file is present and the other not, fail. */
- /* If the files do exist, check them for the app_file */
- /* requested and read it in if present */
-
+ SHARED_REGION_TRACE_DEBUG(
+ ("shared_region: %p [%d(%s)] check_np(0x%llx) <- 0x%llx %d\n",
+ current_thread(), p->p_pid, p->p_comm,
+ (uint64_t)uap->start_address, (uint64_t)start_address, error));
- ret = kmem_alloc(kernel_map,
- (vm_offset_t *)&profile_data_string, PATH_MAX);
+ return error;
+}
- if(ret) {
- (*profile)->data_vp = NULL;
- (*profile)->busy = 0;
- wakeup(*profile);
- return ENOMEM;
+/*
+ * shared_region_map_np()
+ *
+ * This system call is intended for dyld.
+ *
+ * dyld uses this to map a shared cache file into a shared region.
+ * This is usually done only the first time a shared cache is needed.
+ * Subsequent processes will just use the populated shared region without
+ * requiring any further setup.
+ */
+int
+shared_region_map_np(
+ struct proc *p,
+ struct shared_region_map_np_args *uap,
+ __unused int *retvalp)
+{
+ int error;
+ kern_return_t kr;
+ int fd;
+ struct fileproc *fp;
+ struct vnode *vp, *root_vp;
+ struct vnode_attr va;
+ off_t fs;
+ memory_object_size_t file_size;
+ user_addr_t user_mappings;
+ struct shared_file_mapping_np *mappings;
+#define SFM_MAX_STACK 8
+ struct shared_file_mapping_np stack_mappings[SFM_MAX_STACK];
+ unsigned int mappings_count;
+ vm_size_t mappings_size;
+ memory_object_control_t file_control;
+ struct vm_shared_region *shared_region;
+
+ SHARED_REGION_TRACE_DEBUG(
+ ("shared_region: %p [%d(%s)] -> map\n",
+ current_thread(), p->p_pid, p->p_comm));
+
+ shared_region = NULL;
+ mappings_count = 0;
+ mappings_size = 0;
+ mappings = NULL;
+ fp = NULL;
+ vp = NULL;
+
+ /* get file descriptor for shared region cache file */
+ fd = uap->fd;
+
+ /* get file structure from file descriptor */
+ error = fp_lookup(p, fd, &fp, 0);
+ if (error) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map: "
+ "fd=%d lookup failed (error=%d)\n",
+ current_thread(), p->p_pid, p->p_comm, fd, error));
+ goto done;
}
- /* Split the buffer in half since we know the size of */
- /* our file path and our allocation is adequate for */
- /* both file path names */
- profile_names_string = profile_data_string + (PATH_MAX/2);
-
-
- strcpy(profile_data_string, cache_path);
- strcpy(profile_names_string, cache_path);
- profile_names_length = profile_data_length
- = strlen(profile_data_string);
- substring = profile_data_string + profile_data_length;
- sprintf(substring, "%x_data", user);
- substring = profile_names_string + profile_names_length;
- sprintf(substring, "%x_names", user);
-
- /* We now have the absolute file names */
-
- ret = kmem_alloc(kernel_map,
- (vm_offset_t *)&names_buf, 4 * PAGE_SIZE);
- if(ret) {
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- (*profile)->data_vp = NULL;
- (*profile)->busy = 0;
- wakeup(*profile);
- return ENOMEM;
+ /* make sure we're attempting to map a vnode */
+ if (fp->f_fglob->fg_type != DTYPE_VNODE) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map: "
+ "fd=%d not a vnode (type=%d)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ fd, fp->f_fglob->fg_type));
+ error = EINVAL;
+ goto done;
}
- NDINIT(&nd_names, LOOKUP, FOLLOW | LOCKLEAF,
- UIO_SYSSPACE, profile_names_string, p);
- NDINIT(&nd_data, LOOKUP, FOLLOW | LOCKLEAF,
- UIO_SYSSPACE, profile_data_string, p);
- if (error = vn_open(&nd_data, FREAD | FWRITE, 0)) {
-#ifdef notdef
- printf("bsd_open_page_cache_files: CacheData file not found %s\n",
- profile_data_string);
-#endif
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- (*profile)->data_vp = NULL;
- (*profile)->busy = 0;
- wakeup(*profile);
- return error;
+ /* we need at least read permission on the file */
+ if (! (fp->f_fglob->fg_flag & FREAD)) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map: "
+ "fd=%d not readable\n",
+ current_thread(), p->p_pid, p->p_comm, fd));
+ error = EPERM;
+ goto done;
}
- data_vp = nd_data.ni_vp;
- VOP_UNLOCK(data_vp, 0, p);
-
- if (error = vn_open(&nd_names, FREAD | FWRITE, 0)) {
- printf("bsd_open_page_cache_files: NamesData file not found %s\n",
- profile_data_string);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- vrele(data_vp);
- (*profile)->data_vp = NULL;
- (*profile)->busy = 0;
- wakeup(*profile);
- return error;
- }
- names_vp = nd_names.ni_vp;
-
- if(error = VOP_GETATTR(names_vp, &vattr, p->p_ucred, p)) {
- printf("bsd_open_page_cache_files: Can't stat name file %s\n", profile_names_string);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- vput(names_vp);
- vrele(data_vp);
- (*profile)->data_vp = NULL;
- (*profile)->busy = 0;
- wakeup(*profile);
- return error;
+ /* get vnode from file structure */
+ error = vnode_getwithref((vnode_t) fp->f_fglob->fg_data);
+ if (error) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map: "
+ "fd=%d getwithref failed (error=%d)\n",
+ current_thread(), p->p_pid, p->p_comm, fd, error));
+ goto done;
}
+ vp = (struct vnode *) fp->f_fglob->fg_data;
- size = vattr.va_size;
- if(size > 4 * PAGE_SIZE)
- size = 4 * PAGE_SIZE;
- buf_ptr = names_buf;
- resid_off = 0;
-
- while(size) {
- error = vn_rdwr(UIO_READ, names_vp, (caddr_t)buf_ptr,
- size, resid_off,
- UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
- if((error) || (size == resid)) {
- if(!error) {
- error = EINVAL;
- }
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- vput(names_vp);
- vrele(data_vp);
- (*profile)->data_vp = NULL;
- (*profile)->busy = 0;
- wakeup(*profile);
- return error;
- }
- buf_ptr += size-resid;
- resid_off += size-resid;
- size = resid;
+ /* make sure the vnode is a regular file */
+ if (vp->v_type != VREG) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "not a file (type=%d)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, vp->v_type));
+ error = EINVAL;
+ goto done;
}
-
- VOP_UNLOCK(names_vp, 0, p);
- kmem_free(kernel_map, (vm_offset_t)profile_data_string, PATH_MAX);
- (*profile)->names_vp = names_vp;
- (*profile)->data_vp = data_vp;
- (*profile)->buf_ptr = names_buf;
- return 0;
-
-}
-
-void
-bsd_close_page_cache_files(
- struct global_profile *profile)
-{
- profile->busy = 0;
- wakeup(profile);
-}
-
-int
-bsd_read_page_cache_file(
- unsigned int user,
- int *fid,
- int *mod,
- char *app_name,
- struct vnode *app_vp,
- vm_offset_t *buffer,
- vm_offset_t *buf_size)
-{
-
- boolean_t funnel_state;
- struct proc *p;
- int error;
- int resid;
- vm_size_t size;
-
- off_t profile;
- unsigned int profile_size;
-
- vm_offset_t names_buf;
- struct vattr vattr;
-
- kern_return_t ret;
-
- struct vnode *names_vp;
- struct vnode *data_vp;
- struct vnode *vp1;
- struct vnode *vp2;
-
- struct global_profile *uid_files;
-
- funnel_state = thread_funnel_set(kernel_flock, TRUE);
-
- /* Try to open the appropriate users profile files */
- /* If neither file is present, try to create them */
- /* If one file is present and the other not, fail. */
- /* If the files do exist, check them for the app_file */
- /* requested and read it in if present */
-
-
- error = bsd_open_page_cache_files(user, &uid_files);
- if(error) {
- thread_funnel_set(kernel_flock, funnel_state);
- return EINVAL;
+ /* make sure vnode is on the process's root volume */
+ root_vp = p->p_fd->fd_rdir;
+ if (root_vp == NULL) {
+ root_vp = rootvnode;
}
-
- p = current_proc();
-
- names_vp = uid_files->names_vp;
- data_vp = uid_files->data_vp;
- names_buf = uid_files->buf_ptr;
-
-
- /*
- * Get locks on both files, get the vnode with the lowest address first
- */
-
- if((unsigned int)names_vp < (unsigned int)data_vp) {
- vp1 = names_vp;
- vp2 = data_vp;
- } else {
- vp1 = data_vp;
- vp2 = names_vp;
+ if (vp->v_mount != root_vp->v_mount) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "not on process's root volume\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name));
+ error = EPERM;
+ goto done;
}
- error = vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY, p);
- if(error) {
- printf("bsd_read_page_cache_file: Can't lock profile names %x\n", user);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return error;
+
+ /* make sure vnode is owned by "root" */
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_uid);
+ error = vnode_getattr(vp, &va, vfs_context_current());
+ if (error) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "vnode_getattr(%p) failed (error=%d)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, vp, error));
+ goto done;
}
- error = vn_lock(vp2, LK_EXCLUSIVE | LK_RETRY, p);
- if(error) {
- printf("bsd_read_page_cache_file: Can't lock profile data %x\n", user);
- VOP_UNLOCK(vp1, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return error;
+ if (va.va_uid != 0) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "owned by uid=%d instead of 0\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, va.va_uid));
+ error = EPERM;
+ goto done;
}
- if(error = VOP_GETATTR(app_vp, &vattr, p->p_ucred, p)) {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- printf("bsd_read_cache_file: Can't stat app file %s\n", app_name);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return error;
+ /* get vnode size */
+ error = vnode_size(vp, &fs, vfs_context_current());
+ if (error) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "vnode_size(%p) failed (error=%d)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, vp, error));
+ goto done;
}
+ file_size = fs;
- *fid = vattr.va_fileid;
- *mod = vattr.va_mtime.tv_sec;
-
-
- if (bsd_search_page_cache_data_base(names_vp, names_buf, app_name,
- (unsigned int) vattr.va_mtime.tv_sec,
- vattr.va_fileid, &profile, &profile_size) == 0) {
- /* profile is an offset in the profile data base */
- /* It is zero if no profile data was found */
-
- if(profile_size == 0) {
- *buffer = NULL;
- *buf_size = 0;
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return 0;
- }
- ret = (vm_offset_t)(kmem_alloc(kernel_map, buffer, profile_size));
- if(ret) {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return ENOMEM;
- }
- *buf_size = profile_size;
- while(profile_size) {
- error = vn_rdwr(UIO_READ, data_vp,
- (caddr_t) *buffer, profile_size,
- profile, UIO_SYSSPACE, IO_NODELOCKED,
- p->p_ucred, &resid, p);
- if((error) || (profile_size == resid)) {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- kmem_free(kernel_map, (vm_offset_t)*buffer, profile_size);
- thread_funnel_set(kernel_flock, funnel_state);
- return EINVAL;
- }
- profile += profile_size - resid;
- profile_size = resid;
- }
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return 0;
+ /* get the file's memory object handle */
+ file_control = ubc_getobject(vp, UBC_HOLDOBJECT);
+ if (file_control == MEMORY_OBJECT_CONTROL_NULL) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "no memory object\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name));
+ error = EINVAL;
+ goto done;
+ }
+
+ /* get the list of mappings the caller wants us to establish */
+ mappings_count = uap->count; /* number of mappings */
+ mappings_size = (vm_size_t) (mappings_count * sizeof (mappings[0]));
+ if (mappings_count == 0) {
+ SHARED_REGION_TRACE_INFO(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "no mappings\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name));
+ error = 0; /* no mappings: we're done ! */
+ goto done;
+ } else if (mappings_count <= SFM_MAX_STACK) {
+ mappings = &stack_mappings[0];
} else {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return EINVAL;
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "too many mappings (%d)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, mappings_count));
+ error = EINVAL;
+ goto done;
}
-
-}
-
-int
-bsd_search_page_cache_data_base(
- struct vnode *vp,
- struct profile_names_header *database,
- char *app_name,
- unsigned int mod_date,
- unsigned int inode,
- off_t *profile,
- unsigned int *profile_size)
-{
-
- struct proc *p;
-
- unsigned int i;
- struct profile_element *element;
- unsigned int ele_total;
- unsigned int extended_list = 0;
- off_t file_off = 0;
- unsigned int size;
- off_t resid_off;
- int resid;
- vm_offset_t local_buf = NULL;
-
- int error;
- kern_return_t ret;
- p = current_proc();
+ user_mappings = uap->mappings; /* the mappings, in user space */
+ error = copyin(user_mappings,
+ mappings,
+ mappings_size);
+ if (error) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "copyin(0x%llx, %d) failed (error=%d)\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, (uint64_t)user_mappings, mappings_count, error));
+ goto done;
+ }
- if(((vm_offset_t)database->element_array) !=
- sizeof(struct profile_names_header)) {
- return EINVAL;
+ /* get the process's shared region (setup in vm_map_exec()) */
+ shared_region = vm_shared_region_get(current_task());
+ if (shared_region == NULL) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "no shared region\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name));
+ goto done;
}
- element = (struct profile_element *)(
- (vm_offset_t)database->element_array +
- (vm_offset_t)database);
- ele_total = database->number_of_profiles;
-
- *profile = 0;
- *profile_size = 0;
- while(ele_total) {
- /* note: code assumes header + n*ele comes out on a page boundary */
- if(((local_buf == 0) && (sizeof(struct profile_names_header) +
- (ele_total * sizeof(struct profile_element)))
- > (PAGE_SIZE * 4)) ||
- ((local_buf != 0) &&
- (ele_total * sizeof(struct profile_element))
- > (PAGE_SIZE * 4))) {
- extended_list = ele_total;
- if(element == (struct profile_element *)
- ((vm_offset_t)database->element_array +
- (vm_offset_t)database)) {
- ele_total = ((PAGE_SIZE * 4)/sizeof(struct profile_element)) - 1;
- } else {
- ele_total = (PAGE_SIZE * 4)/sizeof(struct profile_element);
- }
- extended_list -= ele_total;
- }
- for (i=0; i<ele_total; i++) {
- if((mod_date == element[i].mod_date)
- && (inode == element[i].inode)) {
- if(strncmp(element[i].name, app_name, 12) == 0) {
- *profile = element[i].addr;
- *profile_size = element[i].size;
- if(local_buf != NULL) {
- kmem_free(kernel_map,
- (vm_offset_t)local_buf, 4 * PAGE_SIZE);
- }
- return 0;
- }
- }
- }
- if(extended_list == 0)
+ /* map the file into that shared region's submap */
+ kr = vm_shared_region_map_file(shared_region,
+ mappings_count,
+ mappings,
+ file_control,
+ file_size,
+ (void *) p->p_fd->fd_rdir);
+ if (kr != KERN_SUCCESS) {
+ SHARED_REGION_TRACE_ERROR(
+ ("shared_region: %p [%d(%s)] map(%p:'%s'): "
+ "vm_shared_region_map_file() failed kr=0x%x\n",
+ current_thread(), p->p_pid, p->p_comm,
+ vp, vp->v_name, kr));
+ switch (kr) {
+ case KERN_INVALID_ADDRESS:
+ error = EFAULT;
+ break;
+ case KERN_PROTECTION_FAILURE:
+ error = EPERM;
+ break;
+ case KERN_NO_SPACE:
+ error = ENOMEM;
+ break;
+ case KERN_FAILURE:
+ case KERN_INVALID_ARGUMENT:
+ default:
+ error = EINVAL;
break;
- if(local_buf == NULL) {
- ret = kmem_alloc(kernel_map,
- (vm_offset_t *)&local_buf, 4 * PAGE_SIZE);
- if(ret != KERN_SUCCESS) {
- return ENOMEM;
- }
- }
- element = (struct profile_element *)local_buf;
- ele_total = extended_list;
- extended_list = 0;
- file_off += 4 * PAGE_SIZE;
- if((ele_total * sizeof(struct profile_element)) >
- (PAGE_SIZE * 4)) {
- size = PAGE_SIZE * 4;
- } else {
- size = ele_total * sizeof(struct profile_element);
- }
- resid_off = 0;
- while(size) {
- error = vn_rdwr(UIO_READ, vp,
- (caddr_t)(local_buf + resid_off),
- size, file_off + resid_off, UIO_SYSSPACE,
- IO_NODELOCKED, p->p_ucred, &resid, p);
- if((error) || (size == resid)) {
- if(local_buf != NULL) {
- kmem_free(kernel_map,
- (vm_offset_t)local_buf,
- 4 * PAGE_SIZE);
- }
- return EINVAL;
- }
- resid_off += size-resid;
- size = resid;
}
+ goto done;
}
- if(local_buf != NULL) {
- kmem_free(kernel_map,
- (vm_offset_t)local_buf, 4 * PAGE_SIZE);
- }
- return 0;
-}
-int
-bsd_write_page_cache_file(
- unsigned int user,
- char *file_name,
- caddr_t buffer,
- vm_size_t size,
- int mod,
- int fid)
-{
- struct proc *p;
- struct nameidata nd;
- struct vnode *vp = 0;
- int resid;
- off_t resid_off;
- int error;
- boolean_t funnel_state;
- struct vattr vattr;
- struct vattr data_vattr;
+ error = 0;
- off_t profile;
- unsigned int profile_size;
-
- vm_offset_t names_buf;
- struct vnode *names_vp;
- struct vnode *data_vp;
- struct vnode *vp1;
- struct vnode *vp2;
-
- struct profile_names_header *profile_header;
- off_t name_offset;
-
- struct global_profile *uid_files;
-
-
- funnel_state = thread_funnel_set(kernel_flock, TRUE);
-
-
-
- error = bsd_open_page_cache_files(user, &uid_files);
- if(error) {
- thread_funnel_set(kernel_flock, funnel_state);
- return EINVAL;
+ /* update the vnode's access time */
+ if (! (vnode_vfsvisflags(vp) & MNT_NOATIME)) {
+ VATTR_INIT(&va);
+ nanotime(&va.va_access_time);
+ VATTR_SET_ACTIVE(&va, va_access_time);
+ vnode_setattr(vp, &va, vfs_context_current());
}
- p = current_proc();
-
- names_vp = uid_files->names_vp;
- data_vp = uid_files->data_vp;
- names_buf = uid_files->buf_ptr;
-
- /*
- * Get locks on both files, get the vnode with the lowest address first
- */
-
- if((unsigned int)names_vp < (unsigned int)data_vp) {
- vp1 = names_vp;
- vp2 = data_vp;
- } else {
- vp1 = data_vp;
- vp2 = names_vp;
+ if (p->p_flag & P_NOSHLIB) {
+ /* signal that this process is now using split libraries */
+ OSBitAndAtomic(~((uint32_t)P_NOSHLIB), &p->p_flag);
}
- error = vn_lock(vp1, LK_EXCLUSIVE | LK_RETRY, p);
- if(error) {
- printf("bsd_write_page_cache_file: Can't lock profile names %x\n", user);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return error;
- }
- error = vn_lock(vp2, LK_EXCLUSIVE | LK_RETRY, p);
- if(error) {
- printf("bsd_write_page_cache_file: Can't lock profile data %x\n", user);
- VOP_UNLOCK(vp1, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return error;
+done:
+ if (vp != NULL) {
+ /*
+ * release the vnode...
+ * ubc_map() still holds it for us in the non-error case
+ */
+ (void) vnode_put(vp);
+ vp = NULL;
}
-
- /* Stat data file for size */
-
- if(error = VOP_GETATTR(data_vp, &data_vattr, p->p_ucred, p)) {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- printf("bsd_write_page_cache_file: Can't stat profile data %s\n", file_name);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return error;
+ if (fp != NULL) {
+ /* release the file descriptor */
+ fp_drop(p, fd, fp, 0);
+ fp = NULL;
}
-
- if (bsd_search_page_cache_data_base(names_vp,
- (struct profile_names_header *)names_buf,
- file_name, (unsigned int) mod,
- fid, &profile, &profile_size) == 0) {
- /* profile is an offset in the profile data base */
- /* It is zero if no profile data was found */
-
- if(profile_size == 0) {
- unsigned int header_size;
- vm_offset_t buf_ptr;
-
- /* Our Write case */
-
- /* read header for last entry */
- profile_header =
- (struct profile_names_header *)names_buf;
- name_offset = sizeof(struct profile_names_header) +
- (sizeof(struct profile_element)
- * profile_header->number_of_profiles);
- profile_header->number_of_profiles += 1;
-
- if(name_offset < PAGE_SIZE * 4) {
- struct profile_element *name;
- /* write new entry */
- name = (struct profile_element *)
- (names_buf + (vm_offset_t)name_offset);
- name->addr = data_vattr.va_size;
- name->size = size;
- name->mod_date = mod;
- name->inode = fid;
- strncpy (name->name, file_name, 12);
- } else {
- unsigned int ele_size;
- struct profile_element name;
- /* write new entry */
- name.addr = data_vattr.va_size;
- name.size = size;
- name.mod_date = mod;
- name.inode = fid;
- strncpy (name.name, file_name, 12);
- /* write element out separately */
- ele_size = sizeof(struct profile_element);
- buf_ptr = (vm_offset_t)&name;
- resid_off = name_offset;
-
- while(ele_size) {
- error = vn_rdwr(UIO_WRITE, names_vp,
- (caddr_t)buf_ptr,
- ele_size, resid_off,
- UIO_SYSSPACE, IO_NODELOCKED,
- p->p_ucred, &resid, p);
- if(error) {
- printf("bsd_write_page_cache_file: Can't write name_element %x\n", user);
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(
- uid_files);
- thread_funnel_set(
- kernel_flock,
- funnel_state);
- return error;
- }
- buf_ptr += (vm_offset_t)
- ele_size-resid;
- resid_off += ele_size-resid;
- ele_size = resid;
- }
- }
- if(name_offset < PAGE_SIZE * 4) {
- header_size = name_offset +
- sizeof(struct profile_element);
-
- } else {
- header_size =
- sizeof(struct profile_names_header);
- }
- buf_ptr = (vm_offset_t)profile_header;
- resid_off = 0;
-
- /* write names file header */
- while(header_size) {
- error = vn_rdwr(UIO_WRITE, names_vp,
- (caddr_t)buf_ptr,
- header_size, resid_off,
- UIO_SYSSPACE, IO_NODELOCKED,
- p->p_ucred, &resid, p);
- if(error) {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- printf("bsd_write_page_cache_file: Can't write header %x\n", user);
- bsd_close_page_cache_files(
- uid_files);
- thread_funnel_set(
- kernel_flock, funnel_state);
- return error;
- }
- buf_ptr += (vm_offset_t)header_size-resid;
- resid_off += header_size-resid;
- header_size = resid;
- }
- /* write profile to data file */
- resid_off = data_vattr.va_size;
- while(size) {
- error = vn_rdwr(UIO_WRITE, data_vp,
- (caddr_t)buffer, size, resid_off,
- UIO_SYSSPACE, IO_NODELOCKED,
- p->p_ucred, &resid, p);
- if(error) {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- printf("bsd_write_page_cache_file: Can't write header %x\n", user);
- bsd_close_page_cache_files(
- uid_files);
- thread_funnel_set(
- kernel_flock, funnel_state);
- return error;
- }
- buffer += size-resid;
- resid_off += size-resid;
- size = resid;
- }
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return 0;
- }
- /* Someone else wrote a twin profile before us */
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return 0;
- } else {
- VOP_UNLOCK(names_vp, 0, p);
- VOP_UNLOCK(data_vp, 0, p);
- bsd_close_page_cache_files(uid_files);
- thread_funnel_set(kernel_flock, funnel_state);
- return EINVAL;
+ if (shared_region != NULL) {
+ vm_shared_region_deallocate(shared_region);
}
-
-}
-
-int
-prepare_profile_database(int user)
-{
- char *cache_path = "/var/vm/app_profile/";
- struct proc *p;
- int error;
- int resid;
- off_t resid_off;
- unsigned int lru;
- vm_size_t size;
- struct vnode *names_vp;
- struct vnode *data_vp;
- vm_offset_t names_buf;
- vm_offset_t buf_ptr;
+ SHARED_REGION_TRACE_DEBUG(
+ ("shared_region: %p [%d(%s)] <- map\n",
+ current_thread(), p->p_pid, p->p_comm));
- int profile_names_length;
- int profile_data_length;
- char *profile_data_string;
- char *profile_names_string;
- char *substring;
+ return error;
+}
- struct vattr vattr;
- struct profile_names_header *profile_header;
- kern_return_t ret;
+/* sysctl overflow room */
- struct nameidata nd_names;
- struct nameidata nd_data;
+/* vm_page_free_target is provided as a makeshift solution for applications that want to
+ allocate buffer space, possibly purgeable memory, but not cause inactive pages to be
+ reclaimed. It allows the app to calculate how much memory is free outside the free target. */
+extern unsigned int vm_page_free_target;
+SYSCTL_INT(_vm, OID_AUTO, vm_page_free_target, CTLFLAG_RD,
+ &vm_page_free_target, 0, "Pageout daemon free target");
- int i;
+extern unsigned int vm_memory_pressure;
+SYSCTL_INT(_vm, OID_AUTO, memory_pressure, CTLFLAG_RD,
+ &vm_memory_pressure, 0, "Memory pressure indicator");
- p = current_proc();
+static int
+vm_ctl_page_free_wanted SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ unsigned int page_free_wanted;
- ret = kmem_alloc(kernel_map,
- (vm_offset_t *)&profile_data_string, PATH_MAX);
+ page_free_wanted = mach_vm_ctl_page_free_wanted();
+ return SYSCTL_OUT(req, &page_free_wanted, sizeof (page_free_wanted));
+}
+SYSCTL_PROC(_vm, OID_AUTO, page_free_wanted,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_LOCKED,
+ 0, 0, vm_ctl_page_free_wanted, "I", "");
+
+extern unsigned int vm_page_purgeable_count;
+SYSCTL_INT(_vm, OID_AUTO, page_purgeable_count, CTLFLAG_RD,
+ &vm_page_purgeable_count, 0, "Purgeable page count");
+
+extern unsigned int vm_page_purgeable_wired_count;
+SYSCTL_INT(_vm, OID_AUTO, page_purgeable_wired_count, CTLFLAG_RD,
+ &vm_page_purgeable_wired_count, 0, "Wired purgeable page count");
+
+SYSCTL_INT(_vm, OID_AUTO, page_reusable_count, CTLFLAG_RD,
+ &vm_page_stats_reusable.reusable_count, 0, "Reusable page count");
+SYSCTL_QUAD(_vm, OID_AUTO, reusable_success, CTLFLAG_RD,
+ &vm_page_stats_reusable.reusable_pages_success, "");
+SYSCTL_QUAD(_vm, OID_AUTO, reusable_failure, CTLFLAG_RD,
+ &vm_page_stats_reusable.reusable_pages_failure, "");
+SYSCTL_QUAD(_vm, OID_AUTO, reusable_shared, CTLFLAG_RD,
+ &vm_page_stats_reusable.reusable_pages_shared, "");
+SYSCTL_QUAD(_vm, OID_AUTO, all_reusable_calls, CTLFLAG_RD,
+ &vm_page_stats_reusable.all_reusable_calls, "");
+SYSCTL_QUAD(_vm, OID_AUTO, partial_reusable_calls, CTLFLAG_RD,
+ &vm_page_stats_reusable.partial_reusable_calls, "");
+SYSCTL_QUAD(_vm, OID_AUTO, reuse_success, CTLFLAG_RD,
+ &vm_page_stats_reusable.reuse_pages_success, "");
+SYSCTL_QUAD(_vm, OID_AUTO, reuse_failure, CTLFLAG_RD,
+ &vm_page_stats_reusable.reuse_pages_failure, "");
+SYSCTL_QUAD(_vm, OID_AUTO, all_reuse_calls, CTLFLAG_RD,
+ &vm_page_stats_reusable.all_reuse_calls, "");
+SYSCTL_QUAD(_vm, OID_AUTO, partial_reuse_calls, CTLFLAG_RD,
+ &vm_page_stats_reusable.partial_reuse_calls, "");
+SYSCTL_QUAD(_vm, OID_AUTO, can_reuse_success, CTLFLAG_RD,
+ &vm_page_stats_reusable.can_reuse_success, "");
+SYSCTL_QUAD(_vm, OID_AUTO, can_reuse_failure, CTLFLAG_RD,
+ &vm_page_stats_reusable.can_reuse_failure, "");
- if(ret) {
- return ENOMEM;
- }
- /* Split the buffer in half since we know the size of */
- /* our file path and our allocation is adequate for */
- /* both file path names */
- profile_names_string = profile_data_string + (PATH_MAX/2);
-
-
- strcpy(profile_data_string, cache_path);
- strcpy(profile_names_string, cache_path);
- profile_names_length = profile_data_length
- = strlen(profile_data_string);
- substring = profile_data_string + profile_data_length;
- sprintf(substring, "%x_data", user);
- substring = profile_names_string + profile_names_length;
- sprintf(substring, "%x_names", user);
-
- /* We now have the absolute file names */
-
- ret = kmem_alloc(kernel_map,
- (vm_offset_t *)&names_buf, 4 * PAGE_SIZE);
- if(ret) {
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- return ENOMEM;
- }
+int
+vm_pressure_monitor(
+ __unused struct proc *p,
+ struct vm_pressure_monitor_args *uap,
+ int *retval)
+{
+ kern_return_t kr;
+ uint32_t pages_reclaimed;
+ uint32_t pages_wanted;
- NDINIT(&nd_names, LOOKUP, FOLLOW,
- UIO_SYSSPACE, profile_names_string, p);
- NDINIT(&nd_data, LOOKUP, FOLLOW,
- UIO_SYSSPACE, profile_data_string, p);
-
- if (error = vn_open(&nd_data,
- O_CREAT | O_EXCL | FWRITE, S_IRUSR|S_IWUSR)) {
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- return 0;
- }
+ kr = mach_vm_pressure_monitor(
+ (boolean_t) uap->wait_for_pressure,
+ uap->nsecs_monitored,
+ (uap->pages_reclaimed) ? &pages_reclaimed : NULL,
+ &pages_wanted);
- data_vp = nd_data.ni_vp;
- VOP_UNLOCK(data_vp, 0, p);
-
- if (error = vn_open(&nd_names,
- O_CREAT | O_EXCL | FWRITE, S_IRUSR|S_IWUSR)) {
- printf("prepare_profile_database: Can't create CacheNames %s\n",
- profile_data_string);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- vrele(data_vp);
- return error;
+ switch (kr) {
+ case KERN_SUCCESS:
+ break;
+ case KERN_ABORTED:
+ return EINTR;
+ default:
+ return EINVAL;
}
- names_vp = nd_names.ni_vp;
-
-
- /* Write Header for new names file */
-
- profile_header = (struct profile_names_header *)names_buf;
-
- profile_header->number_of_profiles = 0;
- profile_header->user_id = user;
- profile_header->version = 1;
- profile_header->element_array =
- sizeof(struct profile_names_header);
- profile_header->spare1 = 0;
- profile_header->spare2 = 0;
- profile_header->spare3 = 0;
-
- size = sizeof(struct profile_names_header);
- buf_ptr = (vm_offset_t)profile_header;
- resid_off = 0;
-
- while(size) {
- error = vn_rdwr(UIO_WRITE, names_vp,
- (caddr_t)buf_ptr, size, resid_off,
- UIO_SYSSPACE, IO_NODELOCKED,
- p->p_ucred, &resid, p);
- if(error) {
- printf("prepare_profile_database: Can't write header %s\n", profile_names_string);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string,
- PATH_MAX);
- vput(names_vp);
- vrele(data_vp);
- return error;
+ if (uap->pages_reclaimed) {
+ if (copyout((void *)&pages_reclaimed,
+ uap->pages_reclaimed,
+ sizeof (pages_reclaimed)) != 0) {
+ return EFAULT;
}
- buf_ptr += size-resid;
- resid_off += size-resid;
- size = resid;
}
- VATTR_NULL(&vattr);
- vattr.va_uid = user;
- error = VOP_SETATTR(names_vp, &vattr, p->p_cred->pc_ucred, p);
- if(error) {
- printf("prepare_profile_database: "
- "Can't set user %s\n", profile_names_string);
- }
- vput(names_vp);
-
- error = vn_lock(data_vp, LK_EXCLUSIVE | LK_RETRY, p);
- if(error) {
- vrele(data_vp);
- printf("prepare_profile_database: cannot lock data file %s\n",
- profile_data_string);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
- }
- VATTR_NULL(&vattr);
- vattr.va_uid = user;
- error = VOP_SETATTR(data_vp, &vattr, p->p_cred->pc_ucred, p);
- if(error) {
- printf("prepare_profile_database: "
- "Can't set user %s\n", profile_data_string);
- }
-
- vput(data_vp);
- kmem_free(kernel_map,
- (vm_offset_t)profile_data_string, PATH_MAX);
- kmem_free(kernel_map,
- (vm_offset_t)names_buf, 4 * PAGE_SIZE);
+ *retval = (int) pages_wanted;
return 0;
-
}