#define AUE_WATCHEVENT AUE_NULL
#define AUE_WORKQOPEN AUE_NULL
#define AUE_WORKQOPS AUE_NULL
+#define AUE_PERSONA AUE_NULL
+#define AUE_USRCTL AUE_NULL
#endif /* !_BSM_AUDIT_KEVENTS_H_ */
OPTIONS/fdesc optional fdesc
OPTIONS/fifo optional fifo
OPTIONS/devfs optional devfs
+OPTIONS/routefs optional routefs
OPTIONS/crypto optional crypto
OPTIONS/journaling optional journaling
OPTIONS/hfs_compression optional hfs_compression
bsd/miscfs/mockfs/mockfs_vfsops.c optional mockfs
bsd/miscfs/mockfs/mockfs_vnops.c optional mockfs
+bsd/miscfs/routefs/routefs_ops.c optional routefs
+
bsd/kern/decmpfs.c standard
bsd/net/net_stubs.c standard
bsd/kern/kern_asl.c standard
bsd/kern/kern_malloc.c standard
bsd/kern/kern_mman.c standard
+bsd/kern/kern_persona.c standard
bsd/kern/kern_physio.c standard
bsd/kern/kern_priv.c standard
bsd/kern/kern_proc.c standard
bsd/kern/sys_socket.c optional sockets
bsd/kern/sys_domain.c optional sockets
bsd/kern/sys_coalition.c optional config_coalitions
+bsd/kern/sys_persona.c optional config_personas
bsd/kern/sys_work_interval.c standard
./syscalls.c standard
bsd/kern/tty.c standard
/*
* Check for a valid change to a users allocation.
- * Issue an error message if appropriate.
+ * Issue an error message and vfs event if appropriate.
*/
int
hfs_chkdqchg(cp, change, cred, type)
register struct dquot *dq = cp->c_dquot[type];
u_int64_t ncurbytes;
struct vnode *vp = cp->c_vp ? cp->c_vp : cp->c_rsrc_vp;
-
+
+ fsid_t fsid;
+ fsid.val[0] = VTOHFS(vp)->hfs_raw_dev;
+ fsid.val[1] = vfs_typenum(VTOVFS(vp));
+
dqlock(dq);
ncurbytes = dq->dq_curbytes + change;
quotatypes[type]);
#endif
dq->dq_flags |= DQ_BLKS;
+ vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
}
dqunlock(dq);
printf("\nhfs: warning, %s %s\n",
quotatypes[type], "disk quota exceeded");
#endif
+ vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
dqunlock(dq);
return (0);
"disk quota exceeded for too long");
#endif
dq->dq_flags |= DQ_BLKS;
+ vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
}
dqunlock(dq);
{
u_int32_t ncurinodes;
+ fsid_t fsid;
+ fsid.val[0] = hfsmp->hfs_raw_dev;
+ fsid.val[1] = vfs_typenum(HFSTOVFS(hfsmp));
+
dqlock(dq);
ncurinodes = dq->dq_curinodes + change;
if ((dq->dq_flags & DQ_INODS) == 0 &&
uid == kauth_cred_getuid(cred)) {
dq->dq_flags |= DQ_INODS;
+ vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
}
dqunlock(dq);
microuptime(&tv);
if (dq->dq_curinodes < dq->dq_isoftlimit) {
dq->dq_itime = tv.tv_sec + hfsmp->hfs_qfiles[type].qf_itime;
+ vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
dqunlock(dq);
return (0);
}
if (((dq->dq_flags & DQ_INODS) == 0) &&
(uid == kauth_cred_getuid(cred))) {
dq->dq_flags |= DQ_INODS;
+ vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL);
}
dqunlock(dq);
LIST_INSERT_HEAD(SESSHASH(0), &session0, s_hash);
proc_list_unlock();
+#if CONFIG_PERSONAS
+ kernproc->p_persona = NULL;
+#endif
+
kernproc->task = kernel_task;
kernproc->p_stat = SRUN;
need_kds_wakeup = TRUE;
}
lck_spin_unlock(kdw_spin_lock);
-
- ml_set_interrupts_enabled(s);
-
- if (need_kds_wakeup == TRUE)
- wakeup(&kds_waiter);
}
+
+ ml_set_interrupts_enabled(s);
+
+ if (need_kds_wakeup == TRUE)
+ wakeup(&kds_waiter);
}
}
#if CONFIG_EXT_RESOLVER == 0
/*
- * If there's no resolver, short-circuit the kauth_cred_x2y() lookups.
+ * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
*/
static __inline int
-kauth_cred_cache_lookup(__unused int from, __unused int to,
- __unused void *src, __unused void *dst)
+kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
{
- return (EWOULDBLOCK);
-
+ /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
+ static const uuid_t _user_compat_prefix = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
+ static const uuid_t _group_compat_prefix = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
+#define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
+
+ assert(from != to);
+
+ switch (from) {
+ case KI_VALID_UID: {
+ id_t uid = htonl(*(id_t *)src);
+
+ if (to == KI_VALID_GUID) {
+ uint8_t *uu = dst;
+ memcpy(uu, _user_compat_prefix, sizeof(_user_compat_prefix));
+ memcpy(&uu[COMPAT_PREFIX_LEN], &uid, sizeof(uid));
+ return (0);
+ }
+ break;
+ }
+ case KI_VALID_GID: {
+ id_t gid = htonl(*(id_t *)src);
+
+ if (to == KI_VALID_GUID) {
+ uint8_t *uu = dst;
+ memcpy(uu, _group_compat_prefix, sizeof(_group_compat_prefix));
+ memcpy(&uu[COMPAT_PREFIX_LEN], &gid, sizeof(gid));
+ return (0);
+ }
+ break;
+ }
+ case KI_VALID_GUID: {
+ const uint8_t *uu = src;
+
+ if (to == KI_VALID_UID) {
+ if (memcmp(uu, _user_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
+ id_t uid;
+ memcpy(&uid, &uu[COMPAT_PREFIX_LEN], sizeof(uid));
+ *(id_t *)dst = ntohl(uid);
+ return (0);
+ }
+ } else if (to == KI_VALID_GID) {
+ if (memcmp(uu, _group_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
+ id_t gid;
+ memcpy(&gid, &uu[COMPAT_PREFIX_LEN], sizeof(gid));
+ *(id_t *)dst = ntohl(gid);
+ return (0);
+ }
+ }
+ break;
+ }
+ default:
+ /* NOT IMPLEMENTED */
+ break;
+ }
+ return (ENOENT);
}
#endif
*resultp = 1;
break;
default:
-#if CONFIG_EXT_RESOLVER
{
- struct kauth_identity ki;
gid_t gid;
-#if 6603280
+#if CONFIG_EXT_RESOLVER
+ struct kauth_identity ki;
+
/*
* Grovel the identity cache looking for this GUID.
* If we find it, and it is for a user record, return
return (0);
}
}
-#endif /* 6603280 */
+#endif /* CONFIG_EXT_RESOLVER */
/*
* Attempt to translate the GUID to a GID. Even if
* this fails, we will have primed the cache if it is
error = 0;
}
} else {
+#if CONFIG_EXT_RESOLVER
do_check:
+#endif /* CONFIG_EXT_RESOLVER */
error = kauth_cred_ismember_gid(cred, gid, resultp);
}
}
-#else /* CONFIG_EXT_RESOLVER */
- error = ENOENT;
-#endif /* CONFIG_EXT_RESOLVER */
break;
}
return(error);
p->p_pid);
proc_lock(p);
p->p_csflags &= ~(CS_KILL | CS_HARD);
+ if (p->p_csflags & CS_VALID)
+ {
+ p->p_csflags |= CS_DEBUGGED;
+ }
proc_unlock(p);
vm_map_switch_protect(get_task_map(p->task), FALSE);
#endif
* Copyright (c) 2014 Apple 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
* 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,
* 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_OSREFERENCE_LICENSE_HEADER_END@
*/
return error;
}
-void
-csr_set_allow_all(int value)
-{
- csr_allow_all = !!value; // force value to 0 or 1
-}
-
/*
* Syscall stubs
*/
}
}
- const CS_SuperBlob *super_blob = (void *)t_blob->csb_mem_kaddr;
- const CS_CodeDirectory *cd = findCodeDirectory(super_blob,
- (const char *) super_blob,
- (const char *) super_blob + t_blob->csb_mem_size);
+ const CS_CodeDirectory *cd = t_blob->csb_cd;
if (cd == NULL) {
error = ENOENT;
goto outdrop;
error = filt_timervalidate(kn);
if (error != 0) {
filt_timerunlock();
+ thread_call_free(callout);
return (error);
}
struct kqueue *kq;
struct fileproc *fp = NULL;
struct kevent_internal_s kev;
- int error, noutputs;
+ int error = 0, noutputs;
struct timeval atv;
#if 1
#include <sys/signal.h>
#include <sys/aio_kern.h>
#include <sys/sysproto.h>
+#include <sys/persona.h>
#if SYSV_SHM
#include <sys/shm_internal.h> /* shmexec() */
#endif
#include <kern/assert.h>
#include <kern/task.h>
#include <kern/coalition.h>
+#include <kern/kalloc.h>
#if CONFIG_MACF
#include <security/mac.h>
#endif
extern struct savearea *get_user_regs(thread_t);
+extern kern_return_t machine_thread_neon_state_initialize(thread_t thread);
__attribute__((noinline)) int __EXEC_WAITING_ON_TASKGATED_CODE_SIGNATURE_UPCALL__(mach_port_t task_access_port, int32_t new_pid);
return (error);
}
+static int
+activate_thread_state(thread_t thread, load_result_t *result)
+{
+ int ret;
+
+ ret = thread_state_initialize(thread);
+ if (ret != KERN_SUCCESS) {
+ return ret;
+ }
+
+
+ if (result->threadstate) {
+ uint32_t *ts = result->threadstate;
+ uint32_t total_size = result->threadstate_sz;
+
+ while (total_size > 0) {
+ uint32_t flavor = *ts++;
+ uint32_t size = *ts++;
+
+ ret = thread_setstatus(thread, flavor, (thread_state_t)ts, size);
+ if (ret) {
+ return ret;
+ }
+ ts += size;
+ total_size -= (size + 2) * sizeof(uint32_t);
+ }
+ }
+
+ thread_setentrypoint(thread, result->entry_point);
+
+ return KERN_SUCCESS;
+}
+
+
/*
* exec_mach_imgact
*
/*
* Actually load the image file we previously decided to load.
*/
- lret = load_machfile(imgp, mach_header, thread, map, &load_result);
+ lret = load_machfile(imgp, mach_header, thread, &map, &load_result);
if (lret != LOAD_SUCCESS) {
error = load_return_to_errno(lret);
p->p_cpusubtype = imgp->ip_origcpusubtype;
proc_unlock(p);
- vm_map_set_user_wire_limit(get_task_map(task), p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
+ vm_map_set_user_wire_limit(map, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
/*
* Set code-signing flags if this binary is signed, or if parent has
if (p->p_csflags & CS_EXEC_SET_INSTALLER)
imgp->ip_csflags |= CS_INSTALLER;
-
/*
* Set up the system reserved areas in the new address space.
*/
- vm_map_exec(get_task_map(task),
- task,
- (void *) p->p_fd->fd_rdir,
- cpu_type());
-
+ vm_map_exec(map, task, (void *)p->p_fd->fd_rdir, cpu_type());
+
/*
* Close file descriptors which specify close-on-exec.
*/
*/
error = exec_handle_sugid(imgp);
if (error) {
+ if (spawn || !vfexec) {
+ vm_map_deallocate(map);
+ }
goto badtoolate;
- }
+ }
+
+ /*
+ * Commit to new map.
+ *
+ * Swap the new map for the old, which consumes our new map reference but
+ * each leaves us responsible for the old_map reference. That lets us get
+ * off the pmap associated with it, and then we can release it.
+ */
+ if (!vfexec) {
+ old_map = swap_task_map(task, thread, map, !spawn);
+ vm_map_deallocate(old_map);
+ }
+
+ lret = activate_thread_state(thread, &load_result);
+ if (lret != KERN_SUCCESS) {
+ goto badtoolate;
+ }
/*
* deal with voucher on exec-calling thread.
/* Set the stack */
thread_setuserstack(thread, ap);
}
-
+
if (load_result.dynlinker) {
uint64_t ap;
int new_ptr_size = (imgp->ip_flags & IMGPF_IS_64BIT) ? 8 : 4;
if (vfexec || spawn) {
vm_map_switch(old_map);
}
- /* Set the entry point */
- thread_setentrypoint(thread, load_result.entry_point);
/* Stop profiling */
stopprofclock(p);
thread_deallocate(thread);
}
+ if (load_result.threadstate) {
+ kfree(load_result.threadstate, load_result.threadstate_sz);
+ load_result.threadstate = NULL;
+ }
+
bad:
return(error);
}
* namei:???
* vn_rdwr:??? [anything vn_rdwr can return]
* <ex_imgact>:??? [anything an imgact can return]
+ * EDEADLK Process is being terminated
*/
static int
exec_activate_image(struct image_params *imgp)
*/
proc_lock(p);
if (p->p_lflag & P_LEXIT) {
+ error = EDEADLK;
proc_unlock(p);
goto bad_notrans;
}
(uintptr_t)ndp->ni_vp, 0);
}
+ if (error == 0) {
+ /*
+ * Reset atm context from task
+ */
+ task_atm_reset(p->task);
+
+ /*
+ * Reset old bank context from task
+ */
+ task_bank_reset(p->task);
+ }
bad:
proc_transend(p, 0);
}
#endif
+#if CONFIG_PERSONAS
+static int spawn_validate_persona(struct _posix_spawn_persona_info *px_persona)
+{
+ int error = 0;
+ struct persona *persona = NULL;
+ int verify = px_persona->pspi_flags & POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
+
+ /*
+ * TODO: rdar://problem/19981151
+ * Add entitlement check!
+ */
+ if (!kauth_cred_issuser(kauth_cred_get()))
+ return EPERM;
+
+ persona = persona_lookup(px_persona->pspi_id);
+ if (!persona) {
+ error = ESRCH;
+ goto out;
+ }
+
+ if (verify) {
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_UID) {
+ if (px_persona->pspi_uid != persona_get_uid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GID) {
+ if (px_persona->pspi_gid != persona_get_gid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ int ngroups = 0;
+ gid_t groups[NGROUPS_MAX];
+
+ if (persona_get_groups(persona, &ngroups, groups,
+ px_persona->pspi_ngroups) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ if (ngroups != (int)px_persona->pspi_ngroups) {
+ error = EINVAL;
+ goto out;
+ }
+ while (ngroups--) {
+ if (px_persona->pspi_groups[ngroups] != groups[ngroups]) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (px_persona->pspi_gmuid != persona_get_gmuid(persona)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (persona)
+ persona_put(persona);
+
+ return error;
+}
+
+static int spawn_persona_adopt(proc_t p, struct _posix_spawn_persona_info *px_persona)
+{
+ int ret;
+ kauth_cred_t cred;
+ struct persona *persona = NULL;
+ int override = !!(px_persona->pspi_flags & POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
+
+ if (!override)
+ return persona_proc_adopt_id(p, px_persona->pspi_id, NULL);
+
+ /*
+ * we want to spawn into the given persona, but we want to override
+ * the kauth with a different UID/GID combo
+ */
+ persona = persona_lookup(px_persona->pspi_id);
+ if (!persona)
+ return ESRCH;
+
+ cred = persona_get_cred(persona);
+ if (!cred) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_UID) {
+ cred = kauth_cred_setresuid(cred,
+ px_persona->pspi_uid,
+ px_persona->pspi_uid,
+ px_persona->pspi_uid,
+ KAUTH_UID_NONE);
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GID) {
+ cred = kauth_cred_setresgid(cred,
+ px_persona->pspi_gid,
+ px_persona->pspi_gid,
+ px_persona->pspi_gid);
+ }
+
+ if (px_persona->pspi_flags & POSIX_SPAWN_PERSONA_GROUPS) {
+ cred = kauth_cred_setgroups(cred,
+ px_persona->pspi_groups,
+ px_persona->pspi_ngroups,
+ px_persona->pspi_gmuid);
+ }
+
+ ret = persona_proc_adopt(p, persona, cred);
+
+out:
+ persona_put(persona);
+ return ret;
+}
+#endif
+
void
proc_set_return_wait(proc_t p)
{
int portwatch_count = 0;
ipc_port_t * portwatch_ports = NULL;
vm_size_t px_sa_offset = offsetof(struct _posix_spawnattr, psa_ports);
+#if CONFIG_PERSONAS
+ struct _posix_spawn_persona_info *px_persona = NULL;
+#endif
/*
* Allocate a big chunk for locals instead of using stack since these
imgp->ip_flags = (is_64 ? IMGPF_WAS_64BIT : IMGPF_NONE);
imgp->ip_seg = (is_64 ? UIO_USERSPACE64 : UIO_USERSPACE32);
imgp->ip_mac_return = 0;
- imgp->ip_reserved = NULL;
+ imgp->ip_px_persona = NULL;
if (uap->adesc != USER_ADDR_NULL) {
if(is_64) {
px_args.mac_extensions = CAST_USER_ADDR_T(px_args32.mac_extensions);
px_args.coal_info_size = px_args32.coal_info_size;
px_args.coal_info = CAST_USER_ADDR_T(px_args32.coal_info);
- px_args.reserved = 0;
- px_args.reserved_size = 0;
+ px_args.persona_info_size = px_args32.persona_info_size;
+ px_args.persona_info = CAST_USER_ADDR_T(px_args32.persona_info);
}
if (error)
goto bad;
goto bad;
}
}
+#if CONFIG_PERSONAS
+ /* copy in the persona info */
+ if (px_args.persona_info_size != 0 && px_args.persona_info != 0) {
+ /* for now, we need the exact same struct in user space */
+ if (px_args.persona_info_size != sizeof(*px_persona)) {
+ error = ERANGE;
+ goto bad;
+ }
+
+ MALLOC(px_persona, struct _posix_spawn_persona_info *, px_args.persona_info_size, M_TEMP, M_WAITOK|M_ZERO);
+ if (px_persona == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ imgp->ip_px_persona = px_persona;
+ if ((error = copyin(px_args.persona_info, px_persona,
+ px_args.persona_info_size)) != 0)
+ goto bad;
+ if ((error = spawn_validate_persona(px_persona)) != 0)
+ goto bad;
+ }
+#endif
#if CONFIG_MACF
if (px_args.mac_extensions_size != 0) {
if ((error = spawn_copyin_macpolicyinfo(&px_args, (_posix_spawn_mac_policy_extensions_t *)&imgp->ip_px_smpx)) != 0)
do_fork1:
#endif /* CONFIG_COALITIONS */
+ /*
+ * note that this will implicitly inherit the
+ * caller's persona (if it exists)
+ */
error = fork1(p, &imgp->ip_new_thread, PROC_CREATE_SPAWN, coal);
#if CONFIG_COALITIONS
imgp->ip_flags |= IMGPF_SPAWN; /* spawn w/o exec */
spawn_no_exec = TRUE; /* used in later tests */
+#if CONFIG_PERSONAS
+ /*
+ * If the parent isn't in a persona (launchd), and
+ * hasn't specified a new persona for the process,
+ * then we'll put the process into the system persona
+ *
+ * TODO: this will have to be re-worked because as of
+ * now, without any launchd adoption, the resulting
+ * xpcproxy process will not have sufficient
+ * privileges to setuid/gid.
+ */
+#if 0
+ if (!proc_has_persona(p) && imgp->ip_px_persona == NULL) {
+ MALLOC(px_persona, struct _posix_spawn_persona_info *,
+ sizeof(*px_persona), M_TEMP, M_WAITOK|M_ZERO);
+ if (px_persona == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ px_persona->pspi_id = persona_get_id(g_system_persona);
+ imgp->ip_px_persona = px_persona;
+ }
+#endif /* 0 */
+#endif /* CONFIG_PERSONAS */
}
if (spawn_no_exec) {
}
}
+#if CONFIG_PERSONAS
+ if (spawn_no_exec && imgp->ip_px_persona != NULL) {
+ /*
+ * If we were asked to spawn a process into a new persona,
+ * do the credential switch now (which may override the UID/GID
+ * inherit done just above). It's important to do this switch
+ * before image activation both for reasons stated above, and
+ * to ensure that the new persona has access to the image/file
+ * being executed.
+ */
+ error = spawn_persona_adopt(p, imgp->ip_px_persona);
+ if (error != 0)
+ goto bad;
+ }
+#endif /* CONFIG_PERSONAS */
#if !SECURE_KERNEL
/*
* Disable ASLR for the spawned process.
} else if (error == 0) {
/* reset the importance attribute from our previous life */
task_importance_reset(p->task);
+ }
- /* reset atm context from task */
- task_atm_reset(p->task);
+ if (error == 0) {
+ /*
+ * We need to initialize the bank context behind the protection of
+ * the proc_trans lock to prevent a race with exit. We can't do this during
+ * exec_activate_image because task_bank_init checks entitlements that
+ * aren't loaded until subsequent calls (including exec_resettextvp).
+ */
+ error = proc_transstart(p, 0, 0);
+
+ if (error == 0) {
+ task_bank_init(p->task);
+ proc_transend(p, 0);
+ }
}
+
/*
* Apply the spawnattr policy, apptype (which primes the task for importance donation),
* and bind any portwatch ports to the new task.
FREE(imgp->ip_px_sfa, M_TEMP);
if (imgp->ip_px_spa != NULL)
FREE(imgp->ip_px_spa, M_TEMP);
+#if CONFIG_PERSONAS
+ if (imgp->ip_px_persona != NULL)
+ FREE(imgp->ip_px_persona, M_TEMP);
+#endif
#if CONFIG_MACF
if (imgp->ip_px_smpx != NULL)
spawn_free_macpolicyinfo(imgp->ip_px_smpx);
if (imgp->ip_scriptlabelp)
mac_vnode_label_free(imgp->ip_scriptlabelp);
#endif
+
+ if (!error) {
+ /*
+ * We need to initialize the bank context behind the protection of
+ * the proc_trans lock to prevent a race with exit. We can't do this during
+ * exec_activate_image because task_bank_init checks entitlements that
+ * aren't loaded until subsequent calls (including exec_resettextvp).
+ */
+ error = proc_transstart(p, 0, 0);
+
+ if (!error) {
+ task_bank_init(p->task);
+ proc_transend(p, 0);
+ }
+ }
+
if (!error) {
/* Sever any extant thread affinity */
thread_affinity_exec(current_thread());
- thread_t main_thread = (imgp->ip_new_thread != NULL) ? imgp->ip_new_thread : current_thread();
+ thread_t main_thread = (imgp->ip_new_thread != NULL) ? imgp->ip_new_thread : current_thread();
task_set_main_thread_qos(p->task, main_thread);
/* reset task importance */
task_importance_reset(p->task);
- /* reset atm context from task */
- task_atm_reset(p->task);
-
DTRACE_PROC(exec__success);
#if CONFIG_DTRACE
* modifying any others sharing it.
*/
if (mac_transition) {
+ /*
+ * This hook may generate upcalls that require
+ * importance donation from the kernel.
+ * (23925818)
+ */
+ thread_t thread = current_thread();
+ thread_enable_send_importance(thread, TRUE);
kauth_proc_label_update_execve(p,
imgp->ip_vfs_context,
imgp->ip_vp,
imgp->ip_px_smpx,
&disjoint_cred, /* will be non zero if disjoint */
&label_update_return);
+ thread_enable_send_importance(thread, FALSE);
if (disjoint_cred) {
/*
#include <sys/shm_internal.h> /* shmexit */
#endif
#include <sys/acct.h> /* acct_process */
+#if CONFIG_PERSONAS
+#include <sys/persona.h>
+#endif
#include <security/audit/audit.h>
#include <bsm/audit_kevents.h>
TASK_POLICY_TERMINATED, TASK_POLICY_ENABLE);
proc_lock(p);
- error = proc_transstart(p, 1, ((jetsam_flags & P_JETSAM_VNODE) ? 1 : 0));
+ error = proc_transstart(p, 1, (((jetsam_flags & P_JETSAM_MASK) == P_JETSAM_VNODE) ? 1 : 0));
if (error == EDEADLK) {
/* Temp: If deadlock error, then it implies multithreaded exec is
* in progress. Instread of letting exit continue and
* and refernce is dropped after these calls down below
* (locking protection is provided by list lock held in chgproccnt)
*/
-
+#if CONFIG_PERSONAS
+ /*
+ * persona_proc_drop calls chgproccnt(-1) on the persona uid,
+ * and (+1) on the child->p_ucred uid
+ */
+ persona_proc_drop(child);
+#endif
(void)chgproccnt(kauth_cred_getruid(child->p_ucred), -1);
/*
#include <sys/acct.h>
#include <sys/codesign.h>
#include <sys/sysproto.h>
-
+#if CONFIG_PERSONAS
+#include <sys/persona.h>
+#endif
#if CONFIG_DTRACE
/* Do not include dtrace.h, it redefines kmem_[alloc/free] */
extern void dtrace_fasttrap_fork(proc_t, proc_t);
* always less than what an rlim_t can hold.
* (locking protection is provided by list lock held in chgproccnt)
*/
-
count = chgproccnt(uid, 1);
if (uid != 0 &&
(rlim_t)count > parent_proc->p_rlimit[RLIMIT_NPROC].rlim_cur) {
}
#endif /* CONFIG_DTRACE */
+ if (!spawn) {
+ /*
+ * Of note, we need to initialize the bank context behind
+ * the protection of the proc_trans lock to prevent a race with exit.
+ */
+ task_bank_init(get_threadtask(child_thread));
+ }
break;
pth_proc_hashinit(child_proc);
#endif /* PSYNCH */
+#if CONFIG_PERSONAS
+ child_proc->p_persona = NULL;
+ error = persona_proc_inherit(child_proc, parent_proc);
+ if (error != 0) {
+ printf("forkproc: persona_proc_inherit failed (persona %d being destroyed?)\n", persona_get_uid(parent_proc->p_persona));
+ forkproc_free(child_proc);
+ child_proc = NULL;
+ goto bad;
+ }
+#endif
+
#if CONFIG_MEMORYSTATUS
/* Memorystatus + jetsam init */
child_proc->p_memstat_state = 0;
static int memorystatus_jld_eval_aggressive_count = 3; /* Raise the priority max after 'n' aggressive loops */
static int memorystatus_jld_eval_aggressive_priority_band_max = 15; /* Kill aggressively up through this band */
+/*
+ * A FG app can request that the aggressive jetsam mechanism display some leniency in the FG band. This 'lenient' mode is described as:
+ * --- if aggressive jetsam kills an app in the FG band and gets back >=AGGRESSIVE_JETSAM_LENIENT_MODE_THRESHOLD memory, it will stop the aggressive march further into and up the jetsam bands.
+ *
+ * RESTRICTIONS:
+ * - Such a request is respected/acknowledged only once while that 'requesting' app is in the FG band i.e. if aggressive jetsam was
+ * needed and the 'lenient' mode was deployed then that's it for this special mode while the app is in the FG band.
+ *
+ * - If the app is still in the FG band and aggressive jetsam is needed again, there will be no stop-and-check the next time around.
+ *
+ * - Also, the transition of the 'requesting' app away from the FG band will void this special behavior.
+ */
+
+#define AGGRESSIVE_JETSAM_LENIENT_MODE_THRESHOLD 25
+boolean_t memorystatus_aggressive_jetsam_lenient_allowed = FALSE;
+boolean_t memorystatus_aggressive_jetsam_lenient = FALSE;
+
#if DEVELOPMENT || DEBUG
/*
* Jetsam Loop Detection tunables.
proc_list_lock();
- if ((p->p_memstat_state & P_MEMSTAT_TERMINATED) ||
- (p->p_listflag & P_LIST_EXITED) ||
- (p->p_memstat_state & P_MEMSTAT_ERROR)) {
- proc_list_unlock();
- proc_rele(p);
- return FALSE;
- }
-
- p->p_memstat_state |= P_MEMSTAT_TERMINATED;
-
if (memorystatus_jetsam_snapshot_count == 0) {
memorystatus_init_jetsam_snapshot_locked(NULL,0);
}
int kill_count = 0;
unsigned int i = 0;
int32_t aPid_ep = 0;
+ unsigned int memorystatus_level_snapshot = 0;
#pragma unused(any)
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_START,
memorystatus_available_pages, priority_max, 0, 0, 0);
+ memorystatus_sort_bucket(JETSAM_PRIORITY_FOREGROUND, JETSAM_SORT_DEFAULT);
+
proc_list_lock();
next_p = memorystatus_get_first_proc_locked(&i, TRUE);
aPid, (p->p_comm ? p->p_comm : "(unknown)"),
jetsam_kill_cause_name[cause], aPid_ep, memorystatus_available_pages);
+ memorystatus_level_snapshot = memorystatus_level;
+
killed = memorystatus_do_kill(p, cause);
/* Success? */
if (next_p) {
proc_rele_locked(next_p);
}
+
+ if (aPid_ep == JETSAM_PRIORITY_FOREGROUND && memorystatus_aggressive_jetsam_lenient == TRUE) {
+ if (memorystatus_level > memorystatus_level_snapshot && ((memorystatus_level - memorystatus_level_snapshot) >= AGGRESSIVE_JETSAM_LENIENT_MODE_THRESHOLD)) {
+#if DEVELOPMENT || DEBUG
+ printf("Disabling Lenient mode after one-time deployment.\n");
+#endif /* DEVELOPMENT || DEBUG */
+ memorystatus_aggressive_jetsam_lenient = FALSE;
+ break;
+ }
+ }
+
continue;
}
error = memorystatus_cmd_set_panic_bits(args->buffer, args->buffersize);
break;
#endif /* DEVELOPMENT || DEBUG */
+ case MEMORYSTATUS_CMD_AGGRESSIVE_JETSAM_LENIENT_MODE_ENABLE:
+ if (memorystatus_aggressive_jetsam_lenient_allowed == FALSE) {
+#if DEVELOPMENT || DEBUG
+ printf("Enabling Lenient Mode\n");
+#endif /* DEVELOPMENT || DEBUG */
+
+ memorystatus_aggressive_jetsam_lenient_allowed = TRUE;
+ memorystatus_aggressive_jetsam_lenient = TRUE;
+ }
+ break;
+ case MEMORYSTATUS_CMD_AGGRESSIVE_JETSAM_LENIENT_MODE_DISABLE:
+#if DEVELOPMENT || DEBUG
+ printf("Disabling Lenient mode\n");
+#endif /* DEVELOPMENT || DEBUG */
+ memorystatus_aggressive_jetsam_lenient_allowed = FALSE;
+ memorystatus_aggressive_jetsam_lenient = FALSE;
+ break;
#endif /* CONFIG_JETSAM */
case MEMORYSTATUS_CMD_PRIVILEGED_LISTENER_ENABLE:
case MEMORYSTATUS_CMD_PRIVILEGED_LISTENER_DISABLE:
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple 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. 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_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#include <sys/kernel.h>
+#include <sys/kernel_types.h>
+#include <sys/persona.h>
+
+#if CONFIG_PERSONAS
+#include <kern/assert.h>
+#include <kern/simple_lock.h>
+#include <kern/task.h>
+#include <kern/zalloc.h>
+
+#include <sys/param.h>
+#include <sys/proc_internal.h>
+#include <sys/kauth.h>
+#include <sys/proc_info.h>
+#include <sys/resourcevar.h>
+
+#define pna_info(fmt, ...) \
+ printf("%s: " fmt "\n", __func__, ## __VA_ARGS__)
+
+#define pna_err(fmt, ...) \
+ printf("ERROR[%s]: " fmt "\n", __func__, ## __VA_ARGS__)
+
+#define MAX_PERSONAS 512
+
+#define TEMP_PERSONA_ID 499
+
+#define FIRST_PERSONA_ID 501
+#define PERSONA_ID_STEP 10
+
+#define PERSONA_SYSTEM_UID ((uid_t)99)
+#define PERSONA_SYSTEM_LOGIN "system"
+
+#define PERSONA_MAGIC (0x0aa55aa0)
+#define persona_valid(p) ((p)->pna_valid == PERSONA_MAGIC)
+#define persona_mkinvalid(p) ((p)->pna_valid = ~(PERSONA_MAGIC))
+
+static LIST_HEAD(personalist, persona) all_personas;
+static uint32_t g_total_personas;
+uint32_t g_max_personas = MAX_PERSONAS;
+
+struct persona *g_system_persona = NULL;
+
+static uid_t g_next_persona_id;
+
+lck_mtx_t all_personas_lock;
+lck_attr_t *persona_lck_attr;
+lck_grp_t *persona_lck_grp;
+lck_grp_attr_t *persona_lck_grp_attr;
+
+static zone_t persona_zone;
+
+kauth_cred_t g_default_persona_cred;
+
+#define lock_personas() lck_mtx_lock(&all_personas_lock)
+#define unlock_personas() lck_mtx_unlock(&all_personas_lock)
+
+
+extern void mach_kauth_cred_uthread_update(void);
+
+void personas_bootstrap(void)
+{
+ struct posix_cred pcred;
+
+ persona_dbg("Initializing persona subsystem");
+ LIST_INIT(&all_personas);
+ g_total_personas = 0;
+
+ g_next_persona_id = FIRST_PERSONA_ID;
+
+ persona_lck_grp_attr = lck_grp_attr_alloc_init();
+ lck_grp_attr_setstat(persona_lck_grp_attr);
+
+ persona_lck_grp = lck_grp_alloc_init("personas", persona_lck_grp_attr);
+ persona_lck_attr = lck_attr_alloc_init();
+
+ lck_mtx_init(&all_personas_lock, persona_lck_grp, persona_lck_attr);
+
+ persona_zone = zinit(sizeof(struct persona),
+ MAX_PERSONAS * sizeof(struct persona),
+ MAX_PERSONAS, "personas");
+ assert(persona_zone != NULL);
+
+ /*
+ * setup the default credentials that a persona temporarily
+ * inherits (to work around kauth APIs)
+ */
+ bzero(&pcred, sizeof(pcred));
+ pcred.cr_uid = pcred.cr_ruid = pcred.cr_svuid = TEMP_PERSONA_ID;
+ pcred.cr_rgid = pcred.cr_svgid = TEMP_PERSONA_ID;
+ pcred.cr_groups[0] = TEMP_PERSONA_ID;
+ pcred.cr_ngroups = 1;
+ pcred.cr_flags = CRF_NOMEMBERD;
+ pcred.cr_gmuid = KAUTH_UID_NONE;
+
+ g_default_persona_cred = posix_cred_create(&pcred);
+ if (!g_default_persona_cred)
+ panic("couldn't create default persona credentials!");
+
+ g_system_persona = persona_alloc(PERSONA_SYSTEM_UID,
+ PERSONA_SYSTEM_LOGIN,
+ PERSONA_SYSTEM, NULL);
+ assert(g_system_persona != NULL);
+}
+
+struct persona *persona_alloc(uid_t id, const char *login, int type, int *error)
+{
+ struct persona *persona, *tmp;
+ int err = 0;
+ kauth_cred_t tmp_cred;
+ gid_t new_group;
+
+ if (!login) {
+ pna_err("Must provide a login name for a new persona!");
+ if (error)
+ *error = EINVAL;
+ return NULL;
+ }
+
+ if (type <= PERSONA_INVALID || type > PERSONA_TYPE_MAX) {
+ pna_err("Invalid type: %d", type);
+ if (error)
+ *error = EINVAL;
+ return NULL;
+ }
+
+ persona = (struct persona *)zalloc(persona_zone);
+ if (!persona) {
+ if (error)
+ *error = ENOMEM;
+ return NULL;
+ }
+
+ bzero(persona, sizeof(*persona));
+
+ if (hw_atomic_add(&g_total_personas, 1) > MAX_PERSONAS) {
+ /* too many personas! */
+ pna_err("too many active personas!");
+ err = EBUSY;
+ goto out_error;
+ }
+
+ strncpy(persona->pna_login, login, sizeof(persona->pna_login)-1);
+
+ LIST_INIT(&persona->pna_members);
+ lck_mtx_init(&persona->pna_lock, persona_lck_grp, persona_lck_attr);
+ persona->pna_refcount = 1;
+
+ /*
+ * Setup initial (temporary) kauth_cred structure
+ * We need to do this here because all kauth calls require
+ * an existing cred structure.
+ */
+ persona->pna_cred = kauth_cred_create(g_default_persona_cred);
+ if (!persona->pna_cred) {
+ pna_err("could not copy initial credentials!");
+ err = EIO;
+ goto out_error;
+ }
+
+ lock_personas();
+try_again:
+ if (id != PERSONA_ID_NONE)
+ persona->pna_id = id;
+ else
+ persona->pna_id = g_next_persona_id;
+
+ persona_dbg("Adding %d (%s) to global list...", persona->pna_id, persona->pna_login);
+
+ err = 0;
+ LIST_FOREACH(tmp, &all_personas, pna_list) {
+ if (id == PERSONA_ID_NONE && tmp->pna_id == id) {
+ /*
+ * someone else manually claimed this ID, and we're
+ * trying to allocate an ID for the caller: try again
+ */
+ g_next_persona_id += PERSONA_ID_STEP;
+ goto try_again;
+ }
+ if (strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0
+ || tmp->pna_id == id) {
+ /*
+ * Disallow use of identical login names and re-use
+ * of previously allocated persona IDs
+ */
+ err = EEXIST;
+ break;
+ }
+ }
+ if (err)
+ goto out_unlock;
+
+ /* ensure the cred has proper UID/GID defaults */
+ kauth_cred_ref(persona->pna_cred);
+ tmp_cred = kauth_cred_setuidgid(persona->pna_cred,
+ persona->pna_id,
+ persona->pna_id);
+ kauth_cred_unref(&persona->pna_cred);
+ if (tmp_cred != persona->pna_cred)
+ persona->pna_cred = tmp_cred;
+
+ if (!persona->pna_cred) {
+ err = EACCES;
+ goto out_unlock;
+ }
+
+ /* it should be a member of exactly 1 group (equal to its UID) */
+ new_group = (gid_t)persona->pna_id;
+
+ kauth_cred_ref(persona->pna_cred);
+ /* opt _out_ of memberd as a default */
+ tmp_cred = kauth_cred_setgroups(persona->pna_cred,
+ &new_group, 1, KAUTH_UID_NONE);
+ kauth_cred_unref(&persona->pna_cred);
+ if (tmp_cred != persona->pna_cred)
+ persona->pna_cred = tmp_cred;
+
+ if (!persona->pna_cred) {
+ err = EACCES;
+ goto out_unlock;
+ }
+
+ persona->pna_type = type;
+
+ /* insert the, now valid, persona into the global list! */
+ persona->pna_valid = PERSONA_MAGIC;
+ LIST_INSERT_HEAD(&all_personas, persona, pna_list);
+
+ /* if the kernel supplied the persona ID, increment for next time */
+ if (id == PERSONA_ID_NONE)
+ g_next_persona_id += PERSONA_ID_STEP;
+
+out_unlock:
+ unlock_personas();
+
+ if (err) {
+ switch (err) {
+ case EEXIST:
+ persona_dbg("Login '%s' (%d) already exists",
+ login, persona->pna_id);
+ break;
+ case EACCES:
+ persona_dbg("kauth_error for persona:%d", persona->pna_id);
+ break;
+ default:
+ persona_dbg("Unknown error:%d", err);
+ }
+ goto out_error;
+ }
+
+ return persona;
+
+out_error:
+ (void)hw_atomic_add(&g_total_personas, -1);
+ zfree(persona_zone, persona);
+ if (error)
+ *error = err;
+ return NULL;
+}
+
+int persona_invalidate(struct persona *persona)
+{
+ int error = 0;
+ if (!persona)
+ return EINVAL;
+
+ lock_personas();
+ persona_lock(persona);
+
+ if (!persona_valid(persona))
+ panic("Double-invalidation of persona %p", persona);
+
+ LIST_REMOVE(persona, pna_list);
+ if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX)
+ panic("persona ref count underflow!\n");
+ persona_mkinvalid(persona);
+
+ persona_unlock(persona);
+ unlock_personas();
+
+ return error;
+}
+
+static struct persona *persona_get_locked(struct persona *persona)
+{
+ if (persona->pna_refcount) {
+ persona->pna_refcount++;
+ return persona;
+ }
+ return NULL;
+}
+
+struct persona *persona_get(struct persona *persona)
+{
+ struct persona *ret;
+ if (!persona)
+ return NULL;
+ persona_lock(persona);
+ ret = persona_get_locked(persona);
+ persona_unlock(persona);
+
+ return ret;
+}
+
+void persona_put(struct persona *persona)
+{
+ int destroy = 0;
+
+ if (!persona)
+ return;
+
+ persona_lock(persona);
+ if (persona->pna_refcount >= 0) {
+ if (--(persona->pna_refcount) == 0)
+ destroy = 1;
+ }
+ persona_unlock(persona);
+
+ if (!destroy)
+ return;
+
+ persona_dbg("Destroying persona %s", persona_desc(persona, 0));
+
+ /* release our credential reference */
+ if (persona->pna_cred)
+ kauth_cred_unref(&persona->pna_cred);
+
+ /* remove it from the global list and decrement the count */
+ lock_personas();
+ if (persona_valid(persona)) {
+ LIST_REMOVE(persona, pna_list);
+ if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX)
+ panic("persona count underflow!\n");
+ persona_mkinvalid(persona);
+ }
+ unlock_personas();
+
+ assert(LIST_EMPTY(&persona->pna_members));
+ memset(persona, 0, sizeof(*persona));
+ zfree(persona_zone, persona);
+}
+
+uid_t persona_get_id(struct persona *persona)
+{
+ if (persona)
+ return persona->pna_id;
+ return PERSONA_ID_NONE;
+}
+
+struct persona *persona_lookup(uid_t id)
+{
+ struct persona *persona, *tmp;
+
+ persona = NULL;
+
+ /*
+ * simple, linear lookup for now: there shouldn't be too many
+ * of these in memory at any given time.
+ */
+ lock_personas();
+ LIST_FOREACH(tmp, &all_personas, pna_list) {
+ persona_lock(tmp);
+ if (tmp->pna_id == id && persona_valid(tmp)) {
+ persona = persona_get_locked(tmp);
+ persona_unlock(tmp);
+ break;
+ }
+ persona_unlock(tmp);
+ }
+ unlock_personas();
+
+ return persona;
+}
+
+int persona_find(const char *login, uid_t uid,
+ struct persona **persona, size_t *plen)
+{
+ struct persona *tmp;
+ int match = 0;
+ size_t found = 0;
+
+ if (login)
+ match++;
+ if (uid != PERSONA_ID_NONE)
+ match++;
+
+ if (match == 0)
+ return EINVAL;
+
+ persona_dbg("Searching with %d parameters (l:\"%s\", u:%d)",
+ match, login, uid);
+
+ lock_personas();
+ LIST_FOREACH(tmp, &all_personas, pna_list) {
+ int m = 0;
+ persona_lock(tmp);
+ if (login && strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0)
+ m++;
+ if (uid != PERSONA_ID_NONE && uid == tmp->pna_id)
+ m++;
+ if (m == match) {
+ if (persona && *plen > found)
+ persona[found] = persona_get_locked(tmp);
+ found++;
+ }
+#ifdef PERSONA_DEBUG
+ if (m > 0)
+ persona_dbg("ID:%d Matched %d/%d, found:%d, *plen:%d",
+ tmp->pna_id, m, match, (int)found, (int)*plen);
+#endif
+ persona_unlock(tmp);
+ }
+ unlock_personas();
+
+ *plen = found;
+ if (!found)
+ return ESRCH;
+ return 0;
+}
+
+struct persona *persona_proc_get(pid_t pid)
+{
+ struct persona *persona;
+ proc_t p = proc_find(pid);
+
+ if (!p)
+ return NULL;
+
+ proc_lock(p);
+ persona = persona_get(p->p_persona);
+ proc_unlock(p);
+
+ proc_rele(p);
+
+ return persona;
+}
+
+struct persona *current_persona_get(void)
+{
+ proc_t p = current_proc();
+ struct persona *persona;
+
+ proc_lock(p);
+ persona = persona_get(p->p_persona);
+ proc_unlock(p);
+
+ return persona;
+}
+
+/**
+ * inherit a persona from parent to child
+ */
+int persona_proc_inherit(proc_t child, proc_t parent)
+{
+ if (child->p_persona != NULL) {
+ persona_dbg("proc_inherit: child already in persona: %s",
+ persona_desc(child->p_persona, 0));
+ return -1;
+ }
+
+ /* no persona to inherit */
+ if (parent->p_persona == NULL)
+ return 0;
+
+ return persona_proc_adopt(child, parent->p_persona, parent->p_ucred);
+}
+
+int persona_proc_adopt_id(proc_t p, uid_t id, kauth_cred_t auth_override)
+{
+ int ret;
+ struct persona *persona;
+
+ persona = persona_lookup(id);
+ if (!persona)
+ return ESRCH;
+
+ ret = persona_proc_adopt(p, persona, auth_override);
+
+ /* put the reference from the lookup() */
+ persona_put(persona);
+
+ return ret;
+}
+
+
+typedef enum e_persona_reset_op {
+ PROC_REMOVE_PERSONA = 1,
+ PROC_RESET_OLD_PERSONA = 2,
+} persona_reset_op_t;
+
+/*
+ * internal cleanup routine for proc_set_cred_internal
+ *
+ */
+static struct persona *proc_reset_persona_internal(proc_t p, persona_reset_op_t op,
+ struct persona *old_persona,
+ struct persona *new_persona)
+{
+#if (DEVELOPMENT || DEBUG)
+ persona_lock_assert_held(new_persona);
+#endif
+
+ switch (op) {
+ case PROC_REMOVE_PERSONA:
+ old_persona = p->p_persona;
+ /* fall through */
+ case PROC_RESET_OLD_PERSONA:
+ break;
+ default:
+ /* invalid arguments */
+ return NULL;
+ }
+
+ /* unlock the new persona (locked on entry) */
+ persona_unlock(new_persona);
+ /* lock the old persona and the process */
+ persona_lock(old_persona);
+ proc_lock(p);
+
+ switch (op) {
+ case PROC_REMOVE_PERSONA:
+ LIST_REMOVE(p, p_persona_list);
+ p->p_persona = NULL;
+ break;
+ case PROC_RESET_OLD_PERSONA:
+ p->p_persona = old_persona;
+ LIST_INSERT_HEAD(&old_persona->pna_members, p, p_persona_list);
+ break;
+ }
+
+ proc_unlock(p);
+ persona_unlock(old_persona);
+
+ /* re-lock the new persona */
+ persona_lock(new_persona);
+ return old_persona;
+}
+
+/*
+ * Assumes persona is locked.
+ * On success, takes a reference to 'persona' and returns the
+ * previous persona the process had adopted. The caller is
+ * responsible to release the reference.
+ */
+static struct persona *proc_set_cred_internal(proc_t p, struct persona *persona,
+ kauth_cred_t auth_override, int *rlim_error)
+{
+ struct persona *old_persona = NULL;
+ kauth_cred_t my_cred, my_new_cred;
+ uid_t old_uid, new_uid;
+ int count;
+
+ /*
+ * This operation must be done under the proc trans lock
+ * by the thread which took the trans lock!
+ */
+ assert(((p->p_lflag & P_LINTRANSIT) == P_LINTRANSIT) &&
+ p->p_transholder == current_thread());
+ assert(persona != NULL);
+
+ /* no work to do if we "re-adopt" the same persona */
+ if (p->p_persona == persona)
+ return NULL;
+
+ /*
+ * If p is in a persona, then we need to remove 'p' from the list of
+ * processes in that persona. To do this, we need to drop the lock
+ * held on the incoming (new) persona and lock the old one.
+ */
+ if (p->p_persona) {
+ old_persona = proc_reset_persona_internal(p, PROC_REMOVE_PERSONA,
+ NULL, persona);
+ }
+
+ if (auth_override)
+ my_new_cred = auth_override;
+ else
+ my_new_cred = persona->pna_cred;
+
+ if (!my_new_cred)
+ panic("NULL credentials (persona:%p)", persona);
+
+ *rlim_error = 0;
+
+ kauth_cred_ref(my_new_cred);
+
+ new_uid = persona->pna_id;
+
+ /*
+ * Check to see if we will hit a proc rlimit by moving the process
+ * into the persona. If so, we'll bail early before actually moving
+ * the process or changing its credentials.
+ */
+ if (new_uid != 0 &&
+ (rlim_t)chgproccnt(new_uid, 0) > p->p_rlimit[RLIMIT_NPROC].rlim_cur) {
+ pna_err("PID:%d hit proc rlimit in new persona(%d): %s",
+ p->p_pid, new_uid, persona_desc(persona, 1));
+ *rlim_error = EACCES;
+ (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA,
+ old_persona, persona);
+ kauth_cred_unref(&my_new_cred);
+ return NULL;
+ }
+
+ /*
+ * Set the new credentials on the proc
+ */
+set_proc_cred:
+ my_cred = kauth_cred_proc_ref(p);
+ persona_dbg("proc_adopt PID:%d, %s -> %s",
+ p->p_pid,
+ persona_desc(old_persona, 1),
+ persona_desc(persona, 1));
+
+ old_uid = kauth_cred_getruid(my_cred);
+
+ if (my_cred != my_new_cred) {
+ kauth_cred_t old_cred = my_cred;
+
+ proc_ucred_lock(p);
+ /*
+ * We need to protect against a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we should
+ * restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_ucred_unlock(p);
+ kauth_cred_unref(&my_cred);
+ /* try again */
+ goto set_proc_cred;
+ }
+
+ /* update the credential and take a ref for the proc */
+ kauth_cred_ref(my_new_cred);
+ p->p_ucred = my_new_cred;
+
+ /* update cred on proc (and current thread) */
+ mach_kauth_cred_uthread_update();
+ PROC_UPDATE_CREDS_ONPROC(p);
+
+ /* drop the proc's old ref on the credential */
+ kauth_cred_unref(&old_cred);
+ proc_ucred_unlock(p);
+ }
+
+ /* drop this function's reference to the old cred */
+ kauth_cred_unref(&my_cred);
+
+ /*
+ * Update the proc count.
+ * If the UIDs are the same, then there is no work to do.
+ */
+ if (old_persona)
+ old_uid = old_persona->pna_id;
+
+ if (new_uid != old_uid) {
+ count = chgproccnt(old_uid, -1);
+ persona_dbg("Decrement %s:%d proc_count to: %d",
+ old_persona ? "Persona" : "UID", old_uid, count);
+
+ /*
+ * Increment the proc count on the UID associated with
+ * the new persona. Enforce the resource limit just
+ * as in fork1()
+ */
+ count = chgproccnt(new_uid, 1);
+ persona_dbg("Increment Persona:%d (UID:%d) proc_count to: %d",
+ new_uid, kauth_cred_getuid(my_new_cred), count);
+ }
+
+ OSBitOrAtomic(P_ADOPTPERSONA, &p->p_flag);
+
+ proc_lock(p);
+ p->p_persona = persona_get_locked(persona);
+ LIST_INSERT_HEAD(&persona->pna_members, p, p_persona_list);
+ proc_unlock(p);
+
+ kauth_cred_unref(&my_new_cred);
+
+ return old_persona;
+}
+
+int persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override)
+{
+ int error;
+ struct persona *old_persona;
+ struct session * sessp;
+
+ if (!persona)
+ return EINVAL;
+
+ persona_dbg("%d adopting Persona %d (%s)", proc_pid(p),
+ persona->pna_id, persona_desc(persona, 0));
+
+ persona_lock(persona);
+ if (!persona->pna_cred || !persona_valid(persona)) {
+ persona_dbg("Invalid persona (%s): NULL credentials!", persona_desc(persona, 1));
+ persona_unlock(persona);
+ return EINVAL;
+ }
+
+ /* the persona credentials can no longer be adjusted */
+ persona->pna_cred_locked = 1;
+
+ /*
+ * assume the persona: this may drop and re-acquire the persona lock!
+ */
+ error = 0;
+ old_persona = proc_set_cred_internal(p, persona, auth_override, &error);
+
+ /* join the process group associated with the persona */
+ if (persona->pna_pgid) {
+ uid_t uid = kauth_cred_getuid(persona->pna_cred);
+ persona_dbg(" PID:%d, pgid:%d%s",
+ p->p_pid, persona->pna_pgid,
+ persona->pna_pgid == uid ? ", new_session" : ".");
+ enterpgrp(p, persona->pna_pgid, persona->pna_pgid == uid);
+ }
+
+ /* set the login name of the session */
+ sessp = proc_session(p);
+ if (sessp != SESSION_NULL) {
+ session_lock(sessp);
+ bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME);
+ session_unlock(sessp);
+ session_rele(sessp);
+ }
+
+ persona_unlock(persona);
+
+ set_security_token(p);
+
+ /*
+ * Drop the reference to the old persona.
+ */
+ if (old_persona)
+ persona_put(old_persona);
+
+ persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
+ return error;
+}
+
+int persona_proc_drop(proc_t p)
+{
+ struct persona *persona = NULL;
+
+ persona_dbg("PID:%d, %s -> <none>", p->p_pid, persona_desc(p->p_persona, 0));
+
+ /*
+ * There are really no other credentials for us to assume,
+ * so we'll just continue running with the credentials
+ * we got from the persona.
+ */
+
+ /*
+ * the locks must be taken in reverse order here, so
+ * we have to be careful not to cause deadlock
+ */
+try_again:
+ proc_lock(p);
+ if (p->p_persona) {
+ uid_t puid, ruid;
+ if (!persona_try_lock(p->p_persona)) {
+ proc_unlock(p);
+ mutex_pause(0); /* back-off time */
+ goto try_again;
+ }
+ persona = p->p_persona;
+ LIST_REMOVE(p, p_persona_list);
+ p->p_persona = NULL;
+
+ ruid = kauth_cred_getruid(p->p_ucred);
+ puid = kauth_cred_getuid(persona->pna_cred);
+ proc_unlock(p);
+ (void)chgproccnt(ruid, 1);
+ (void)chgproccnt(puid, -1);
+ } else {
+ proc_unlock(p);
+ }
+
+ /*
+ * if the proc had a persona, then it is still locked here
+ * (preserving proper lock ordering)
+ */
+
+ if (persona) {
+ persona_unlock(persona);
+ persona_put(persona);
+ }
+
+ return 0;
+}
+
+int persona_get_type(struct persona *persona)
+{
+ int type;
+
+ if (!persona)
+ return PERSONA_INVALID;
+
+ persona_lock(persona);
+ if (!persona_valid(persona)) {
+ persona_unlock(persona);
+ return PERSONA_INVALID;
+ }
+ type = persona->pna_type;
+ persona_unlock(persona);
+
+ return type;
+}
+
+int persona_set_cred(struct persona *persona, kauth_cred_t cred)
+{
+ int ret = 0;
+ kauth_cred_t my_cred;
+ if (!persona || !cred)
+ return EINVAL;
+
+ persona_lock(persona);
+ if (!persona_valid(persona)) {
+ ret = EINVAL;
+ goto out_unlock;
+ }
+ if (persona->pna_cred_locked) {
+ ret = EPERM;
+ goto out_unlock;
+ }
+
+ /* create a new cred from the passed-in cred */
+ my_cred = kauth_cred_create(cred);
+
+ /* ensure that the UID matches the persona ID */
+ my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
+ persona->pna_id, persona->pna_id,
+ KAUTH_UID_NONE);
+
+ /* TODO: clear the saved GID?! */
+
+ /* replace the persona's cred with the new one */
+ if (persona->pna_cred)
+ kauth_cred_unref(&persona->pna_cred);
+ persona->pna_cred = my_cred;
+
+out_unlock:
+ persona_unlock(persona);
+ return ret;
+}
+
+int persona_set_cred_from_proc(struct persona *persona, proc_t proc)
+{
+ int ret = 0;
+ kauth_cred_t parent_cred, my_cred;
+ if (!persona || !proc)
+ return EINVAL;
+
+ persona_lock(persona);
+ if (!persona_valid(persona)) {
+ ret = EINVAL;
+ goto out_unlock;
+ }
+ if (persona->pna_cred_locked) {
+ ret = EPERM;
+ goto out_unlock;
+ }
+
+ parent_cred = kauth_cred_proc_ref(proc);
+
+ /* TODO: clear the saved UID/GID! */
+
+ /* create a new cred from the proc's cred */
+ my_cred = kauth_cred_create(parent_cred);
+
+ /* ensure that the UID matches the persona ID */
+ my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
+ persona->pna_id, persona->pna_id,
+ KAUTH_UID_NONE);
+
+ /* replace the persona's cred with the new one */
+ if (persona->pna_cred)
+ kauth_cred_unref(&persona->pna_cred);
+ persona->pna_cred = my_cred;
+
+ kauth_cred_unref(&parent_cred);
+
+out_unlock:
+ persona_unlock(persona);
+ return ret;
+}
+
+kauth_cred_t persona_get_cred(struct persona *persona)
+{
+ kauth_cred_t cred = NULL;
+
+ if (!persona)
+ return NULL;
+
+ persona_lock(persona);
+ if (!persona_valid(persona))
+ goto out_unlock;
+
+ if (persona->pna_cred) {
+ kauth_cred_ref(persona->pna_cred);
+ cred = persona->pna_cred;
+ }
+
+out_unlock:
+ persona_unlock(persona);
+
+ return cred;
+}
+
+uid_t persona_get_uid(struct persona *persona)
+{
+ uid_t uid = UID_MAX;
+
+ if (!persona || !persona->pna_cred)
+ return UID_MAX;
+
+ persona_lock(persona);
+ if (persona_valid(persona)) {
+ uid = kauth_cred_getuid(persona->pna_cred);
+ assert(uid == persona->pna_id);
+ }
+ persona_unlock(persona);
+
+ return uid;
+}
+
+int persona_set_gid(struct persona *persona, gid_t gid)
+{
+ int ret = 0;
+ kauth_cred_t my_cred, new_cred;
+
+ if (!persona || !persona->pna_cred)
+ return EINVAL;
+
+ persona_lock(persona);
+ if (!persona_valid(persona)) {
+ ret = EINVAL;
+ goto out_unlock;
+ }
+ if (persona->pna_cred_locked) {
+ ret = EPERM;
+ goto out_unlock;
+ }
+
+ my_cred = persona->pna_cred;
+ kauth_cred_ref(my_cred);
+ new_cred = kauth_cred_setresgid(my_cred, gid, gid, gid);
+ if (new_cred != my_cred)
+ persona->pna_cred = new_cred;
+ kauth_cred_unref(&my_cred);
+
+out_unlock:
+ persona_unlock(persona);
+ return ret;
+}
+
+gid_t persona_get_gid(struct persona *persona)
+{
+ gid_t gid = GID_MAX;
+
+ if (!persona || !persona->pna_cred)
+ return GID_MAX;
+
+ persona_lock(persona);
+ if (persona_valid(persona))
+ gid = kauth_cred_getgid(persona->pna_cred);
+ persona_unlock(persona);
+
+ return gid;
+}
+
+int persona_set_groups(struct persona *persona, gid_t *groups, int ngroups, uid_t gmuid)
+{
+ int ret = 0;
+ kauth_cred_t my_cred, new_cred;
+
+ if (!persona || !persona->pna_cred)
+ return EINVAL;
+ if (ngroups > NGROUPS_MAX)
+ return EINVAL;
+
+ persona_lock(persona);
+ if (!persona_valid(persona)) {
+ ret = EINVAL;
+ goto out_unlock;
+ }
+ if (persona->pna_cred_locked) {
+ ret = EPERM;
+ goto out_unlock;
+ }
+
+ my_cred = persona->pna_cred;
+ kauth_cred_ref(my_cred);
+ new_cred = kauth_cred_setgroups(my_cred, groups, ngroups, gmuid);
+ if (new_cred != my_cred)
+ persona->pna_cred = new_cred;
+ kauth_cred_unref(&my_cred);
+
+out_unlock:
+ persona_unlock(persona);
+ return ret;
+}
+
+int persona_get_groups(struct persona *persona, int *ngroups, gid_t *groups, int groups_sz)
+{
+ int ret = EINVAL;
+ if (!persona || !persona->pna_cred || !groups || !ngroups)
+ return EINVAL;
+
+ *ngroups = groups_sz;
+
+ persona_lock(persona);
+ if (persona_valid(persona)) {
+ kauth_cred_getgroups(persona->pna_cred, groups, ngroups);
+ ret = 0;
+ }
+ persona_unlock(persona);
+
+ return ret;
+}
+
+uid_t persona_get_gmuid(struct persona *persona)
+{
+ uid_t gmuid = KAUTH_UID_NONE;
+
+ if (!persona || !persona->pna_cred)
+ return gmuid;
+
+ persona_lock(persona);
+ if (!persona_valid(persona))
+ goto out_unlock;
+
+ posix_cred_t pcred = posix_cred_get(persona->pna_cred);
+ gmuid = pcred->cr_gmuid;
+
+out_unlock:
+ persona_unlock(persona);
+ return gmuid;
+}
+
+int persona_get_login(struct persona *persona, char login[MAXLOGNAME+1])
+{
+ int ret = EINVAL;
+ if (!persona || !persona->pna_cred)
+ return EINVAL;
+
+ persona_lock(persona);
+ if (!persona_valid(persona))
+ goto out_unlock;
+
+ strlcpy(login, persona->pna_login, MAXLOGNAME);
+ ret = 0;
+
+out_unlock:
+ persona_unlock(persona);
+ login[MAXLOGNAME] = 0;
+
+ return ret;
+}
+
+#else /* !CONFIG_PERSONAS */
+
+/*
+ * symbol exports for kext compatibility
+ */
+
+uid_t persona_get_id(__unused struct persona *persona)
+{
+ return PERSONA_ID_NONE;
+}
+
+int persona_get_type(__unused struct persona *persona)
+{
+ return PERSONA_INVALID;
+}
+
+kauth_cred_t persona_get_cred(__unused struct persona *persona)
+{
+ return NULL;
+}
+
+struct persona *persona_lookup(__unused uid_t id)
+{
+ return NULL;
+}
+
+int persona_find(__unused const char *login,
+ __unused uid_t uid,
+ __unused struct persona **persona,
+ __unused size_t *plen)
+{
+ return ENOTSUP;
+}
+
+struct persona *current_persona_get(void)
+{
+ return NULL;
+}
+
+struct persona *persona_get(struct persona *persona)
+{
+ return persona;
+}
+
+void persona_put(__unused struct persona *persona)
+{
+ return;
+}
+#endif
#include <sys/priv.h>
#include <sys/proc_info.h>
#include <sys/bsdtask_info.h>
+#include <sys/persona.h>
#if CONFIG_MEMORYSTATUS
#include <sys/kern_memorystatus.h>
pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash);
sesshashtbl = hashinit(maxproc / 4, M_PROC, &sesshash);
uihashtbl = hashinit(maxproc / 16, M_PROC, &uihash);
+#if CONFIG_PERSONAS
+ personas_bootstrap();
+#endif
}
/*
return (current_proc()->p_ppid);
}
+int
+proc_selfcsflags(void)
+{
+ return (current_proc()->p_csflags);
+}
+
#if CONFIG_DTRACE
static proc_t
dtrace_current_proc_vforking(void)
return(p->p_idversion);
}
+uint32_t
+proc_persona_id(proc_t p)
+{
+ return (uint32_t)persona_id_from_proc(p);
+}
+
+uint32_t
+proc_getuid(proc_t p)
+{
+ return(p->p_uid);
+}
+
+uint32_t
+proc_getgid(proc_t p)
+{
+ return(p->p_gid);
+}
+
uint64_t
proc_uniqueid(proc_t p)
{
proc_lock(pt);
- if ((pt->p_csflags & CS_VALID) == 0) {
+ if ((pt->p_csflags & (CS_VALID | CS_DEBUGGED)) == 0) {
proc_unlock(pt);
error = EINVAL;
break;
size_t length;
proc_lock(pt);
- if ((pt->p_csflags & CS_VALID) == 0) {
+ if ((pt->p_csflags & (CS_VALID | CS_DEBUGGED)) == 0) {
proc_unlock(pt);
error = EINVAL;
break;
memset(fakeheader, 0, sizeof(fakeheader));
proc_lock(pt);
- if ((pt->p_csflags & CS_VALID) == 0) {
+ if ((pt->p_csflags & (CS_VALID | CS_DEBUGGED)) == 0) {
proc_unlock(pt);
error = EINVAL;
break;
#include <sys/timeb.h>
#include <sys/times.h>
#include <sys/malloc.h>
-
-#define chgproccnt_ok(p) 1
+#include <sys/persona.h>
#include <security/audit/audit.h>
* may be able to decrement the proc count of B before we can increment it. This results in a panic.
* Incrementing the proc count of the target ruid, B, before setting the process credentials prevents this race.
*/
- if (ruid != KAUTH_UID_NONE && chgproccnt_ok(p)) {
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
(void)chgproccnt(ruid, 1);
}
* We didn't successfully switch to the new ruid, so decrement
* the procs/uid count that we incremented above.
*/
- if (ruid != KAUTH_UID_NONE && chgproccnt_ok(p)) {
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
(void)chgproccnt(ruid, -1);
}
kauth_cred_unref(&my_new_cred);
* If we've updated the ruid, decrement the count of procs running
* under the previous ruid
*/
- if (ruid != KAUTH_UID_NONE && chgproccnt_ok(p)) {
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
(void)chgproccnt(my_pcred->cr_ruid, -1);
}
}
* may be able to decrement the proc count of B before we can increment it. This results in a panic.
* Incrementing the proc count of the target ruid, B, before setting the process credentials prevents this race.
*/
- if (ruid != KAUTH_UID_NONE && chgproccnt_ok(p)) {
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
(void)chgproccnt(ruid, 1);
}
*/
if (p->p_ucred != my_cred) {
proc_ucred_unlock(p);
- if (ruid != KAUTH_UID_NONE && chgproccnt_ok(p)) {
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
/*
* We didn't successfully switch to the new ruid, so decrement
* the procs/uid count that we incremented above.
OSBitOrAtomic(P_SUGID, &p->p_flag);
proc_ucred_unlock(p);
- if (ruid != KAUTH_UID_NONE && chgproccnt_ok(p)) {
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
/*
* We switched to a new ruid, so decrement the count of procs running
* under the previous ruid
static void proc_shutdown(void);
static void kernel_hwm_panic_info(void);
extern void IOSystemShutdownNotification(void);
+#if DEVELOPMENT || DEBUG
+extern boolean_t kdp_has_polled_corefile(void);
+#endif /* DEVELOPMENT || DEBUG */
struct sd_filterargs{
int delayterm;
/*
* Unmount filesystems
*/
- vfs_unmountall();
+
+#if DEVELOPMENT || DEBUG
+ if (!(howto & RB_PANIC) || !kdp_has_polled_corefile())
+#endif /* DEVELOPMENT || DEBUG */
+ {
+ vfs_unmountall();
+ }
/* Wait for the buffer cache to clean remaining dirty buffers */
for (iter = 0; iter < 100; iter++) {
#include <security/mac_framework.h>
#endif
+int pshm_cache_purge_all(proc_t p);
+int psem_cache_purge_all(proc_t p);
+
int
reboot(struct proc *p, register struct reboot_args *uap, __unused int32_t *retval)
{
}
return(error);
}
+
+int
+usrctl(struct proc *p, __unused register struct usrctl_args *uap, __unused int32_t *retval)
+{
+ if (p != initproc) {
+ return EPERM;
+ }
+
+ int error = 0;
+ error = pshm_cache_purge_all(p);
+ if (error)
+ return error;
+
+ error = psem_cache_purge_all(p);
+ return error;
+}
__func__, sock->so_retaincnt, sock);
/* NOTREACHED */
}
- if ((sock->so_retaincnt == 0) && (sock->so_usecount == 2)) {
+ /*
+ * Check SS_NOFDREF in case a close happened as sock_retain()
+ * was grabbing the lock
+ */
+ if ((sock->so_retaincnt == 0) && (sock->so_usecount == 2) &&
+ (!(sock->so_state & SS_NOFDREF) ||
+ (sock->so_flags & SOF_MP_SUBFLOW))) {
/* close socket only if the FD is not holding it */
soclose_locked(sock);
} else {
return (error);
}
-__private_extern__ int
-sflt_connectout(struct socket *so, const struct sockaddr *nam)
+static int
+sflt_connectout_common(struct socket *so, const struct sockaddr *nam)
{
- if (so->so_filt == NULL)
- return (0);
-
struct socket_filter_entry *entry;
int unlocked = 0;
int error = 0;
}
__private_extern__ int
-sflt_connectxout(struct socket *so, struct sockaddr_list **dst_sl0)
+sflt_connectout(struct socket *so, const struct sockaddr *nam)
{
char buf[SOCK_MAXADDRLEN];
+ struct sockaddr *sa;
+ int error;
+
+ if (so->so_filt == NULL)
+ return (0);
+
+ /*
+ * Workaround for rdar://23362120
+ * Always pass a buffer that can hold an IPv6 socket address
+ */
+ bzero(buf, sizeof (buf));
+ bcopy(nam, buf, nam->sa_len);
+ sa = (struct sockaddr *)buf;
+
+ error = sflt_connectout_common(so, sa);
+ if (error != 0)
+ return (error);
+
+ /*
+ * If the address was modified, copy it back
+ */
+ if (bcmp(sa, nam, nam->sa_len) != 0) {
+ bcopy(sa, (struct sockaddr *)(uintptr_t)nam, nam->sa_len);
+ }
+
+ return (0);
+}
+
+__private_extern__ int
+sflt_connectxout(struct socket *so, struct sockaddr_list **dst_sl0)
+{
struct sockaddr_list *dst_sl;
struct sockaddr_entry *se, *tse;
int modified = 0;
* as soon as we get an error.
*/
TAILQ_FOREACH_SAFE(se, &dst_sl->sl_head, se_link, tse) {
- int sa_len = se->se_addr->sa_len;
+ char buf[SOCK_MAXADDRLEN];
+ struct sockaddr *sa;
+
+ VERIFY(se->se_addr != NULL);
- /* remember the original address */
+ /*
+ * Workaround for rdar://23362120
+ * Always pass a buffer that can hold an IPv6 socket address
+ */
bzero(buf, sizeof (buf));
- bcopy(se->se_addr, buf, sa_len);
+ bcopy(se->se_addr, buf, se->se_addr->sa_len);
+ sa = (struct sockaddr *)buf;
- VERIFY(se->se_addr != NULL);
- error = sflt_connectout(so, se->se_addr);
+ error = sflt_connectout_common(so, sa);
if (error != 0)
break;
- /* see if the address was modified */
- if (bcmp(se->se_addr, buf, sa_len) != 0)
+ /*
+ * If the address was modified, copy it back
+ */
+ if (bcmp(se->se_addr, sa, se->se_addr->sa_len) != 0) {
+ bcopy(sa, se->se_addr, se->se_addr->sa_len);
modified = 1;
+ }
}
if (error != 0 || !modified) {
#include <vm/vm_protos.h>
#include <IOKit/IOReturn.h> /* for kIOReturnNotPrivileged */
+#include <os/overflow.h>
+
/*
* XXX vm/pmap.h should not treat these prototypes as MACH_KERNEL_PRIVATE
* when KERNEL is defined.
extern pmap_t pmap_create(ledger_t ledger, vm_map_size_t size,
boolean_t is_64bit);
-extern kern_return_t machine_thread_neon_state_initialize(thread_t thread);
-
/* XXX should have prototypes in a shared header file */
extern int get_map_nentries(vm_map_t);
.uuid = { 0 },
.min_vm_addr = MACH_VM_MAX_ADDRESS,
.max_vm_addr = MACH_VM_MIN_ADDRESS,
- .cs_end_offset = 0
+ .cs_end_offset = 0,
+ .threadstate = NULL,
+ .threadstate_sz = 0
};
/*
load_threadstate(
thread_t thread,
uint32_t *ts,
- uint32_t total_size
+ uint32_t total_size,
+ load_result_t *
);
static load_return_t
struct image_params *imgp,
struct mach_header *header,
thread_t thread,
- vm_map_t new_map,
+ vm_map_t *mapp,
load_result_t *result
)
{
off_t file_offset = imgp->ip_arch_offset;
off_t macho_size = imgp->ip_arch_size;
off_t file_size = imgp->ip_vattr->va_data_size;
-
+ vm_map_t new_map = *mapp;
pmap_t pmap = 0; /* protected by create_map */
vm_map_t map;
- vm_map_t old_map;
- task_t old_task = TASK_NULL; /* protected by create_map */
load_result_t myresult;
load_return_t lret;
boolean_t create_map = FALSE;
if (new_map == VM_MAP_NULL) {
create_map = TRUE;
- old_task = current_task();
}
/*
*/
if (spawn) {
create_map = TRUE;
- old_task = get_threadtask(thread);
}
if (create_map) {
}
}
- /*
- * Commit to new map.
- *
- * Swap the new map for the old, which consumes our new map
- * reference but each leaves us responsible for the old_map reference.
- * That lets us get off the pmap associated with it, and
- * then we can release it.
- */
-
- if (create_map) {
+ if (create_map) {
/*
* If this is an exec, then we are going to destroy the old
* task, and it's correct to halt it; if it's spawn, the
* task is not yet running, and it makes no sense.
*/
- if (!spawn) {
+ if (!spawn) {
/*
* Mark the task as halting and start the other
* threads towards terminating themselves. Then
kqueue_dealloc(p->p_wqkqueue);
p->p_wqkqueue = NULL;
}
- old_map = swap_task_map(old_task, thread, map, !spawn);
- vm_map_deallocate(old_map);
+ *mapp = map;
}
return(LOAD_SUCCESS);
}
/* kernel does *not* use entryoff from LC_MAIN. Dyld uses it. */
result->needs_dynlinker = TRUE;
result->using_lcmain = TRUE;
-
- ret = thread_state_initialize( thread );
- if (ret != KERN_SUCCESS) {
- return(LOAD_FAILURE);
- }
-
-
result->unixproc = TRUE;
result->thread_count++;
result->entry_point += slide;
ret = load_threadstate(thread,
- (uint32_t *)(((vm_offset_t)tcp) +
- sizeof(struct thread_command)),
- tcp->cmdsize - sizeof(struct thread_command));
+ (uint32_t *)(((vm_offset_t)tcp) + sizeof(struct thread_command)),
+ tcp->cmdsize - sizeof(struct thread_command),
+ result);
if (ret != LOAD_SUCCESS)
return (ret);
-
result->unixproc = TRUE;
result->thread_count++;
load_threadstate(
thread_t thread,
uint32_t *ts,
- uint32_t total_size
+ uint32_t total_size,
+ load_result_t *result
)
{
- kern_return_t ret;
uint32_t size;
int flavor;
uint32_t thread_size;
- uint32_t *local_ts;
- uint32_t local_ts_size;
+ uint32_t *local_ts = NULL;
+ uint32_t local_ts_size = 0;
+ int ret;
- local_ts = NULL;
- local_ts_size = 0;
+ (void)thread;
- ret = thread_state_initialize( thread );
- if (ret != KERN_SUCCESS) {
- ret = LOAD_FAILURE;
- goto done;
- }
-
if (total_size > 0) {
local_ts_size = total_size;
local_ts = kalloc(local_ts_size);
if (local_ts == NULL) {
- ret = LOAD_FAILURE;
- goto done;
+ return LOAD_FAILURE;
}
memcpy(local_ts, ts, local_ts_size);
ts = local_ts;
}
/*
- * Set the new thread state; iterate through the state flavors in
- * the mach-o file.
+ * Validate the new thread state; iterate through the state flavors in
+ * the Mach-O file.
+ * XXX: we should validate the machine state here, to avoid failing at
+ * activation time where we can't bail out cleanly.
*/
while (total_size > 0) {
flavor = *ts++;
size = *ts++;
- if (UINT32_MAX-2 < size ||
- UINT32_MAX/sizeof(uint32_t) < size+2) {
- ret = LOAD_BADMACHO;
- goto done;
- }
- thread_size = (size+2)*sizeof(uint32_t);
- if (thread_size > total_size) {
+
+ if (os_add_overflow(size, UINT32_C(2), &thread_size) ||
+ os_mul_overflow(thread_size, (uint32_t)sizeof(uint32_t), &thread_size) ||
+ os_sub_overflow(total_size, thread_size, &total_size)) {
ret = LOAD_BADMACHO;
- goto done;
- }
- total_size -= thread_size;
- /*
- * Third argument is a kernel space pointer; it gets cast
- * to the appropriate type in machine_thread_set_state()
- * based on the value of flavor.
- */
- ret = thread_setstatus(thread, flavor, (thread_state_t)ts, size);
- if (ret != KERN_SUCCESS) {
- ret = LOAD_FAILURE;
- goto done;
+ goto bad;
}
+
ts += size; /* ts is a (uint32_t *) */
}
- ret = LOAD_SUCCESS;
-done:
- if (local_ts != NULL) {
+ result->threadstate = local_ts;
+ result->threadstate_sz = local_ts_size;
+ return LOAD_SUCCESS;
+
+bad:
+ if (local_ts) {
kfree(local_ts, local_ts_size);
- local_ts = NULL;
}
return ret;
}
}
}
- if (ret == LOAD_SUCCESS) {
+ if (ret == LOAD_SUCCESS) {
+ if (result->threadstate) {
+ /* don't use the app's threadstate if we have a dyld */
+ kfree(result->threadstate, result->threadstate_sz);
+ }
+ result->threadstate = myresult->threadstate;
+ result->threadstate_sz = myresult->threadstate_sz;
+
result->dynlinker = TRUE;
result->entry_point = myresult->entry_point;
result->validentry = myresult->validentry;
using_lcmain :1,
:0;
unsigned int csflags;
- unsigned char uuid[16];
+ unsigned char uuid[16];
mach_vm_address_t min_vm_addr;
mach_vm_address_t max_vm_addr;
unsigned int platform_binary;
off_t cs_end_offset;
+ void *threadstate;
+ size_t threadstate_sz;
} load_result_t;
struct image_params;
struct image_params *imgp,
struct mach_header *header,
thread_t thread,
- vm_map_t map,
+ vm_map_t *mapp,
load_result_t *result);
#define LOAD_SUCCESS 0
};
#define PSEMCACHE_NULL (struct psemcache *)0
+#define PSEMCACHE_NOTFOUND (0)
+#define PSEMCACHE_FOUND (-1)
+#define PSEMCACHE_NEGATIVE (ENOENT)
+
struct psemstats {
long goodhits; /* hits that we can really use */
long neghits; /* negative hits that we can use */
caddr_t data, vfs_context_t ctx);
static int psem_select (struct fileproc *fp, int which, void *wql, vfs_context_t ctx);
static int psem_closefile (struct fileglob *fp, vfs_context_t ctx);
+static int psem_unlink_internal(struct pseminfo *pinfo, struct psemcache *pcache);
static int psem_kqfilter (struct fileproc *fp, struct knote *kn, vfs_context_t ctx);
#define PSEM_SUBSYS_LOCK() lck_mtx_lock(& psx_sem_subsys_mutex)
#define PSEM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_sem_subsys_mutex)
+#define PSEM_SUBSYS_ASSERT_HELD() lck_mtx_assert(&psx_sem_subsys_mutex, LCK_MTX_ASSERT_OWNED)
static int psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp);
+static void psem_cache_delete(struct psemcache *pcp);
+int psem_cache_purge_all(proc_t);
+
+
/* Initialize the mutex governing access to the posix sem subsystem */
__private_extern__ void
psem_lock_init( void )
if (pnp->psem_namelen > PSEMNAMLEN) {
psemstats.longnames++;
- return (0);
+ return PSEMCACHE_NOTFOUND;
}
pcpp = PSEMHASH(pnp);
if (pcp == 0) {
psemstats.miss++;
- return (0);
+ return PSEMCACHE_NOTFOUND;
}
/* We found a "positive" match, return the vnode */
/* TOUCH(ncp); */
*psemp = pcp->pseminfo;
*pcache = pcp;
- return (-1);
+ return PSEMCACHE_FOUND;
}
/*
* The nc_vpid field records whether this is a whiteout.
*/
psemstats.neghits++;
- return (ENOENT);
+ return PSEMCACHE_NEGATIVE;
}
/*
/* if the entry has already been added by some one else return */
- if (psem_cache_search(&dpinfo, pnp, &dpcp) == -1) {
- return(EEXIST);
+ if (psem_cache_search(&dpinfo, pnp, &dpcp) == PSEMCACHE_FOUND) {
+ return EEXIST;
}
if (psemnument >= posix_sem_max)
- return(ENOSPC);
+ return ENOSPC;
psemnument++;
/*
* Fill in cache info, if vp is NULL this is a "negative" cache entry.
}
#endif
LIST_INSERT_HEAD(pcpp, pcp, psem_hash);
- return(0);
+ return 0;
}
/*
psemnument--;
}
-#if NOT_USED
/*
- * Invalidate a all entries to particular vnode.
- *
- * We actually just increment the v_id, that will do it. The entries will
- * be purged by lookup as they get found. If the v_id wraps around, we
- * need to ditch the entire cache, to avoid confusion. No valid vnode will
- * ever have (v_id == 0).
+ * Remove all cached psem entries. Open semaphores (with a positive refcount)
+ * will continue to exist, but their cache entries tying them to a particular
+ * name/path will be removed making all future lookups on the name fail.
*/
-static void
-psem_cache_purge(void)
+int
+psem_cache_purge_all(__unused proc_t p)
{
- struct psemcache *pcp;
+ struct psemcache *pcp, *tmppcp;
struct psemhashhead *pcpp;
+ int error = 0;
+
+ if (kauth_cred_issuser(kauth_cred_get()) == 0)
+ return EPERM;
+ PSEM_SUBSYS_LOCK();
for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) {
- while ( (pcp = pcpp->lh_first) )
- psem_cache_delete(pcp);
+ LIST_FOREACH_SAFE(pcp, pcpp, psem_hash, tmppcp) {
+ assert(pcp->psem_nlen);
+ /*
+ * unconditionally unlink the cache entry
+ */
+ error = psem_unlink_internal(pcp->pseminfo, pcp);
+ if (error)
+ goto out;
+ }
}
+ assert(psemnument == 0);
+
+out:
+ PSEM_SUBSYS_UNLOCK();
+
+ if (error)
+ printf("%s: Error %d removing all semaphores: %ld remain!\n",
+ __func__, error, psemnument);
+ return error;
}
-#endif /* NOT_USED */
int
sem_open(proc_t p, struct sem_open_args *uap, user_addr_t *retval)
PSEM_SUBSYS_LOCK();
error = psem_cache_search(&pinfo, &nd, &pcache);
- if (error == ENOENT) {
+ if (error == PSEMCACHE_NEGATIVE) {
error = EINVAL;
goto bad_locked;
-
}
- if (!error) {
- incache = 0;
- } else
+
+ if (error == PSEMCACHE_FOUND)
incache = 1;
+ else
+ incache = 0;
cmode &= ALLPERMS;
return(posix_cred_access(cred, pinfo->psem_uid, pinfo->psem_gid, pinfo->psem_mode, mode_req));
}
+static int
+psem_unlink_internal(struct pseminfo *pinfo, struct psemcache *pcache)
+{
+ PSEM_SUBSYS_ASSERT_HELD();
+
+ if (!pinfo || !pcache)
+ return EINVAL;
+
+ if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) == 0)
+ return EINVAL;
+
+ if (pinfo->psem_flags & PSEM_INDELETE)
+ return 0;
+
+ AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid,
+ pinfo->psem_mode);
+
+ pinfo->psem_flags |= PSEM_INDELETE;
+ pinfo->psem_usecount--;
+
+ if (!pinfo->psem_usecount) {
+ psem_delete(pinfo);
+ FREE(pinfo,M_SHM);
+ } else {
+ pinfo->psem_flags |= PSEM_REMOVED;
+ }
+
+ psem_cache_delete(pcache);
+ FREE(pcache, M_SHM);
+ return 0;
+}
+
+
int
sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *retval)
{
int error=0;
struct psemname nd;
struct pseminfo *pinfo;
- char * pnbuf;
char * nameptr;
char * cp;
- size_t pathlen, plen;
- int incache = 0;
+ char * pnbuf;
+ size_t pathlen;
struct psemcache *pcache = PSEMCACHE_NULL;
pinfo = PSEMINFO_NULL;
goto bad;
}
+ nameptr = pnbuf;
#ifdef PSXSEM_NAME_RESTRICT
- nameptr = pnbuf;
if (*nameptr == '/') {
while (*(nameptr++) == '/') {
- plen--;
+ pathlen--;
error = EINVAL;
goto bad;
}
}
#endif /* PSXSEM_NAME_RESTRICT */
- plen = pathlen;
- nameptr = pnbuf;
nd.psem_nameptr = nameptr;
- nd.psem_namelen = plen;
+ nd.psem_namelen = pathlen;
nd. psem_hash =0;
- for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
+ for (cp = nameptr, i=1; *cp != 0 && i <= pathlen; i++, cp++) {
nd.psem_hash += (unsigned char)*cp * i;
}
PSEM_SUBSYS_LOCK();
error = psem_cache_search(&pinfo, &nd, &pcache);
- if (error == ENOENT) {
+ if (error != PSEMCACHE_FOUND) {
PSEM_SUBSYS_UNLOCK();
error = EINVAL;
goto bad;
}
- if (!error) {
- PSEM_SUBSYS_UNLOCK();
- error = EINVAL;
- goto bad;
- } else
- incache = 1;
+
#if CONFIG_MACF
error = mac_posixsem_check_unlink(kauth_cred_get(), pinfo, nameptr);
if (error) {
goto bad;
}
- if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))==0) {
- PSEM_SUBSYS_UNLOCK();
- error = EINVAL;
- goto bad;
- }
-
- if ( (pinfo->psem_flags & PSEM_INDELETE) ) {
- PSEM_SUBSYS_UNLOCK();
- error = 0;
- goto bad;
- }
-
- AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid,
- pinfo->psem_mode);
-
- pinfo->psem_flags |= PSEM_INDELETE;
- pinfo->psem_usecount--;
-
- if (!pinfo->psem_usecount) {
- psem_delete(pinfo);
- FREE(pinfo,M_SHM);
- } else
- pinfo->psem_flags |= PSEM_REMOVED;
-
- psem_cache_delete(pcache);
+ error = psem_unlink_internal(pinfo, pcache);
PSEM_SUBSYS_UNLOCK();
- FREE(pcache, M_SHM);
- error = 0;
+
bad:
FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
- return (error);
+ return error;
}
int
};
#define PSHMCACHE_NULL (struct pshmcache *)0
+#define PSHMCACHE_NOTFOUND (0)
+#define PSHMCACHE_FOUND (-1)
+#define PSHMCACHE_NEGATIVE (ENOENT)
+
struct pshmstats {
long goodhits; /* hits that we can really use */
long neghits; /* negative hits that we can use */
static int pshm_kqfilter(struct fileproc *fp, struct knote *kn, vfs_context_t ctx);
int pshm_access(struct pshminfo *pinfo, int mode, kauth_cred_t cred, proc_t p);
+int pshm_cache_purge_all(proc_t p);
+
static int pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *pcp);
static void pshm_cache_delete(struct pshmcache *pcp);
-#if NOT_USED
-static void pshm_cache_purge(void);
-#endif /* NOT_USED */
static int pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp,
struct pshmcache **pcache, int addref);
+static int pshm_unlink_internal(struct pshminfo *pinfo, struct pshmcache *pcache);
static const struct fileops pshmops = {
DTYPE_PSXSHM,
#define PSHM_SUBSYS_LOCK() lck_mtx_lock(& psx_shm_subsys_mutex)
#define PSHM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_shm_subsys_mutex)
+#define PSHM_SUBSYS_ASSERT_HELD() lck_mtx_assert(&psx_shm_subsys_mutex, LCK_MTX_ASSERT_OWNED)
/* Initialize the mutex governing access to the posix shm subsystem */
if (pnp->pshm_namelen > PSHMNAMLEN) {
pshmstats.longnames++;
- return (0);
+ return PSHMCACHE_NOTFOUND;
}
pcpp = PSHMHASH(pnp);
if (pcp == 0) {
pshmstats.miss++;
- return (0);
+ return PSHMCACHE_NOTFOUND;
}
/* We found a "positive" match, return the vnode */
*pcache = pcp;
if (addref)
pcp->pshminfo->pshm_usecount++;
- return (-1);
+ return PSHMCACHE_FOUND;
}
/*
* We found a "negative" match, ENOENT notifies client of this match.
*/
pshmstats.neghits++;
- return (ENOENT);
+ return PSHMCACHE_NEGATIVE;
}
/*
/* if the entry has already been added by some one else return */
- if (pshm_cache_search(&dpinfo, pnp, &dpcp, 0) == -1) {
- return(EEXIST);
+ if (pshm_cache_search(&dpinfo, pnp, &dpcp, 0) == PSHMCACHE_FOUND) {
+ return EEXIST;
}
pshmnument++;
}
#endif
LIST_INSERT_HEAD(pcpp, pcp, pshm_hash);
- return(0);
+ return 0;
}
/*
pshmhashtbl = hashinit(desiredvnodes / 8, M_SHM, &pshmhash);
}
-#if NOT_USED
/*
- * Invalidate a all entries to particular vnode.
- *
+ * Invalidate all entries and delete all objects associated with it. Entire
+ * non Kernel entries are going away. Just dump'em all
+ *
* We actually just increment the v_id, that will do it. The entries will
* be purged by lookup as they get found. If the v_id wraps around, we
* need to ditch the entire cache, to avoid confusion. No valid vnode will
* ever have (v_id == 0).
*/
-static void
-pshm_cache_purge(void)
+int
+pshm_cache_purge_all(__unused proc_t p)
{
- struct pshmcache *pcp;
+ struct pshmcache *pcp, *tmppcp;
struct pshmhashhead *pcpp;
+ int error = 0;
+
+ if (kauth_cred_issuser(kauth_cred_get()) == 0)
+ return EPERM;
+ PSHM_SUBSYS_LOCK();
for (pcpp = &pshmhashtbl[pshmhash]; pcpp >= pshmhashtbl; pcpp--) {
- while ( (pcp = pcpp->lh_first) )
- pshm_cache_delete(pcp);
+ LIST_FOREACH_SAFE(pcp, pcpp, pshm_hash, tmppcp) {
+ assert(pcp->pshm_nlen);
+ error = pshm_unlink_internal(pcp->pshminfo, pcp);
+ if (error)
+ goto out;
+ }
}
+ assert(pshmnument == 0);
+
+out:
+ PSHM_SUBSYS_UNLOCK();
+
+ if (error)
+ printf("%s: Error %d removing shm cache: %ld remain!\n",
+ __func__, error, pshmnument);
+ return error;
}
-#endif /* NOT_USED */
static void
pshm_cache_delete(struct pshmcache *pcp)
PSHM_SUBSYS_UNLOCK();
- if (error == ENOENT) {
+ if (error == PSHMCACHE_NEGATIVE) {
error = EINVAL;
goto bad;
}
- if (!error) {
+ if (error == PSHMCACHE_NOTFOUND) {
incache = 0;
if (fmode & O_CREAT) {
/* create a new one (commit the allocation) */
}
+static int
+pshm_unlink_internal(struct pshminfo *pinfo, struct pshmcache *pcache)
+{
+ struct pshmobj *pshmobj, *pshmobj_next;
+
+ PSHM_SUBSYS_ASSERT_HELD();
+
+ if (!pinfo || !pcache)
+ return EINVAL;
+
+ if ((pinfo->pshm_flags & (PSHM_DEFINED | PSHM_ALLOCATED)) == 0)
+ return EINVAL;
+
+ if (pinfo->pshm_flags & PSHM_INDELETE)
+ return 0;
+
+ pinfo->pshm_flags |= PSHM_INDELETE;
+ pinfo->pshm_usecount--;
+
+ pshm_cache_delete(pcache);
+ pinfo->pshm_flags |= PSHM_REMOVED;
+
+ /* release the existence reference */
+ if (!pinfo->pshm_usecount) {
+#if CONFIG_MACF
+ mac_posixshm_label_destroy(pinfo);
+#endif
+ /*
+ * If this is the last reference going away on the object,
+ * then we need to destroy the backing object. The name
+ * has an implied but uncounted reference on the object,
+ * once it's created, since it's used as a rendezvous, and
+ * therefore may be subsequently reopened.
+ */
+ for (pshmobj = pinfo->pshm_memobjects;
+ pshmobj != NULL;
+ pshmobj = pshmobj_next) {
+ mach_memory_entry_port_release(pshmobj->pshmo_memobject);
+ pshmobj_next = pshmobj->pshmo_next;
+ FREE(pshmobj, M_SHM);
+ }
+ FREE(pinfo,M_SHM);
+ }
+
+ FREE(pcache, M_SHM);
+
+ return 0;
+}
+
int
-shm_unlink(__unused proc_t p, struct shm_unlink_args *uap,
- __unused int32_t *retval)
+shm_unlink(proc_t p, struct shm_unlink_args *uap, __unused int32_t *retval)
{
size_t i;
- int error=0;
+ char * pnbuf;
+ size_t pathlen;
+ int error = 0;
+
struct pshmname nd;
struct pshminfo *pinfo;
- char * pnbuf;
char * nameptr;
char * cp;
- size_t pathlen, plen;
- int incache = 0;
struct pshmcache *pcache = PSHMCACHE_NULL;
- struct pshmobj *pshmobj, *pshmobj_next;
pinfo = PSHMINFO_NULL;
+
MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (pnbuf == NULL) {
return(ENOSPC); /* XXX non-standard */
goto bad;
}
+ nameptr = pnbuf;
#ifdef PSXSHM_NAME_RESTRICT
- nameptr = pnbuf;
if (*nameptr == '/') {
while (*(nameptr++) == '/') {
- plen--;
+ pathlen--;
error = EINVAL;
goto bad;
}
}
#endif /* PSXSHM_NAME_RESTRICT */
- plen = pathlen;
- nameptr = pnbuf;
nd.pshm_nameptr = nameptr;
- nd.pshm_namelen = plen;
- nd. pshm_hash =0;
+ nd.pshm_namelen = pathlen;
+ nd.pshm_hash = 0;
- for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
+ for (cp = nameptr, i=1; *cp != 0 && i <= pathlen; i++, cp++) {
nd.pshm_hash += (unsigned char)*cp * i;
}
PSHM_SUBSYS_LOCK();
error = pshm_cache_search(&pinfo, &nd, &pcache, 0);
- if (error == ENOENT) {
- PSHM_SUBSYS_UNLOCK();
- goto bad;
-
- }
/* During unlink lookup failure also implies ENOENT */
- if (!error) {
+ if (error != PSHMCACHE_FOUND) {
PSHM_SUBSYS_UNLOCK();
error = ENOENT;
goto bad;
- } else
- incache = 1;
+
+ }
if ((pinfo->pshm_flags & (PSHM_DEFINED | PSHM_ALLOCATED))==0) {
PSHM_SUBSYS_UNLOCK();
error = 0;
goto bad;
}
+
#if CONFIG_MACF
error = mac_posixshm_check_unlink(kauth_cred_get(), pinfo, nameptr);
if (error) {
pinfo->pshm_mode);
/*
- * following file semantics, unlink should be allowed
- * for users with write permission only.
+ * following file semantics, unlink should be allowed
+ * for users with write permission only.
*/
if ( (error = pshm_access(pinfo, FWRITE, kauth_cred_get(), p)) ) {
PSHM_SUBSYS_UNLOCK();
goto bad;
}
- pinfo->pshm_flags |= PSHM_INDELETE;
- pshm_cache_delete(pcache);
- pinfo->pshm_flags |= PSHM_REMOVED;
- /* release the existence reference */
- if (!--pinfo->pshm_usecount) {
-#if CONFIG_MACF
- mac_posixshm_label_destroy(pinfo);
-#endif
- PSHM_SUBSYS_UNLOCK();
- /*
- * If this is the last reference going away on the object,
- * then we need to destroy the backing object. The name
- * has an implied but uncounted reference on the object,
- * once it's created, since it's used as a rendezvous, and
- * therefore may be subsequently reopened.
- */
- for (pshmobj = pinfo->pshm_memobjects;
- pshmobj != NULL;
- pshmobj = pshmobj_next) {
- mach_memory_entry_port_release(pshmobj->pshmo_memobject);
- pshmobj_next = pshmobj->pshmo_next;
- FREE(pshmobj, M_SHM);
- }
- FREE(pinfo,M_SHM);
- } else {
- PSHM_SUBSYS_UNLOCK();
- }
- FREE(pcache, M_SHM);
- error = 0;
+ error = pshm_unlink_internal(pinfo, pcache);
+ PSHM_SUBSYS_UNLOCK();
+
bad:
FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
- return (error);
+ return error;
}
/* already called locked */
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple 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. 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_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kernel_types.h>
+#include <sys/sysproto.h>
+
+#include <sys/kauth.h>
+#include <sys/malloc.h>
+#include <sys/persona.h>
+#include <sys/proc.h>
+
+#include <libkern/libkern.h>
+
+static int kpersona_copyin(user_addr_t infop, struct kpersona_info *kinfo)
+{
+ uint32_t info_v = 0;
+ int error;
+
+ error = copyin(infop, &info_v, sizeof(info_v));
+ if (error)
+ return error;
+
+ /* only support a single version of the struct for now */
+ if (info_v != PERSONA_INFO_V1)
+ return EINVAL;
+
+ error = copyin(infop, kinfo, sizeof(*kinfo));
+
+ /* enforce NULL termination on strings */
+ kinfo->persona_name[MAXLOGNAME] = 0;
+
+ return error;
+}
+
+static int kpersona_copyout(struct kpersona_info *kinfo, user_addr_t infop)
+{
+ uint32_t info_v;
+ int error;
+
+ error = copyin(infop, &info_v, sizeof(info_v));
+ if (error)
+ return error;
+
+ /* only support a single version of the struct for now */
+ /* TODO: in the future compare info_v to kinfo->persona_info_version */
+ if (info_v != PERSONA_INFO_V1)
+ return EINVAL;
+
+ error = copyout(kinfo, infop, sizeof(*kinfo));
+ return error;
+}
+
+
+static int kpersona_alloc_syscall(user_addr_t infop, user_addr_t idp)
+{
+ int error;
+ struct kpersona_info kinfo;
+ struct persona *persona;
+ uid_t id = PERSONA_ID_NONE;
+ const char *login;
+
+ /*
+ * TODO: rdar://problem/19981151
+ * Add entitlement check!
+ */
+ if (!kauth_cred_issuser(kauth_cred_get()))
+ return EPERM;
+
+ error = kpersona_copyin(infop, &kinfo);
+ if (error)
+ return error;
+
+ login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
+ if (kinfo.persona_id != PERSONA_ID_NONE && kinfo.persona_id != (uid_t)0)
+ id = kinfo.persona_id;
+
+ error = 0;
+ persona = persona_alloc(id, login, kinfo.persona_type, &error);
+ if (!persona)
+ return error;
+
+ if (kinfo.persona_gid) {
+ error = persona_set_gid(persona, kinfo.persona_gid);
+ if (error)
+ goto out_error;
+ }
+
+ if (kinfo.persona_ngroups > 0) {
+ /* force gmuid 0 to *opt-out* of memberd */
+ if (kinfo.persona_gmuid == 0)
+ kinfo.persona_gmuid = KAUTH_UID_NONE;
+
+ error = persona_set_groups(persona, kinfo.persona_groups,
+ kinfo.persona_ngroups,
+ kinfo.persona_gmuid);
+ if (error)
+ goto out_error;
+ }
+
+ error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
+ if (error)
+ goto out_error;
+ error = kpersona_copyout(&kinfo, infop);
+
+ /*
+ * On success, we have a persona structure in the global list with a
+ * single reference count on it. The corresponding _dealloc() call
+ * will release this reference.
+ */
+ return error;
+
+out_error:
+ printf("%s: ERROR:%d\n", __func__, error);
+ if (persona)
+ persona_put(persona);
+ return error;
+}
+
+static int kpersona_dealloc_syscall(user_addr_t idp)
+{
+ int error;
+ uid_t persona_id;
+ struct persona *persona;
+
+ if (!kauth_cred_issuser(kauth_cred_get()))
+ return EPERM;
+
+ error = copyin(idp, &persona_id, sizeof(persona_id));
+ if (error)
+ return error;
+
+ persona = persona_lookup(persona_id);
+ if (!persona)
+ return ESRCH;
+
+ /* invalidate the persona (deny subsequent spawn/fork) */
+ error = persona_invalidate(persona);
+
+ /* one reference from the _lookup() */
+ persona_put(persona);
+
+ /* one reference from the _alloc() */
+ if (!error)
+ persona_put(persona);
+
+ return error;
+}
+
+static int kpersona_get_syscall(user_addr_t idp)
+{
+ int error;
+ struct persona *persona = current_persona_get();
+
+ if (!persona)
+ return ESRCH;
+
+ error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
+ persona_put(persona);
+
+ return error;
+}
+
+static int kpersona_info_syscall(user_addr_t idp, user_addr_t infop)
+{
+ int error;
+ uid_t persona_id;
+ struct persona *persona;
+ struct kpersona_info kinfo;
+
+ error = copyin(idp, &persona_id, sizeof(persona_id));
+ if (error)
+ return error;
+
+ /*
+ * TODO: rdar://problem/19981151
+ * Add entitlement check!
+ */
+
+ persona = persona_lookup(persona_id);
+ if (!persona)
+ return ESRCH;
+
+ persona_dbg("FOUND: persona:%p, id:%d, gid:%d, login:\"%s\"",
+ persona, persona->pna_id, persona_get_gid(persona),
+ persona->pna_login);
+
+ memset(&kinfo, 0, sizeof(kinfo));
+ kinfo.persona_info_version = PERSONA_INFO_V1;
+ kinfo.persona_id = persona->pna_id;
+ kinfo.persona_type = persona->pna_type;
+ kinfo.persona_gid = persona_get_gid(persona);
+ int ngroups = 0;
+ persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
+ kinfo.persona_ngroups = ngroups;
+ kinfo.persona_gmuid = persona_get_gmuid(persona);
+
+ /*
+ * NULL termination is assured b/c persona_name is
+ * exactly MAXLOGNAME + 1 bytes (and has been memset to 0)
+ */
+ strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
+
+ persona_put(persona);
+
+ error = kpersona_copyout(&kinfo, infop);
+
+ return error;
+}
+
+static int kpersona_pidinfo_syscall(user_addr_t idp, user_addr_t infop)
+{
+ int error;
+ pid_t pid;
+ struct persona *persona;
+ struct kpersona_info kinfo;
+
+ error = copyin(idp, &pid, sizeof(pid));
+ if (error)
+ return error;
+
+ if (!kauth_cred_issuser(kauth_cred_get())
+ && (pid != current_proc()->p_pid))
+ return EPERM;
+
+ persona = persona_proc_get(pid);
+ if (!persona)
+ return ESRCH;
+
+ memset(&kinfo, 0, sizeof(kinfo));
+ kinfo.persona_info_version = PERSONA_INFO_V1;
+ kinfo.persona_id = persona->pna_id;
+ kinfo.persona_type = persona->pna_type;
+ kinfo.persona_gid = persona_get_gid(persona);
+ int ngroups = 0;
+ persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
+ kinfo.persona_ngroups = ngroups;
+ kinfo.persona_gmuid = persona_get_gmuid(persona);
+
+ strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
+
+ persona_put(persona);
+
+ error = kpersona_copyout(&kinfo, infop);
+
+ return error;
+}
+
+static int kpersona_find_syscall(user_addr_t infop, user_addr_t idp, user_addr_t idlenp)
+{
+ int error;
+ struct kpersona_info kinfo;
+ const char *login;
+ size_t u_idlen, k_idlen = 0;
+ struct persona **persona = NULL;
+
+ error = copyin(idlenp, &u_idlen, sizeof(u_idlen));
+ if (error)
+ return error;
+
+ if (u_idlen > g_max_personas)
+ u_idlen = g_max_personas;
+
+ error = kpersona_copyin(infop, &kinfo);
+ if (error)
+ goto out;
+
+ login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
+
+ if (u_idlen > 0) {
+ MALLOC(persona, struct persona **, sizeof(*persona) * u_idlen,
+ M_TEMP, M_WAITOK|M_ZERO);
+ if (!persona) {
+ error = ENOMEM;
+ goto out;
+ }
+ }
+
+ k_idlen = u_idlen;
+ error = persona_find(login, kinfo.persona_id, persona, &k_idlen);
+ if (error)
+ goto out;
+
+ /* copyout all the IDs of each persona we found */
+ for (size_t i = 0; i < k_idlen; i++) {
+ if (i >= u_idlen)
+ break;
+ error = copyout(&persona[i]->pna_id,
+ idp + (i * sizeof(persona[i]->pna_id)),
+ sizeof(persona[i]->pna_id));
+ if (error)
+ goto out;
+ }
+
+out:
+ if (persona) {
+ for (size_t i = 0; i < u_idlen; i++)
+ persona_put(persona[i]);
+ FREE(persona, M_TEMP);
+ }
+
+ (void)copyout(&k_idlen, idlenp, sizeof(u_idlen));
+
+ return error;
+}
+
+
+/*
+ * Syscall entry point / demux.
+ */
+int persona(__unused proc_t p, struct persona_args *pargs, __unused int32_t *retval)
+{
+ int error;
+ uint32_t op = pargs->operation;
+ /* uint32_t flags = pargs->flags; */
+ user_addr_t infop = pargs->info;
+ user_addr_t idp = pargs->id;
+
+ switch (op) {
+ case PERSONA_OP_ALLOC:
+ error = kpersona_alloc_syscall(infop, idp);
+ break;
+ case PERSONA_OP_DEALLOC:
+ error = kpersona_dealloc_syscall(idp);
+ break;
+ case PERSONA_OP_GET:
+ error = kpersona_get_syscall(idp);
+ break;
+ case PERSONA_OP_INFO:
+ error = kpersona_info_syscall(idp, infop);
+ break;
+ case PERSONA_OP_PIDINFO:
+ error = kpersona_pidinfo_syscall(idp, infop);
+ break;
+ case PERSONA_OP_FIND:
+ error = kpersona_find_syscall(infop, idp, pargs->idlen);
+ break;
+ default:
+ error = ENOSYS;
+ break;
+ }
+
+ return error;
+}
442 AUE_CLOSE ALL { int guarded_close_np(int fd, const guardid_t *guard); }
443 AUE_KQUEUE ALL { int guarded_kqueue_np(const guardid_t *guard, u_int guardflags); }
444 AUE_NULL ALL { int change_fdguard_np(int fd, const guardid_t *guard, u_int guardflags, const guardid_t *nguard, u_int nguardflags, int *fdflagsp); }
-445 AUE_NULL ALL { int nosys(void); } { old __proc_suppress }
+445 AUE_USRCTL ALL { int usrctl(uint32_t flags); }
446 AUE_NULL ALL { int proc_rlimit_control(pid_t pid, int flavor, void *arg); }
#if SOCKETS
447 AUE_CONNECT ALL { int connectx(int socket, const sa_endpoints_t *endpoints, sae_associd_t associd, unsigned int flags, const struct iovec *iov, unsigned int iovcnt, size_t *len, sae_connid_t *connid); }
492 AUE_NULL ALL { int enosys(void); }
#endif /* CONFIG_TELEMETRY */
493 AUE_NULL ALL { user_ssize_t grab_pgo_data (user_addr_t uuid, int flags, user_addr_t buffer, user_ssize_t size); }
+#if CONFIG_PERSONAS
+494 AUE_PERSONA ALL { int persona(uint32_t operation, uint32_t flags, struct kpersona_info *info, uid_t *id, size_t *idlen) NO_SYSCALL_STUB; }
+#else
494 AUE_NULL ALL { int enosys(void); }
+#endif
495 AUE_NULL ALL { int enosys(void); }
496 AUE_NULL ALL { int enosys(void); }
497 AUE_NULL ALL { int enosys(void); }
SYSV_MSG_SUBSYS_UNLOCK();
if (IS_64BIT_PROCESS(p)) {
- struct user64_msqid_ds msqid_ds64;
+ struct user64_msqid_ds msqid_ds64 = {};
msqid_ds_kerneltouser64(&msqptr->u, &msqid_ds64);
eval = copyout(&msqid_ds64, uap->buf, sizeof(msqid_ds64));
} else {
- struct user32_msqid_ds msqid_ds32;
+ struct user32_msqid_ds msqid_ds32 = {};
msqid_ds_kerneltouser32(&msqptr->u, &msqid_ds32);
eval = copyout(&msqid_ds32, uap->buf, sizeof(msqid_ds32));
}
struct user32_IPCS_command u32;
struct user_IPCS_command u64;
} ipcs;
- struct user32_msqid_ds msqid_ds32; /* post conversion, 32 bit version */
- struct user64_msqid_ds msqid_ds64; /* post conversion, 64 bit version */
+ struct user32_msqid_ds msqid_ds32 = {}; /* post conversion, 32 bit version */
+ struct user64_msqid_ds msqid_ds64 = {}; /* post conversion, 64 bit version */
void *msqid_dsp;
size_t ipcs_sz;
size_t msqid_ds_sz;
0x14000A8 MACH_THREAD_BIND
0x14000AC MACH_WAITQ_PROMOTE
0x14000B0 MACH_WAITQ_DEMOTE
+0x14000B4 MACH_SCHED_LOAD
+0x14000B8 MACH_REC_CORES_FAILSAFE
+0x14000BC MACH_SCHED_QUANTUM_EXPIRED
0x1500000 MACH_MSGID_INVALID
0x1600000 MTX_SLEEP
0x1600004 MTX_SLEEP_DEADLINE
0x40c0784 BSC_sendmsg_x
0x40c0788 BSC_thread_selfusage
0x40c07a4 BSC_mremap_encrypted
-0x40c07b8 BSC_reserved
+0x40c07b8 BSC_persona
0x40c07cc BSC_work_interval_ctl
0x40e0104 BSC_msync_extended_info
0x40e0264 BSC_pread_extended_info
0x2700E010 PERF_SRAMEMA_DOM1
0x2700E020 PERF_SRAMEMA_DOM2
0x2700E030 PERF_SRAMEMA_DOM3
+0x28100004 BANK_SETTLE_CPU_TIME
+0x28100008 BANK_SECURE_ORIGINATOR_CHANGED
0x2a100004 ATM_MIN_CALLED
0x2a100008 ATM_LINK_LIST_TRIM
0x2a200004 ATM_VALUE_REPLACED
typedef void (*cs_md_final)(void *hash, void *ctx);
struct cs_hash {
- uint8_t cs_type;
- size_t cs_cd_size;
- size_t cs_size;
- size_t cs_digest_size;
+ uint8_t cs_type; /* type code as per code signing */
+ size_t cs_size; /* size of effective hash (may be truncated) */
+ size_t cs_digest_size; /* size of native hash */
cs_md_init cs_init;
cs_md_update cs_update;
cs_md_final cs_final;
static struct cs_hash cs_hash_sha1 = {
.cs_type = CS_HASHTYPE_SHA1,
- .cs_cd_size = CS_SHA1_LEN,
.cs_size = CS_SHA1_LEN,
.cs_digest_size = SHA_DIGEST_LENGTH,
.cs_init = (cs_md_init)SHA1Init,
#if CRYPTO_SHA2
static struct cs_hash cs_hash_sha256 = {
.cs_type = CS_HASHTYPE_SHA256,
- .cs_cd_size = SHA256_DIGEST_LENGTH,
.cs_size = SHA256_DIGEST_LENGTH,
.cs_digest_size = SHA256_DIGEST_LENGTH,
.cs_init = (cs_md_init)SHA256_Init,
};
static struct cs_hash cs_hash_sha256_truncate = {
.cs_type = CS_HASHTYPE_SHA256_TRUNCATED,
- .cs_cd_size = CS_SHA256_TRUNCATED_LEN,
.cs_size = CS_SHA256_TRUNCATED_LEN,
.cs_digest_size = SHA256_DIGEST_LENGTH,
.cs_init = (cs_md_init)SHA256_Init,
.cs_update = (cs_md_update)SHA256_Update,
.cs_final = (cs_md_final)SHA256_Final,
};
+static struct cs_hash cs_hash_sha384 = {
+ .cs_type = CS_HASHTYPE_SHA384,
+ .cs_size = SHA384_DIGEST_LENGTH,
+ .cs_digest_size = SHA384_DIGEST_LENGTH,
+ .cs_init = (cs_md_init)SHA384_Init,
+ .cs_update = (cs_md_update)SHA384_Update,
+ .cs_final = (cs_md_final)SHA384_Final,
+};
#endif
static struct cs_hash *
return &cs_hash_sha256;
} else if (type == CS_HASHTYPE_SHA256_TRUNCATED) {
return &cs_hash_sha256_truncate;
+ } else if (type == CS_HASHTYPE_SHA384) {
+ return &cs_hash_sha384;
#endif
}
return NULL;
union cs_hash_union {
SHA1_CTX sha1ctxt;
SHA256_CTX sha256ctx;
+ SHA384_CTX sha384ctx;
};
/*
- * Locate the CodeDirectory from an embedded signature blob
+ * Choose among different hash algorithms.
+ * Higher is better, 0 => don't use at all.
*/
-const
-CS_CodeDirectory *findCodeDirectory(
- const CS_SuperBlob *embedded,
- const char *lower_bound,
- const char *upper_bound)
-{
- const CS_CodeDirectory *cd = NULL;
-
- if (embedded &&
- cs_valid_range(embedded, embedded + 1, lower_bound, upper_bound) &&
- ntohl(embedded->magic) == CSMAGIC_EMBEDDED_SIGNATURE) {
- const CS_BlobIndex *limit;
- const CS_BlobIndex *p;
-
- limit = &embedded->index[ntohl(embedded->count)];
- if (!cs_valid_range(&embedded->index[0], limit,
- lower_bound, upper_bound)) {
- return NULL;
- }
- for (p = embedded->index; p < limit; ++p) {
- if (ntohl(p->type) == CSSLOT_CODEDIRECTORY) {
- const unsigned char *base;
-
- base = (const unsigned char *)embedded;
- cd = (const CS_CodeDirectory *)(base + ntohl(p->offset));
- break;
- }
- }
- } else {
- /*
- * Detached signatures come as a bare CS_CodeDirectory,
- * without a blob.
- */
- cd = (const CS_CodeDirectory *) embedded;
- }
+static uint32_t hashPriorities[] = {
+ CS_HASHTYPE_SHA1,
+ CS_HASHTYPE_SHA256_TRUNCATED,
+ CS_HASHTYPE_SHA256,
+ CS_HASHTYPE_SHA384,
+};
- if (cd &&
- cs_valid_range(cd, cd + 1, lower_bound, upper_bound) &&
- cs_valid_range(cd, (const char *) cd + ntohl(cd->length),
- lower_bound, upper_bound) &&
- cs_valid_range(cd, (const char *) cd + ntohl(cd->hashOffset),
- lower_bound, upper_bound) &&
- cs_valid_range(cd, (const char *) cd +
- ntohl(cd->hashOffset) +
- (ntohl(cd->nCodeSlots) * SHA1_RESULTLEN),
- lower_bound, upper_bound) &&
-
- ntohl(cd->magic) == CSMAGIC_CODEDIRECTORY) {
- return cd;
- }
+static unsigned int
+hash_rank(const CS_CodeDirectory *cd)
+{
+ uint32_t type = cd->hashType;
+ unsigned int n;
- // not found or not a valid code directory
- return NULL;
+ for (n = 0; n < sizeof(hashPriorities) / sizeof(hashPriorities[0]); ++n)
+ if (hashPriorities[n] == type)
+ return n + 1;
+ return 0; /* not supported */
}
if (hashtype == NULL)
return EBADEXEC;
- if (cd->hashSize != hashtype->cs_cd_size)
+ if (cd->hashSize != hashtype->cs_size)
return EBADEXEC;
-
if (length < ntohl(cd->hashOffset))
return EBADEXEC;
length = ntohl(blob->length);
if (ntohl(blob->magic) == CSMAGIC_EMBEDDED_SIGNATURE) {
- const CS_SuperBlob *sb = (const CS_SuperBlob *)blob;
- uint32_t n, count = ntohl(sb->count);
+ const CS_SuperBlob *sb;
+ uint32_t n, count;
+ const CS_CodeDirectory *best_cd = NULL;
+ unsigned int best_rank = 0;
if (length < sizeof(CS_SuperBlob))
return EBADEXEC;
+ sb = (const CS_SuperBlob *)blob;
+ count = ntohl(sb->count);
+
/* check that the array of BlobIndex fits in the rest of the data */
if ((length - sizeof(CS_SuperBlob)) / sizeof(CS_BlobIndex) < count)
return EBADEXEC;
/* now check each BlobIndex */
for (n = 0; n < count; n++) {
const CS_BlobIndex *blobIndex = &sb->index[n];
- if (length < ntohl(blobIndex->offset))
+ uint32_t type = ntohl(blobIndex->type);
+ uint32_t offset = ntohl(blobIndex->offset);
+ if (length < offset)
return EBADEXEC;
const CS_GenericBlob *subBlob =
- (const CS_GenericBlob *)(const void *)(addr + ntohl(blobIndex->offset));
+ (const CS_GenericBlob *)(const void *)(addr + offset);
- size_t subLength = length - ntohl(blobIndex->offset);
+ size_t subLength = length - offset;
if ((error = cs_validate_blob(subBlob, subLength)) != 0)
return error;
subLength = ntohl(subBlob->length);
/* extra validation for CDs, that is also returned */
- if (ntohl(blobIndex->type) == CSSLOT_CODEDIRECTORY) {
- const CS_CodeDirectory *cd = (const CS_CodeDirectory *)subBlob;
- if ((error = cs_validate_codedirectory(cd, subLength)) != 0)
+ if (type == CSSLOT_CODEDIRECTORY || (type >= CSSLOT_ALTERNATE_CODEDIRECTORIES && type < CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) {
+ const CS_CodeDirectory *candidate = (const CS_CodeDirectory *)subBlob;
+ if ((error = cs_validate_codedirectory(candidate, subLength)) != 0)
return error;
- *rcd = cd;
+ unsigned int rank = hash_rank(candidate);
+ if (cs_debug > 3)
+ printf("CodeDirectory type %d rank %d at slot 0x%x index %d\n", candidate->hashType, (int)rank, (int)type, (int)n);
+ if (best_cd == NULL || rank > best_rank) {
+ best_cd = candidate;
+ best_rank = rank;
+ } else if (best_cd != NULL && rank == best_rank) {
+ /* repeat of a hash type (1:1 mapped to ranks), illegal and suspicious */
+ printf("multiple hash=%d CodeDirectories in signature; rejecting", best_cd->hashType);
+ return EBADEXEC;
+ }
}
+ if (best_cd && cs_debug > 2)
+ printf("using CodeDirectory type %d (rank %d)\n", (int)best_cd->hashType, best_rank);
+ *rcd = best_cd;
}
} else if (ntohl(blob->magic) == CSMAGIC_CODEDIRECTORY) {
if (csblob->csb_hashtype == NULL || csblob->csb_hashtype->cs_digest_size > sizeof(computed_hash))
return EBADEXEC;
- if ((code_dir = (const CS_CodeDirectory *)csblob_find_blob(csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY)) == NULL)
- return 0;
+ code_dir = csblob->csb_cd;
entitlements = csblob_find_blob(csblob, CSSLOT_ENTITLEMENTS, CSMAGIC_EMBEDDED_ENTITLEMENTS);
embedded_hash = find_special_slot(code_dir, csblob->csb_hashtype->cs_size, CSSLOT_ENTITLEMENTS);
if (entitlements)
return EBADEXEC;
return 0;
- } else if (entitlements == NULL && memcmp(embedded_hash, cshash_zero, csblob->csb_hashtype->cs_size) != 0) {
- return EBADEXEC;
+ } else if (entitlements == NULL) {
+ if (memcmp(embedded_hash, cshash_zero, csblob->csb_hashtype->cs_size) != 0) {
+ return EBADEXEC;
+ } else {
+ return 0;
+ }
}
csblob->csb_hashtype->cs_init(&context);
{
const CS_CodeDirectory *cd;
- if ((cd = (const CS_CodeDirectory *)csblob_find_blob(
- csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY)) == NULL)
- return NULL;
+ cd = csblob->csb_cd;
if (ntohl(cd->version) < CS_SUPPORTSTEAMID)
return NULL;
uint8_t hash[CS_HASH_MAX_SIZE];
int md_size;
+#if CS_BLOB_PAGEABLE
+#error "cd might move under CS_BLOB_PAGEABLE; reconsider this code"
+#endif
+ blob->csb_cd = cd;
blob->csb_hashtype = cs_find_md(cd->hashType);
if (blob->csb_hashtype == NULL || blob->csb_hashtype->cs_digest_size > sizeof(hash))
panic("validated CodeDirectory but unsupported type");
- if (blob->csb_hashtype->cs_cd_size < CS_CDHASH_LEN) {
- if (cs_debug)
- printf("cs_cd_size is too small for a cdhash\n");
- error = EINVAL;
- goto out;
- }
blob->csb_flags = (ntohl(cd->flags) & CS_ALLOWED_MACHO) | CS_VALID;
blob->csb_end_offset = round_page_4K(ntohl(cd->codeLimit));
union cs_hash_union mdctx;
struct cs_hash *hashtype = NULL;
unsigned char actual_hash[CS_HASH_MAX_SIZE];
- unsigned char expected_hash[SHA1_RESULTLEN];
+ unsigned char expected_hash[CS_HASH_MAX_SIZE];
boolean_t found_hash;
struct cs_blob *blobs, *blob;
const CS_CodeDirectory *cd;
- const CS_SuperBlob *embedded;
const unsigned char *hash;
boolean_t validated;
off_t offset; /* page offset in the file */
}
blob_addr = kaddr + blob->csb_mem_offset;
-
lower_bound = CAST_DOWN(char *, blob_addr);
upper_bound = lower_bound + blob->csb_mem_size;
-
- embedded = (const CS_SuperBlob *) blob_addr;
- cd = findCodeDirectory(embedded, lower_bound, upper_bound);
+
+ cd = blob->csb_cd;
if (cd != NULL) {
/* all CD's that have been injected is already validated */
hashtype->cs_size,
lower_bound, upper_bound);
if (hash != NULL) {
- bcopy(hash, expected_hash, sizeof(expected_hash));
+ bcopy(hash, expected_hash, hashtype->cs_size);
found_hash = TRUE;
}
asha1 = (const uint32_t *) actual_hash;
esha1 = (const uint32_t *) expected_hash;
- if (bcmp(expected_hash, actual_hash, hashtype->cs_cd_size) != 0) {
+ if (bcmp(expected_hash, actual_hash, hashtype->cs_size) != 0) {
if (cs_debug) {
printf("CODE SIGNING: cs_validate_page: "
"mobj %p off 0x%llx size 0x%lx: "
*/
error = sflt_connectxout(so, dst_sl);
if (error != 0) {
+ /* Disable PRECONNECT_DATA, as we don't need to send a SYN anymore. */
+ so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
if (error == EJUSTRETURN)
error = 0;
} else {
error = so_set_extended_bk_idle(so, optval);
break;
+ case SO_MARK_CELLFALLBACK:
+ error = sooptcopyin(sopt, &optval, sizeof(optval),
+ sizeof(optval));
+ if (error != 0)
+ goto out;
+ if (optval < 0) {
+ error = EINVAL;
+ goto out;
+ }
+ if (optval == 0)
+ so->so_flags1 &= ~SOF1_CELLFALLBACK;
+ else
+ so->so_flags1 |= SOF1_CELLFALLBACK;
+ break;
default:
error = ENOPROTOOPT;
break;
case SO_EXTENDED_BK_IDLE:
optval = (so->so_flags1 & SOF1_EXTEND_BK_IDLE_WANTED);
goto integer;
-
+ case SO_MARK_CELLFALLBACK:
+ optval = ((so->so_flags1 & SOF1_CELLFALLBACK) > 0)
+ ? 1 : 0;
+ goto integer;
default:
error = ENOPROTOOPT;
break;
dst_se = sockaddrentry_alloc(how);
if (dst_se != NULL) {
int len = src_se->se_addr->sa_len;
- /*
- * Workaround for rdar://23362120
- * Allways allocate a buffer that can hold an IPv6 socket address
- */
- size_t alloclen = MAX(len, sizeof(struct sockaddr_in6));
MALLOC(dst_se->se_addr, struct sockaddr *,
- alloclen, M_SONAME, how | M_ZERO);
+ len, M_SONAME, how | M_ZERO);
if (dst_se->se_addr != NULL) {
bcopy(src_se->se_addr, dst_se->se_addr, len);
} else {
((SOCK_CHECK_DOM(sb->sb_so, PF_MULTIPATH)) &&
(SOCK_CHECK_PROTO(sb->sb_so, IPPROTO_TCP)))) &&
(!(sb->sb_so->so_flags1 & SOF1_POST_FALLBACK_SYNC))) {
- mptcp_preproc_sbdrop(m, (unsigned int)len);
+ mptcp_preproc_sbdrop(sb->sb_so, m, (unsigned int)len);
}
#endif /* MPTCP */
KERNEL_DEBUG((DBG_FNC_SBDROP | DBG_FUNC_START), sb, len, 0, 0, 0);
{
struct sockaddr *sa;
int error;
- size_t alloclen;
if (len > SOCK_MAXADDRLEN)
return (ENAMETOOLONG);
if (len < offsetof(struct sockaddr, sa_data[0]))
return (EINVAL);
- /*
- * Workaround for rdar://23362120
- * Allways allocate a buffer that can hold an IPv6 socket address
- */
- alloclen = MAX(len, sizeof(struct sockaddr_in6));
- MALLOC(sa, struct sockaddr *, alloclen, M_SONAME, M_WAITOK | M_ZERO);
+ MALLOC(sa, struct sockaddr *, len, M_SONAME, M_WAITOK | M_ZERO);
if (sa == NULL) {
return (ENOMEM);
}
*slp = NULL;
- if (uaddr == USER_ADDR_NULL || uaddrlen == 0)
+ if (uaddr == USER_ADDR_NULL || uaddrlen == 0 ||
+ uaddrlen > (sizeof(struct sockaddr_in6) * SOCKADDRLIST_MAX_ENTRIES))
return (EINVAL);
sl = sockaddrlist_alloc(M_WAITOK);
} else if (ss.ss_len > sizeof (ss)) {
/*
* sockaddr_storage size is less than SOCK_MAXADDRLEN,
- * so the check here is inclusive. We could user the
+ * so the check here is inclusive. We could use the
* latter instead, but seems like an overkill for now.
*/
error = ENAMETOOLONG;
}
se = sockaddrentry_alloc(M_WAITOK);
- if (se == NULL)
+ if (se == NULL) {
+ error = ENOBUFS;
break;
+ }
sockaddrlist_insert(sl, se);
/*
- * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
if (namelen <= 0)
return (EINVAL);
-
+ /*
+ * Note: sun_path is not a zero terminated "C" string
+ */
+ ASSERT(namelen < SOCK_MAXADDRLEN);
+ bcopy(soun->sun_path, buf, namelen);
+ buf[namelen] = 0;
+
socket_unlock(so, 0);
- strlcpy(buf, soun->sun_path, namelen+1);
NDINIT(&nd, CREATE, OP_MKFIFO, FOLLOW | LOCKPARENT, UIO_SYSSPACE,
CAST_USER_ADDR_T(buf), ctx);
/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
len = nam->sa_len - offsetof(struct sockaddr_un, sun_path);
if (len <= 0)
return (EINVAL);
+ /*
+ * Note: sun_path is not a zero terminated "C" string
+ */
+ ASSERT(len < SOCK_MAXADDRLEN);
+ bcopy(soun->sun_path, buf, len);
+ buf[len] = 0;
- strlcpy(buf, soun->sun_path, len+1);
socket_unlock(so, 0);
NDINIT(&nd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
INSTINC_SUBDIRS = \
devfs \
fifofs \
+ routefs \
specfs \
union
EXPINC_SUBDIRS = \
devfs \
fifofs \
+ routefs \
specfs \
union
--- /dev/null
+export MakeInc_cmd=${SRCROOT}/makedefs/MakeInc.cmd
+export MakeInc_def=${SRCROOT}/makedefs/MakeInc.def
+export MakeInc_rule=${SRCROOT}/makedefs/MakeInc.rule
+export MakeInc_dir=${SRCROOT}/makedefs/MakeInc.dir
+
+
+include $(MakeInc_cmd)
+include $(MakeInc_def)
+
+DATAFILES =
+
+KERNELFILES = \
+ routefs.h
+
+INSTALL_MI_LIST = ${DATAFILES}
+
+INSTALL_MI_DIR = miscfs/routefs
+
+INSTALL_KF_MI_LIST = ${DATAFILES}
+
+INSTALL_KF_MI_LCL_LIST =
+
+EXPORT_MI_LIST =
+
+EXPORT_MI_DIR = miscfs/routefs
+
+include $(MakeInc_rule)
+include $(MakeInc_dir)
+
+
--- /dev/null
+/*
+ * Copyright (c) 2015 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. 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_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#ifndef _MISCFS_ROUTEFS_DEVFS_H_
+#define _MISCFS_ROUTEFS_DEVFS_H_
+
+#include <sys/appleapiopts.h>
+
+
+__BEGIN_DECLS
+
+
+#ifdef BSD_KERNEL_PRIVATE
+
+struct routefs_args {
+ char route_path[MAXPATHLEN]; /* path name of the target route */
+ vnode_t route_rvp; /* vnode of the target of route */
+
+};
+
+struct routefs_mount {
+ char route_path[MAXPATHLEN]; /* path name of the target route */
+ mount_t route_mount;
+ vnode_t route_rvp; /* vnode of the target of route */
+ int route_vpvid; /* vnode of the target of route */
+};
+
+
+/*
+ * Function: routefs_kernel_mount
+ *
+ * Purpose:
+ * mount routefs
+ * any links created with devfs_make_link().
+ */
+int routefs_kernel_mount(char * routepath);
+
+#endif /* BSD_KERNEL_PRIVATE */
+
+__END_DECLS
+
+
+#endif /* !_MISCFS_ROUTEFS_DEVFS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple 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. 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_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/vnode_internal.h>
+#include <sys/proc.h>
+#include <sys/kauth.h>
+#include <sys/mount_internal.h>
+#include <sys/fcntl.h>
+#include <sys/unistd.h>
+#include <sys/malloc.h>
+#include <vfs/vfs_support.h>
+
+#include <libkern/OSAtomic.h>
+
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#endif
+
+#include "routefs.h"
+
+static int routefs_init(__unused struct vfsconf *vfsp);
+static int routefs_mount(struct mount *mp, __unused vnode_t devvp, __unused user_addr_t data, vfs_context_t ctx);
+static int routefs_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t ctx);
+static int routefs_unmount( struct mount *mp, int mntflags, __unused vfs_context_t ctx);
+static int routefs_root(struct mount *mp, struct vnode **vpp, __unused vfs_context_t ctx);
+static int routefs_statfs( struct mount *mp, struct vfsstatfs *sbp, __unused vfs_context_t ctx);
+static int routefs_vfs_getattr(__unused mount_t mp, struct vfs_attr *fsap, __unused vfs_context_t ctx);
+static int routefs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx);
+static int routefs_vget(__unused struct mount *mp, __unused ino64_t ino, __unused struct vnode **vpp, __unused vfs_context_t ctx);
+static int routefs_fhtovp (__unused struct mount *mp, __unused int fhlen, __unused unsigned char *fhp, __unused struct vnode **vpp, __unused vfs_context_t ctx);
+static int routefs_vptofh (__unused struct vnode *vp, __unused int *fhlenp, __unused unsigned char *fhp, __unused vfs_context_t ctx);
+static int routefs_sysctl(__unused int *name, __unused u_int namelen, __unused user_addr_t oldp,
+ __unused size_t *oldlenp, __unused user_addr_t newp,
+ __unused size_t newlen, __unused vfs_context_t ctx);
+static int routefserr_lookup(__unused struct vnop_lookup_args * args);
+
+static int routefserr_setlabel(__unused struct vnop_setlabel_args * args);
+
+
+lck_grp_t * routefs_lck_grp;
+lck_grp_attr_t * routefs_lck_grp_attr;
+lck_attr_t * routefs_lck_attr;
+lck_mtx_t routefs_mutex;
+
+#define ROUTEFS_LOCK() lck_mtx_lock(&routefs_mutex)
+#define ROUTEFS_UNLOCK() lck_mtx_unlock(&routefs_mutex)
+static int _lock_inited = 0;
+static boolean_t _fs_alreadyMounted = FALSE; /* atleast a mount of this filesystem is present */
+
+static int
+routefs_init(__unused struct vfsconf *vfsp)
+{
+ routefs_lck_grp_attr = lck_grp_attr_alloc_init();
+ routefs_lck_grp = lck_grp_alloc_init("routefs_lock", routefs_lck_grp_attr);
+ routefs_lck_attr = lck_attr_alloc_init();
+ lck_mtx_init(&routefs_mutex, routefs_lck_grp, routefs_lck_attr);
+ _lock_inited = 1;
+
+ return 0;
+}
+
+static int
+routefs_mount(struct mount *mp, __unused vnode_t devvp, user_addr_t data, vfs_context_t ctx)
+{
+ struct routefs_mount *routefs_mp_p = NULL; /* routefs specific mount info */
+ int error=EINVAL;
+ struct routefs_args * rargs = (struct routefs_args *)data;
+
+ /*-
+ * If they just want to update, we don't need to do anything.
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ {
+ return 0;
+ }
+
+
+ /* check for root mount only */
+ if ((error = proc_suser(current_proc()))!= 0) {
+ goto out;
+ }
+
+ if (vfs_iskernelmount(mp) == FALSE) {
+ error = EPERM;
+ goto out;
+ }
+
+ if (_fs_alreadyMounted == TRUE) {
+ /* if a filesystem is mounted, it needs to be unmounted prior to mount again */
+ error = EPERM;
+ goto out;
+ }
+
+ /* Advisory locking should be handled at the VFS layer */
+ vfs_setlocklocal(mp);
+
+ /*-
+ * Well, it's not an update, it's a real mount request.
+ * Time to get dirty.
+ * HERE we should check to see if we are already mounted here.
+ */
+
+ MALLOC(routefs_mp_p, struct routefs_mount *, sizeof(struct routefs_mount),
+ M_TEMP, M_WAITOK);
+ if (routefs_mp_p == NULL)
+ return (ENOMEM);
+ bzero(routefs_mp_p, sizeof(*routefs_mp_p));
+
+ routefs_mp_p->route_mount = mp;
+
+ if (rargs->route_rvp == NULLVP) {
+ error = EACCES;
+ goto out;
+ }
+
+ strlcpy(routefs_mp_p->route_path,rargs->route_path, MAXPATHLEN);
+ routefs_mp_p->route_rvp = rargs->route_rvp;
+ routefs_mp_p->route_vpvid = vnode_vid(rargs->route_rvp);
+
+ if (vnode_ref(routefs_mp_p->route_rvp) != 0) {
+ error = EACCES;
+ goto out;
+ }
+
+ /*
+ * Fill out some fields
+ */
+ __IGNORE_WCASTALIGN(mp->mnt_data = (qaddr_t)routefs_mp_p);
+ mp->mnt_vfsstat.f_fsid.val[0] = (int32_t)(uintptr_t)routefs_mp_p;
+ mp->mnt_vfsstat.f_fsid.val[1] = vfs_typenum(mp);
+ mp->mnt_flag |= MNT_LOCAL;
+
+ /*-
+ * Copy in the name of the directory the filesystem
+ * is to be mounted on.
+ * And we clear the remainder of the character strings
+ * to be tidy.
+ */
+
+ bzero(mp->mnt_vfsstat.f_mntfromname, MAXPATHLEN);
+ bcopy("routefs",mp->mnt_vfsstat.f_mntfromname, 5);
+ (void)routefs_statfs(mp, &mp->mnt_vfsstat, ctx);
+ _fs_alreadyMounted = TRUE; /* yep, fs is in play now */
+ error = 0;
+out:
+ if (error != 0) {
+ if (routefs_mp_p != NULL)
+ FREE((caddr_t)routefs_mp_p, M_TEMP);
+ }
+ return error;
+}
+
+
+static int
+routefs_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t ctx)
+{
+ return 0;
+}
+
+/*-
+ * Unmount the filesystem described by mp.
+ */
+static int
+routefs_unmount( struct mount *mp, int mntflags, __unused vfs_context_t ctx)
+{
+ struct routefs_mount *routefs_mp_p = (struct routefs_mount *)mp->mnt_data;
+ int flags = 0;
+ int force = 0;
+ int error;
+
+ /* check for root unmount only */
+ if ((error = proc_suser(current_proc()))!= 0) {
+ return(error);
+ }
+
+ if (mntflags & MNT_FORCE) {
+ flags |= FORCECLOSE;
+ force = 1;
+ }
+ /* giveup the ioref of vnode, no longer need it */
+ if (routefs_mp_p->route_rvp != NULLVP) {
+ if (vnode_getwithref(routefs_mp_p->route_rvp) == 0) {
+ vnode_rele(routefs_mp_p->route_rvp);
+ vnode_put(routefs_mp_p->route_rvp);
+ routefs_mp_p->route_rvp = NULLVP;
+ }
+ }
+ /* no vnodes, ignore any errors */
+ (void)vflush(mp, NULLVP, flags);
+ FREE((caddr_t)routefs_mp_p, M_TEMP);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ _fs_alreadyMounted = FALSE; /* unmounted the fs, only one allowed at a time */
+ return 0;
+}
+
+/* return the address of the root vnode in *vpp */
+static int
+routefs_root(struct mount *mp, struct vnode **vpp, __unused vfs_context_t ctx)
+{
+ struct routefs_mount *routefs_mp_p = (struct routefs_mount *)(mp->mnt_data);
+ int error=0;
+
+ /* check for nullvp incase its being rolled */
+ if (routefs_mp_p->route_rvp == NULLVP) {
+ ROUTEFS_LOCK();
+ if (routefs_mp_p->route_rvp == NULLVP) {
+ ROUTEFS_UNLOCK();
+ error = EACCES;
+ goto out;
+ }
+ ROUTEFS_UNLOCK();
+ }
+ if (vnode_getwithvid(routefs_mp_p->route_rvp, routefs_mp_p->route_vpvid) != 0) {
+ /* only one in the path., since no vnodes with this, you can hold across this call */
+ ROUTEFS_LOCK();
+ if (vnode_getwithref(routefs_mp_p->route_rvp) == 0) {
+ vnode_rele(routefs_mp_p->route_rvp);
+ vnode_put(routefs_mp_p->route_rvp);
+ routefs_mp_p->route_rvp = NULLVP;
+ routefs_mp_p->route_vpvid = -1;
+ error = vnode_lookup(routefs_mp_p->route_path, FREAD|O_DIRECTORY, &routefs_mp_p->route_rvp, ctx);
+ if (error == 0)
+ routefs_mp_p->route_vpvid = vnode_vid(routefs_mp_p->route_rvp);
+ } else {
+ error = EACCES;
+ }
+ ROUTEFS_UNLOCK();
+
+ if (error != 0)
+ goto out;
+ }
+ *vpp = routefs_mp_p->route_rvp;
+out:
+ return error;
+}
+
+static int
+routefs_statfs( struct mount *mp, struct vfsstatfs *sbp, __unused vfs_context_t ctx)
+{
+ struct routefs_mount *routefs_mp_p = (struct routefs_mount *)mp->mnt_data;
+
+ /*-
+ * Fill in the stat block.
+ */
+ //sbp->f_type = mp->mnt_vfsstat.f_type;
+ sbp->f_flags = 0; /* XXX */
+ sbp->f_bsize = 512;
+ sbp->f_iosize = 512;
+ sbp->f_blocks = (sizeof(struct routefs_mount)+ sbp->f_bsize) / sbp->f_bsize;
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ sbp->f_fsid.val[0] = (int32_t)(uintptr_t)routefs_mp_p;
+ sbp->f_fsid.val[1] = vfs_typenum(mp);
+
+ return 0;
+}
+
+static int
+routefs_vfs_getattr(__unused mount_t mp, struct vfs_attr *fsap, __unused vfs_context_t ctx)
+{
+ VFSATTR_RETURN(fsap, f_objcount, 1);
+ VFSATTR_RETURN(fsap, f_maxobjcount, 1);
+ VFSATTR_RETURN(fsap, f_bsize, 512);
+ VFSATTR_RETURN(fsap, f_iosize, 512);
+ if (VFSATTR_IS_ACTIVE(fsap, f_blocks) || VFSATTR_IS_ACTIVE(fsap, f_bused)) {
+ fsap->f_blocks = (sizeof(struct routefs_mount)+ fsap->f_bsize) / fsap->f_bsize;
+ fsap->f_bused = fsap->f_blocks;
+ VFSATTR_SET_SUPPORTED(fsap, f_blocks);
+ VFSATTR_SET_SUPPORTED(fsap, f_bused);
+ }
+ VFSATTR_RETURN(fsap, f_bfree, 0);
+ VFSATTR_RETURN(fsap, f_bavail, 0);
+ VFSATTR_RETURN(fsap, f_files, 0);
+ VFSATTR_RETURN(fsap, f_ffree, 0);
+ VFSATTR_RETURN(fsap, f_fssubtype, 0);
+
+ if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
+ fsap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] =
+ VOL_CAP_FMT_SYMBOLICLINKS |
+ VOL_CAP_FMT_HARDLINKS |
+ VOL_CAP_FMT_NO_ROOT_TIMES |
+ VOL_CAP_FMT_CASE_SENSITIVE |
+ VOL_CAP_FMT_CASE_PRESERVING |
+ VOL_CAP_FMT_FAST_STATFS |
+ VOL_CAP_FMT_2TB_FILESIZE |
+ VOL_CAP_FMT_HIDDEN_FILES;
+ fsap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] =
+ VOL_CAP_INT_ATTRLIST ;
+ fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
+ fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
+
+ fsap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] =
+ VOL_CAP_FMT_PERSISTENTOBJECTIDS |
+ VOL_CAP_FMT_SYMBOLICLINKS |
+ VOL_CAP_FMT_HARDLINKS |
+ VOL_CAP_FMT_JOURNAL |
+ VOL_CAP_FMT_JOURNAL_ACTIVE |
+ VOL_CAP_FMT_NO_ROOT_TIMES |
+ VOL_CAP_FMT_SPARSE_FILES |
+ VOL_CAP_FMT_ZERO_RUNS |
+ VOL_CAP_FMT_CASE_SENSITIVE |
+ VOL_CAP_FMT_CASE_PRESERVING |
+ VOL_CAP_FMT_FAST_STATFS |
+ VOL_CAP_FMT_2TB_FILESIZE |
+ VOL_CAP_FMT_OPENDENYMODES |
+ VOL_CAP_FMT_HIDDEN_FILES |
+ VOL_CAP_FMT_PATH_FROM_ID |
+ VOL_CAP_FMT_NO_VOLUME_SIZES;
+ fsap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] =
+ VOL_CAP_INT_SEARCHFS |
+ VOL_CAP_INT_ATTRLIST |
+ VOL_CAP_INT_NFSEXPORT |
+ VOL_CAP_INT_READDIRATTR |
+ VOL_CAP_INT_EXCHANGEDATA |
+ VOL_CAP_INT_COPYFILE |
+ VOL_CAP_INT_ALLOCATE |
+ VOL_CAP_INT_VOL_RENAME |
+ VOL_CAP_INT_ADVLOCK |
+ VOL_CAP_INT_FLOCK |
+ VOL_CAP_INT_EXTENDED_SECURITY |
+ VOL_CAP_INT_USERACCESS |
+ VOL_CAP_INT_MANLOCK |
+ VOL_CAP_INT_EXTENDED_ATTR |
+ VOL_CAP_INT_NAMEDSTREAMS;
+ fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
+ fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
+
+ VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
+ }
+
+ if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
+ fsap->f_attributes.validattr.commonattr =
+ ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID |
+ ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |
+ ATTR_CMN_PAROBJID |
+ ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME |
+ ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK |
+ ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID;
+ fsap->f_attributes.validattr.volattr =
+ ATTR_VOL_FSTYPE | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |
+ ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |
+ ATTR_VOL_OBJCOUNT | ATTR_VOL_MAXOBJCOUNT |
+ ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |
+ ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |
+ ATTR_VOL_ATTRIBUTES;
+ fsap->f_attributes.validattr.dirattr =
+ ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS;
+ fsap->f_attributes.validattr.fileattr =
+ ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |
+ ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE |
+ ATTR_FILE_DATALENGTH;
+ fsap->f_attributes.validattr.forkattr = 0;
+
+ fsap->f_attributes.nativeattr.commonattr =
+ ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID |
+ ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |
+ ATTR_CMN_PAROBJID |
+ ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME |
+ ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK |
+ ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID;
+ fsap->f_attributes.nativeattr.volattr =
+ ATTR_VOL_FSTYPE | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |
+ ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |
+ ATTR_VOL_OBJCOUNT | ATTR_VOL_MAXOBJCOUNT |
+ ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |
+ ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |
+ ATTR_VOL_ATTRIBUTES;
+ fsap->f_attributes.nativeattr.dirattr =
+ ATTR_DIR_MOUNTSTATUS;
+ fsap->f_attributes.nativeattr.fileattr =
+ ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |
+ ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE |
+ ATTR_FILE_DATALENGTH;
+ fsap->f_attributes.nativeattr.forkattr = 0;
+
+ VFSATTR_SET_SUPPORTED(fsap, f_attributes);
+ }
+
+ return 0;
+}
+
+static int
+routefs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx)
+{
+ return (0);
+}
+
+
+static int
+routefs_vget(__unused struct mount *mp, __unused ino64_t ino, __unused struct vnode **vpp, __unused vfs_context_t ctx)
+{
+ return ENOTSUP;
+}
+
+static int
+routefs_fhtovp (__unused struct mount *mp, __unused int fhlen, __unused unsigned char *fhp, __unused struct vnode **vpp, __unused vfs_context_t ctx)
+{
+ return (EINVAL);
+}
+
+
+static int
+routefs_vptofh (__unused struct vnode *vp, __unused int *fhlenp, __unused unsigned char *fhp, __unused vfs_context_t ctx)
+{
+ return (EINVAL);
+}
+
+static int
+routefs_sysctl(__unused int *name, __unused u_int namelen, __unused user_addr_t oldp,
+ __unused size_t *oldlenp, __unused user_addr_t newp,
+ __unused size_t newlen, __unused vfs_context_t ctx)
+{
+ return (ENOTSUP);
+}
+
+#include <sys/namei.h>
+#define MOBILE_DIR_PATH "/private/var/mobile"
+/*
+ * Function: routefs_kernel_mount
+ * Purpose:
+ * Mount routefs at the given mount point from within the kernel.
+ */
+int
+routefs_kernel_mount(char * routepath)
+{
+ int error = EINVAL;
+ vfs_context_t ctx = vfs_context_kernel();
+ char fsname[] = "routefs";
+ struct routefs_args args;
+ char mounthere[] = MOBILE_DIR_PATH; /* !const because of internal casting */
+
+ bzero(&args, sizeof(struct routefs_args));
+ strlcpy(args.route_path, routepath, MAXPATHLEN);
+ error = vnode_lookup(args.route_path, FREAD|O_DIRECTORY, &args.route_rvp, ctx);
+ if (error) {
+ goto out;
+ }
+
+ if (!vnode_isdir(args.route_rvp)) {
+ error = EACCES;
+ goto out;
+ }
+
+ error = kernel_mount(fsname, NULLVP, NULLVP, mounthere, &args, 0, MNT_DONTBROWSE, KERNEL_MOUNT_NOAUTH, ctx);
+ if (error) {
+ goto out;
+ }
+
+out:
+ if(args.route_rvp != NULLVP)
+ (void) vnode_put(args.route_rvp);
+ return (error);
+}
+
+struct vfsops routefs_vfsops = {
+ .vfs_mount = routefs_mount,
+ .vfs_start = routefs_start,
+ .vfs_unmount = routefs_unmount,
+ .vfs_root = routefs_root,
+ .vfs_getattr = routefs_vfs_getattr,
+ .vfs_sync = routefs_sync,
+ .vfs_vget = routefs_vget,
+ .vfs_fhtovp = routefs_fhtovp,
+ .vfs_vptofh = routefs_vptofh,
+ .vfs_init = routefs_init,
+ .vfs_sysctl = routefs_sysctl,
+ // There are other VFS ops that we do not support
+};
+
+static int routefserr_lookup(__unused struct vnop_lookup_args * args)
+{
+ return (ENOTSUP);
+}
+
+static int routefserr_setlabel(__unused struct vnop_setlabel_args * args)
+{
+ return (ENOTSUP);
+
+}
+
+#define VOPFUNC int (*)(void *)
+
+/* The following ops are used by directories and symlinks */
+int (**routefs_vnodeop_p)(void *);
+static struct vnodeopv_entry_desc routefs_vnodeop_entries[] = {
+ { &vnop_default_desc, (VOPFUNC)vn_default_error },
+ { &vnop_lookup_desc, (VOPFUNC)routefserr_lookup }, /* lookup */
+ { &vnop_create_desc, (VOPFUNC)err_create }, /* create */
+ { &vnop_whiteout_desc, (VOPFUNC)err_whiteout }, /* whiteout */
+ { &vnop_mknod_desc, (VOPFUNC)err_mknod }, /* mknod */
+ { &vnop_open_desc, (VOPFUNC)err_open }, /* open */
+ { &vnop_close_desc, (VOPFUNC)err_close }, /* close */
+ { &vnop_getattr_desc, (VOPFUNC)err_getattr }, /* getattr */
+ { &vnop_setattr_desc, (VOPFUNC)err_setattr }, /* setattr */
+ { &vnop_read_desc, (VOPFUNC)err_read }, /* read */
+ { &vnop_write_desc, (VOPFUNC)err_write }, /* write */
+ { &vnop_ioctl_desc, (VOPFUNC)err_ioctl }, /* ioctl */
+ { &vnop_select_desc, (VOPFUNC)err_select }, /* select */
+ { &vnop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */
+ { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
+ { &vnop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */
+ { &vnop_remove_desc, (VOPFUNC)err_remove }, /* remove */
+ { &vnop_link_desc, (VOPFUNC)err_link }, /* link */
+ { &vnop_rename_desc, (VOPFUNC)err_rename }, /* rename */
+ { &vnop_mkdir_desc, (VOPFUNC)err_mkdir }, /* mkdir */
+ { &vnop_rmdir_desc, (VOPFUNC)err_rmdir }, /* rmdir */
+ { &vnop_symlink_desc, (VOPFUNC)err_symlink }, /* symlink */
+ { &vnop_readdir_desc, (VOPFUNC)err_readdir }, /* readdir */
+ { &vnop_readlink_desc, (VOPFUNC)err_readlink }, /* readlink */
+ { &vnop_inactive_desc, (VOPFUNC)err_inactive }, /* inactive */
+ { &vnop_reclaim_desc, (VOPFUNC)err_reclaim }, /* reclaim */
+ { &vnop_strategy_desc, (VOPFUNC)err_strategy }, /* strategy */
+ { &vnop_pathconf_desc, (VOPFUNC)err_pathconf }, /* pathconf */
+ { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
+ { &vnop_bwrite_desc, (VOPFUNC)err_bwrite },
+ { &vnop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
+ { &vnop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
+ { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
+ { &vnop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff */
+ { &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */
+ { &vnop_blockmap_desc, (VOPFUNC)err_blockmap }, /* blockmap */
+#if CONFIG_MACF
+ { &vnop_setlabel_desc, (VOPFUNC)routefserr_setlabel }, /* setlabel */
+#endif
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc routefs_vnodeop_opv_desc =
+{ &routefs_vnodeop_p, routefs_vnodeop_entries };
+
+
+
if (dlil_verbose)
printf("%s: detaching\n", if_name(ifp));
+ /* Reset ECN enable/disable flags */
+ ifp->if_eflags &= ~IFEF_ECN_DISABLE;
+ ifp->if_eflags &= ~IFEF_ECN_ENABLE;
+
/*
* Remove ifnet from the ifnet_head, ifindex2ifnet[]; it will
* no longer be visible during lookups from this point.
* silently ignore facilities other than ours.
*/
flags &= IFNET_LOGF_DLIL;
- if (flags == 0 && (!ifp->if_log.flags & IFNET_LOGF_DLIL))
+ if (flags == 0 && (!(ifp->if_log.flags & IFNET_LOGF_DLIL)))
level = 0;
}
for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
- strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ);
+ bzero(outbuf, sizeof(outbuf));
+ strlcpy(outbuf, ifc->ifc_name,
+ min(strlen(ifc->ifc_name), IFNAMSIZ));
error = copyout(outbuf, dst, IFNAMSIZ);
if (error)
break;
};
struct if_tcp_ecn_perf_stat {
+ u_int64_t total_txpkts;
+ u_int64_t total_rxmitpkts;
+ u_int64_t total_rxpkts;
+ u_int64_t total_oopkts;
+ u_int64_t total_reorderpkts;
u_int64_t rtt_avg;
u_int64_t rtt_var;
- u_int64_t oo_percent;
u_int64_t sack_episodes;
+ u_int64_t rxmit_drop;
+ u_int64_t rst_drop;
+ u_int64_t oo_percent;
u_int64_t reorder_percent;
u_int64_t rxmit_percent;
- u_int64_t rxmit_drop;
};
struct if_tcp_ecn_stat {
u_int64_t ecn_fallback_synloss;
u_int64_t ecn_fallback_reorder;
u_int64_t ecn_fallback_ce;
+ u_int64_t ecn_off_conn;
+ u_int64_t ecn_total_conn;
struct if_tcp_ecn_perf_stat ecn_on;
struct if_tcp_ecn_perf_stat ecn_off;
};
ifp->if_delegated.subfamily = delegated_ifp->if_subfamily;
ifp->if_delegated.expensive =
delegated_ifp->if_eflags & IFEF_EXPENSIVE ? 1 : 0;
+
+ /*
+ * Propogate flags related to ECN from delegated interface
+ */
+ ifp->if_eflags &= ~(IFEF_ECN_ENABLE|IFEF_ECN_DISABLE);
+ ifp->if_eflags |= (delegated_ifp->if_eflags &
+ (IFEF_ECN_ENABLE|IFEF_ECN_DISABLE));
+
printf("%s: is now delegating %s (type 0x%x, family %u, "
"sub-family %u)\n", ifp->if_xname, delegated_ifp->if_xname,
delegated_ifp->if_type, delegated_ifp->if_family,
delegated_ifp->if_subfamily);
}
+
ifnet_lock_done(ifp);
if (odifp != NULL) {
return (TRUE);
}
+static inline bool
+necp_address_is_valid(struct sockaddr *address)
+{
+ if (address->sa_family == AF_INET) {
+ return (address->sa_len == sizeof(struct sockaddr_in));
+ } else if (address->sa_family == AF_INET6) {
+ return (address->sa_len == sizeof(struct sockaddr_in6));
+ } else {
+ return (FALSE);
+ }
+}
+
#define NECP_KERNEL_POLICY_SUBORDER_ID_TUNNEL_CONDITION 0
#define NECP_KERNEL_POLICY_SUBORDER_NON_ID_TUNNEL_CONDITION 1
#define NECP_KERNEL_POLICY_SUBORDER_ID_CONDITION 2
}
case NECP_POLICY_CONDITION_LOCAL_ADDR: {
struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value;
+ if (!necp_address_is_valid(&address_struct->address.sa)) {
+ break;
+ }
+
cond_local_prefix = address_struct->prefix;
memcpy(&cond_local_start, &address_struct->address, sizeof(address_struct->address));
master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START;
}
case NECP_POLICY_CONDITION_REMOTE_ADDR: {
struct necp_policy_condition_addr *address_struct = (struct necp_policy_condition_addr *)(void *)condition_value;
+ if (!necp_address_is_valid(&address_struct->address.sa)) {
+ break;
+ }
+
cond_remote_prefix = address_struct->prefix;
memcpy(&cond_remote_start, &address_struct->address, sizeof(address_struct->address));
master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START;
}
case NECP_POLICY_CONDITION_LOCAL_ADDR_RANGE: {
struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value;
+ if (!necp_address_is_valid(&address_struct->start_address.sa) ||
+ !necp_address_is_valid(&address_struct->end_address.sa)) {
+ break;
+ }
+
memcpy(&cond_local_start, &address_struct->start_address, sizeof(address_struct->start_address));
memcpy(&cond_local_end, &address_struct->end_address, sizeof(address_struct->end_address));
master_condition_mask |= NECP_KERNEL_CONDITION_LOCAL_START;
}
case NECP_POLICY_CONDITION_REMOTE_ADDR_RANGE: {
struct necp_policy_condition_addr_range *address_struct = (struct necp_policy_condition_addr_range *)(void *)condition_value;
+ if (!necp_address_is_valid(&address_struct->start_address.sa) ||
+ !necp_address_is_valid(&address_struct->end_address.sa)) {
+ break;
+ }
+
memcpy(&cond_remote_start, &address_struct->start_address, sizeof(address_struct->start_address));
memcpy(&cond_remote_end, &address_struct->end_address, sizeof(address_struct->end_address));
master_condition_mask |= NECP_KERNEL_CONDITION_REMOTE_START;
if (necp_kernel_socket_policies_condition_mask & NECP_KERNEL_ADDRESS_TYPE_CONDITIONS) {
if (inp->inp_vflag & INP_IPV4) {
if (override_local_addr) {
- memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+ if (override_local_addr->sa_len <= sizeof(struct sockaddr_in)) {
+ memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+ }
} else {
((struct sockaddr_in *)&info->local_addr)->sin_family = AF_INET;
((struct sockaddr_in *)&info->local_addr)->sin_len = sizeof(struct sockaddr_in);
}
if (override_remote_addr) {
- memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+ if (override_remote_addr->sa_len <= sizeof(struct sockaddr_in)) {
+ memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+ }
} else {
((struct sockaddr_in *)&info->remote_addr)->sin_family = AF_INET;
((struct sockaddr_in *)&info->remote_addr)->sin_len = sizeof(struct sockaddr_in);
}
} else if (inp->inp_vflag & INP_IPV6) {
if (override_local_addr) {
- memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+ if (override_local_addr->sa_len <= sizeof(struct sockaddr_in6)) {
+ memcpy(&info->local_addr, override_local_addr, override_local_addr->sa_len);
+ }
} else {
((struct sockaddr_in6 *)&info->local_addr)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&info->local_addr)->sin6_len = sizeof(struct sockaddr_in6);
}
if (override_remote_addr) {
- memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+ if (override_remote_addr->sa_len <= sizeof(struct sockaddr_in6)) {
+ memcpy(&info->remote_addr, override_remote_addr, override_remote_addr->sa_len);
+ }
} else {
((struct sockaddr_in6 *)&info->remote_addr)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&info->remote_addr)->sin6_len = sizeof(struct sockaddr_in6);
break;
case IFRTYPE_FUNCTIONAL_CELLULAR:
flags |= NSTAT_IFNET_IS_CELLULAR;
+ if (inp->inp_socket != NULL &&
+ (inp->inp_socket->so_flags1 & SOF1_CELLFALLBACK))
+ flags |= NSTAT_IFNET_VIA_CELLFALLBACK;
break;
}
static u_int64_t nstat_ifnet_last_report_time = 0;
extern int tcp_report_stats_interval;
+static void
+nstat_ifnet_compute_percentages(struct if_tcp_ecn_perf_stat *ifst)
+{
+ /* Retransmit percentage */
+ if (ifst->total_rxmitpkts > 0 && ifst->total_txpkts > 0) {
+ /* shift by 10 for precision */
+ ifst->rxmit_percent =
+ ((ifst->total_rxmitpkts << 10) * 100) / ifst->total_txpkts;
+ } else {
+ ifst->rxmit_percent = 0;
+ }
+
+ /* Out-of-order percentage */
+ if (ifst->total_oopkts > 0 && ifst->total_rxpkts > 0) {
+ /* shift by 10 for precision */
+ ifst->oo_percent =
+ ((ifst->total_oopkts << 10) * 100) / ifst->total_rxpkts;
+ } else {
+ ifst->oo_percent = 0;
+ }
+
+ /* Reorder percentage */
+ if (ifst->total_reorderpkts > 0 &&
+ (ifst->total_txpkts + ifst->total_rxpkts) > 0) {
+ /* shift by 10 for precision */
+ ifst->reorder_percent =
+ ((ifst->total_reorderpkts << 10) * 100) /
+ (ifst->total_txpkts + ifst->total_rxpkts);
+ } else {
+ ifst->reorder_percent = 0;
+ }
+}
+
+static void
+nstat_ifnet_normalize_counter(struct if_tcp_ecn_stat *if_st)
+{
+ u_int64_t ecn_on_conn, ecn_off_conn;
+
+ if (if_st == NULL)
+ return;
+ ecn_on_conn = if_st->ecn_client_success +
+ if_st->ecn_server_success;
+ ecn_off_conn = if_st->ecn_off_conn +
+ (if_st->ecn_client_setup - if_st->ecn_client_success) +
+ (if_st->ecn_server_setup - if_st->ecn_server_success);
+
+ /*
+ * report sack episodes, rst_drop and rxmit_drop
+ * as a ratio per connection, shift by 10 for precision
+ */
+ if (ecn_on_conn > 0) {
+ if_st->ecn_on.sack_episodes =
+ (if_st->ecn_on.sack_episodes << 10) / ecn_on_conn;
+ if_st->ecn_on.rst_drop =
+ (if_st->ecn_on.rst_drop << 10) * 100 / ecn_on_conn;
+ if_st->ecn_on.rxmit_drop =
+ (if_st->ecn_on.rxmit_drop << 10) * 100 / ecn_on_conn;
+ } else {
+ /* set to zero, just in case */
+ if_st->ecn_on.sack_episodes = 0;
+ if_st->ecn_on.rst_drop = 0;
+ if_st->ecn_on.rxmit_drop = 0;
+ }
+
+ if (ecn_off_conn > 0) {
+ if_st->ecn_off.sack_episodes =
+ (if_st->ecn_off.sack_episodes << 10) / ecn_off_conn;
+ if_st->ecn_off.rst_drop =
+ (if_st->ecn_off.rst_drop << 10) * 100 / ecn_off_conn;
+ if_st->ecn_off.rxmit_drop =
+ (if_st->ecn_off.rxmit_drop << 10) * 100 / ecn_off_conn;
+ } else {
+ if_st->ecn_off.sack_episodes = 0;
+ if_st->ecn_off.rst_drop = 0;
+ if_st->ecn_off.rxmit_drop = 0;
+ }
+ if_st->ecn_total_conn = ecn_off_conn + ecn_on_conn;
+}
+
void
nstat_ifnet_report_ecn_stats(void)
{
ifp->if_ipv4_stat->timestamp < last_report_time)
goto v6;
st->ifnet_proto = NSTAT_IFNET_ECN_PROTO_IPV4;
+ /* compute percentages using packet counts */
+ nstat_ifnet_compute_percentages(&ifp->if_ipv4_stat->ecn_on);
+ nstat_ifnet_compute_percentages(&ifp->if_ipv4_stat->ecn_off);
+ nstat_ifnet_normalize_counter(ifp->if_ipv4_stat);
+
bcopy(ifp->if_ipv4_stat, &st->ecn_stat,
sizeof(st->ecn_stat));
nstat_sysinfo_send_data(&data);
ifp->if_ipv6_stat->timestamp < last_report_time)
continue;
st->ifnet_proto = NSTAT_IFNET_ECN_PROTO_IPV6;
+
+ /* compute percentages using packet counts */
+ nstat_ifnet_compute_percentages(&ifp->if_ipv6_stat->ecn_on);
+ nstat_ifnet_compute_percentages(&ifp->if_ipv6_stat->ecn_off);
+ nstat_ifnet_normalize_counter(ifp->if_ipv6_stat);
+
bcopy(ifp->if_ipv6_stat, &st->ecn_stat,
sizeof(st->ecn_stat));
nstat_sysinfo_send_data(&data);
nstat_sysinfo_data *data)
{
nstat_msg_sysinfo_counts *syscnt = NULL;
- size_t allocsize = 0, countsize = 0, nkeyvals = 0;
+ size_t allocsize = 0, countsize = 0, nkeyvals = 0, finalsize = 0;
nstat_sysinfo_keyval *kv;
errno_t result = 0;
size_t i = 0;
allocsize = offsetof(nstat_msg_sysinfo_counts, counts);
countsize = offsetof(nstat_sysinfo_counts, nstat_sysinfo_keyvals);
+ finalsize = allocsize;
/* get number of key-vals for each kind of stat */
switch (data->flags)
case NSTAT_SYSINFO_IFNET_ECN_STATS:
nkeyvals = (sizeof(struct if_tcp_ecn_stat) /
sizeof(u_int64_t));
- /* One less because we are not going to send timestamp */
- nkeyvals -= 1;
+
/* Two more keys for ifnet type and proto */
nkeyvals += 2;
break;
if (syscnt == NULL)
return;
bzero(syscnt, allocsize);
-
- syscnt->hdr.type = NSTAT_MSG_TYPE_SYSINFO_COUNTS;
- syscnt->hdr.length = allocsize;
- syscnt->counts.nstat_sysinfo_len = countsize;
kv = (nstat_sysinfo_keyval *) &syscnt->counts.nstat_sysinfo_keyvals;
switch (data->flags)
nstat_set_keyval_scalar(&kv[i++],
NSTAT_SYSINFO_TFO_BLACKHOLE,
data->u.tcp_stats.tfo_blackhole);
-
VERIFY(i == nkeyvals);
break;
}
nstat_set_keyval_scalar(&kv[i++],
NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_DROP,
data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rxmit_drop);
- VERIFY(i == nkeyvals);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_TXPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_txpkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_RXMTPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_rxmitpkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_RXPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_rxpkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_OOPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_oopkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_ON_DROP_RST,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rst_drop);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_TXPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_txpkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_RXMTPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_rxmitpkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_RXPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_rxpkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_OOPKTS,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_oopkts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_OFF_DROP_RST,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rst_drop);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_IFNET_TOTAL_CONN,
+ data->u.ifnet_ecn_stats.ecn_stat.ecn_total_conn);
break;
}
}
-
if (syscnt != NULL)
{
+ VERIFY(i > 0 && i <= nkeyvals);
+ countsize = offsetof(nstat_sysinfo_counts,
+ nstat_sysinfo_keyvals) +
+ sizeof(nstat_sysinfo_keyval) * i;
+ finalsize += countsize;
+ syscnt->hdr.type = NSTAT_MSG_TYPE_SYSINFO_COUNTS;
+ syscnt->hdr.length = finalsize;
+ syscnt->counts.nstat_sysinfo_len = countsize;
+
result = ctl_enqueuedata(control->ncs_kctl,
- control->ncs_unit, syscnt, allocsize, CTL_DATA_EOR);
+ control->ncs_unit, syscnt, finalsize, CTL_DATA_EOR);
if (result != 0)
{
nstat_stats.nstat_sysinfofailures += 1;
nstat_control_state *state)
{
errno_t result = 0;
- if (state->ncs_accumulated && mbuf_len(state->ncs_accumulated))
+ if (state->ncs_accumulated != NULL && mbuf_len(state->ncs_accumulated) > 0)
{
mbuf_pkthdr_setlen(state->ncs_accumulated, mbuf_len(state->ncs_accumulated));
result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, state->ncs_accumulated, CTL_DATA_EOR);
- if (result != 0 && nstat_debug)
+ if (result != 0)
{
nstat_stats.nstat_flush_accumulated_msgs_failures++;
if (nstat_debug != 0)
,NSTAT_SYSINFO_ECN_IFNET_OFF_REORDER_PERCENT = 78
,NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_PERCENT = 79
,NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_DROP = 80
+ ,NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_TXPKTS = 81
+ ,NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_RXMTPKTS = 82
+ ,NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_RXPKTS = 83
+ ,NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_OOPKTS = 84
+ ,NSTAT_SYSINFO_ECN_IFNET_ON_DROP_RST = 85
+ ,NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_TXPKTS = 86
+ ,NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_RXMTPKTS = 87
+ ,NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_RXPKTS = 88
+ ,NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_OOPKTS = 89
+ ,NSTAT_SYSINFO_ECN_IFNET_OFF_DROP_RST = 90
+ ,NSTAT_SYSINFO_ECN_IFNET_TOTAL_CONN = 91
+// NSTAT_SYSINFO_ENUM_VERSION must be updated any time a value is added
+#define NSTAT_SYSINFO_ENUM_VERSION 20151208
};
#pragma mark -- Network Statistics Providers --
#define NSTAT_IFNET_IS_AWDL 0x20
#define NSTAT_IFNET_IS_EXPENSIVE 0x40
#define NSTAT_IFNET_IS_VPN 0x80
+#define NSTAT_IFNET_VIA_CELLFALLBACK 0x100
enum
*m0 = pd.mp;
PF_APPLE_UPDATE_PDESC_IPv4();
- if (action == PF_PASS && h->ip_hl > 5 &&
- !((s && s->allow_opts) || r->allow_opts)) {
- action = PF_DROP;
- REASON_SET(&reason, PFRES_IPOPTIONS);
- log = 1;
- DPFPRINTF(PF_DEBUG_MISC,
- ("pf: dropping packet with ip options [hlen=%u]\n",
- (unsigned int) h->ip_hl));
- }
+ if (action != PF_DROP) {
+ if (action == PF_PASS && h->ip_hl > 5 &&
+ !((s && s->allow_opts) || r->allow_opts)) {
+ action = PF_DROP;
+ REASON_SET(&reason, PFRES_IPOPTIONS);
+ log = 1;
+ DPFPRINTF(PF_DEBUG_MISC,
+ ("pf: dropping packet with ip options [hlen=%u]\n",
+ (unsigned int) h->ip_hl));
+ }
- if ((s && s->tag) || PF_RTABLEID_IS_VALID(r->rtableid) ||
- (pd.pktflags & PKTF_FLOW_ID))
- (void) pf_tag_packet(m, pd.pf_mtag, s ? s->tag : 0,
- r->rtableid, &pd);
+ if ((s && s->tag) || PF_RTABLEID_IS_VALID(r->rtableid) ||
+ (pd.pktflags & PKTF_FLOW_ID))
+ (void) pf_tag_packet(m, pd.pf_mtag, s ? s->tag : 0,
+ r->rtableid, &pd);
- if (action == PF_PASS) {
+ if (action == PF_PASS) {
#if PF_ALTQ
- if (altq_allowed && r->qid) {
- if (pqid || (pd.tos & IPTOS_LOWDELAY))
- pd.pf_mtag->pftag_qid = r->pqid;
- else
- pd.pf_mtag->pftag_qid = r->qid;
- }
+ if (altq_allowed && r->qid) {
+ if (pqid || (pd.tos & IPTOS_LOWDELAY))
+ pd.pf_mtag->pftag_qid = r->pqid;
+ else
+ pd.pf_mtag->pftag_qid = r->qid;
+ }
#endif /* PF_ALTQ */
#if PF_ECN
- /* add hints for ecn */
- pd.pf_mtag->pftag_hdr = h;
- /* record address family */
- pd.pf_mtag->pftag_flags &= ~PF_TAG_HDR_INET6;
- pd.pf_mtag->pftag_flags |= PF_TAG_HDR_INET;
+ /* add hints for ecn */
+ pd.pf_mtag->pftag_hdr = h;
+ /* record address family */
+ pd.pf_mtag->pftag_flags &= ~PF_TAG_HDR_INET6;
+ pd.pf_mtag->pftag_flags |= PF_TAG_HDR_INET;
#endif /* PF_ECN */
- /* record protocol */
- m->m_pkthdr.pkt_proto = pd.proto;
- }
+ /* record protocol */
+ m->m_pkthdr.pkt_proto = pd.proto;
- /*
- * connections redirected to loopback should not match sockets
- * bound specifically to loopback due to security implications,
- * see tcp_input() and in_pcblookup_listen().
- */
- if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP ||
- pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL &&
- (s->nat_rule.ptr->action == PF_RDR ||
- s->nat_rule.ptr->action == PF_BINAT) &&
- (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
- pd.pf_mtag->pftag_flags |= PF_TAG_TRANSLATE_LOCALHOST;
+ /*
+ * connections redirected to loopback should not match sockets
+ * bound specifically to loopback due to security implications,
+ * see tcp_input() and in_pcblookup_listen().
+ */
+ if (dir == PF_IN && (pd.proto == IPPROTO_TCP ||
+ pd.proto == IPPROTO_UDP) && s != NULL &&
+ s->nat_rule.ptr != NULL &&
+ (s->nat_rule.ptr->action == PF_RDR ||
+ s->nat_rule.ptr->action == PF_BINAT) &&
+ (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT)
+ == IN_LOOPBACKNET)
+ pd.pf_mtag->pftag_flags |= PF_TAG_TRANSLATE_LOCALHOST;
+ }
+ }
if (log) {
struct pf_rule *lr;
}
/* handle dangerous IPv6 extension headers. */
- if (action == PF_PASS && rh_cnt &&
- !((s && s->allow_opts) || r->allow_opts)) {
- action = PF_DROP;
- REASON_SET(&reason, PFRES_IPOPTIONS);
- log = 1;
- DPFPRINTF(PF_DEBUG_MISC,
- ("pf: dropping packet with dangerous v6 headers\n"));
- }
+ if (action != PF_DROP) {
+ if (action == PF_PASS && rh_cnt &&
+ !((s && s->allow_opts) || r->allow_opts)) {
+ action = PF_DROP;
+ REASON_SET(&reason, PFRES_IPOPTIONS);
+ log = 1;
+ DPFPRINTF(PF_DEBUG_MISC,
+ ("pf: dropping packet with dangerous v6 headers\n"));
+ }
- if ((s && s->tag) || PF_RTABLEID_IS_VALID(r->rtableid) ||
- (pd.pktflags & PKTF_FLOW_ID))
- (void) pf_tag_packet(m, pd.pf_mtag, s ? s->tag : 0,
- r->rtableid, &pd);
+ if ((s && s->tag) || PF_RTABLEID_IS_VALID(r->rtableid) ||
+ (pd.pktflags & PKTF_FLOW_ID))
+ (void) pf_tag_packet(m, pd.pf_mtag, s ? s->tag : 0,
+ r->rtableid, &pd);
- if (action == PF_PASS) {
+ if (action == PF_PASS) {
#if PF_ALTQ
- if (altq_allowed && r->qid) {
- if (pd.tos & IPTOS_LOWDELAY)
- pd.pf_mtag->pftag_qid = r->pqid;
- else
- pd.pf_mtag->pftag_qid = r->qid;
- }
+ if (altq_allowed && r->qid) {
+ if (pd.tos & IPTOS_LOWDELAY)
+ pd.pf_mtag->pftag_qid = r->pqid;
+ else
+ pd.pf_mtag->pftag_qid = r->qid;
+ }
#endif /* PF_ALTQ */
#if PF_ECN
- /* add hints for ecn */
- pd.pf_mtag->pftag_hdr = h;
- /* record address family */
- pd.pf_mtag->pftag_flags &= ~PF_TAG_HDR_INET;
- pd.pf_mtag->pftag_flags |= PF_TAG_HDR_INET6;
+ /* add hints for ecn */
+ pd.pf_mtag->pftag_hdr = h;
+ /* record address family */
+ pd.pf_mtag->pftag_flags &= ~PF_TAG_HDR_INET;
+ pd.pf_mtag->pftag_flags |= PF_TAG_HDR_INET6;
#endif /* PF_ECN */
- /* record protocol */
- m->m_pkthdr.pkt_proto = pd.proto;
+ /* record protocol */
+ m->m_pkthdr.pkt_proto = pd.proto;
+ if (dir == PF_IN && (pd.proto == IPPROTO_TCP ||
+ pd.proto == IPPROTO_UDP) && s != NULL &&
+ s->nat_rule.ptr != NULL &&
+ (s->nat_rule.ptr->action == PF_RDR ||
+ s->nat_rule.ptr->action == PF_BINAT) &&
+ IN6_IS_ADDR_LOOPBACK(&pd.dst->v6))
+ pd.pf_mtag->pftag_flags |= PF_TAG_TRANSLATE_LOCALHOST;
+ }
}
- if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP ||
- pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL &&
- (s->nat_rule.ptr->action == PF_RDR ||
- s->nat_rule.ptr->action == PF_BINAT) &&
- IN6_IS_ADDR_LOOPBACK(&pd.dst->v6))
- pd.pf_mtag->pftag_flags |= PF_TAG_TRANSLATE_LOCALHOST;
if (log) {
struct pf_rule *lr;
printf("%s: pf_find_mtag returned NULL(1)\n", __func__);
if ((pd->pf_mtag = pf_get_mtag(m)) == NULL) {
m_freem(m);
- *m0 = NULL;
+ m = *m0 = NULL;
goto no_mem;
}
}
printf("%s: pf_find_mtag returned NULL(2)\n", __func__);
if ((pd->pf_mtag = pf_get_mtag(m)) == NULL) {
m_freem(m);
- *m0 = NULL;
+ m = *m0 = NULL;
goto no_mem;
}
}
/*
- * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#define FLOW_DIVERT_TUNNEL_RD_CLOSED 0x00000008
#define FLOW_DIVERT_TUNNEL_WR_CLOSED 0x00000010
#define FLOW_DIVERT_TRANSFERRED 0x00000020
+#define FLOW_DIVERT_HAS_HMAC 0x00000040
#define FDLOG(level, pcb, format, ...) do { \
if (level <= (pcb)->log_level) { \
if (fd_cb->connect_token != NULL) {
mbuf_freem(fd_cb->connect_token);
}
+ if (fd_cb->connect_packet != NULL) {
+ mbuf_freem(fd_cb->connect_packet);
+ }
+ if (fd_cb->app_data != NULL) {
+ FREE(fd_cb->app_data, M_TEMP);
+ }
FREE_ZONE(fd_cb, sizeof(*fd_cb), M_FLOW_DIVERT_PCB);
}
return NULL_TRIE_IDX;
}
+struct uuid_search_info {
+ uuid_t target_uuid;
+ char *found_signing_id;
+ boolean_t found_multiple_signing_ids;
+ proc_t found_proc;
+};
+
+static int
+flow_divert_find_proc_by_uuid_callout(proc_t p, void *arg)
+{
+ struct uuid_search_info *info = (struct uuid_search_info *)arg;
+ int result = PROC_RETURNED_DONE; /* By default, we didn't find the process */
+
+ if (info->found_signing_id != NULL) {
+ if (!info->found_multiple_signing_ids) {
+ /* All processes that were found had the same signing identifier, so just claim this first one and be done. */
+ info->found_proc = p;
+ result = PROC_CLAIMED_DONE;
+ } else {
+ uuid_string_t uuid_str;
+ uuid_unparse(info->target_uuid, uuid_str);
+ FDLOG(LOG_WARNING, &nil_pcb, "Found multiple processes with UUID %s with different signing identifiers", uuid_str);
+ }
+ FREE(info->found_signing_id, M_TEMP);
+ info->found_signing_id = NULL;
+ }
+
+ if (result == PROC_RETURNED_DONE) {
+ uuid_string_t uuid_str;
+ uuid_unparse(info->target_uuid, uuid_str);
+ FDLOG(LOG_WARNING, &nil_pcb, "Failed to find a process with UUID %s", uuid_str);
+ }
+
+ return result;
+}
+
+static int
+flow_divert_find_proc_by_uuid_filter(proc_t p, void *arg)
+{
+ struct uuid_search_info *info = (struct uuid_search_info *)arg;
+ int include = 0;
+
+ if (info->found_multiple_signing_ids) {
+ return include;
+ }
+
+ include = (uuid_compare(p->p_uuid, info->target_uuid) == 0);
+ if (include) {
+ const char *signing_id = cs_identity_get(p);
+ if (signing_id != NULL) {
+ FDLOG(LOG_INFO, &nil_pcb, "Found process %d with signing identifier %s", p->p_pid, signing_id);
+ size_t signing_id_size = strlen(signing_id) + 1;
+ if (info->found_signing_id == NULL) {
+ MALLOC(info->found_signing_id, char *, signing_id_size, M_TEMP, M_WAITOK);
+ memcpy(info->found_signing_id, signing_id, signing_id_size);
+ } else if (memcmp(signing_id, info->found_signing_id, signing_id_size)) {
+ info->found_multiple_signing_ids = TRUE;
+ }
+ } else {
+ info->found_multiple_signing_ids = TRUE;
+ }
+ include = !info->found_multiple_signing_ids;
+ }
+
+ return include;
+}
+
+static proc_t
+flow_divert_find_proc_by_uuid(uuid_t uuid)
+{
+ struct uuid_search_info info;
+
+ if (LOG_INFO <= nil_pcb.log_level) {
+ uuid_string_t uuid_str;
+ uuid_unparse(uuid, uuid_str);
+ FDLOG(LOG_INFO, &nil_pcb, "Looking for process with UUID %s", uuid_str);
+ }
+
+ memset(&info, 0, sizeof(info));
+ info.found_proc = PROC_NULL;
+ uuid_copy(info.target_uuid, uuid);
+
+ proc_iterate(PROC_ALLPROCLIST, flow_divert_find_proc_by_uuid_callout, &info, flow_divert_find_proc_by_uuid_filter, &info);
+
+ return info.found_proc;
+}
+
static int
-flow_divert_get_src_proc(struct socket *so, proc_t *proc, boolean_t match_delegate)
+flow_divert_get_src_proc(struct socket *so, proc_t *proc)
{
int release = 0;
- if (!match_delegate &&
- (so->so_flags & SOF_DELEGATED) &&
- (*proc == PROC_NULL || (*proc)->p_pid != so->e_pid))
- {
- *proc = proc_find(so->e_pid);
- release = 1;
+ if (so->so_flags & SOF_DELEGATED) {
+ if ((*proc)->p_pid != so->e_pid) {
+ *proc = proc_find(so->e_pid);
+ release = 1;
+ } else if (uuid_compare((*proc)->p_uuid, so->e_uuid)) {
+ *proc = flow_divert_find_proc_by_uuid(so->e_uuid);
+ release = 1;
+ }
} else if (*proc == PROC_NULL) {
*proc = current_proc();
}
}
static int
-flow_divert_send_connect(struct flow_divert_pcb *fd_cb, struct sockaddr *to, mbuf_t connect_packet)
+flow_divert_create_connect_packet(struct flow_divert_pcb *fd_cb, struct sockaddr *to, struct socket *so, proc_t p, mbuf_t *out_connect_packet)
{
int error = 0;
int flow_type = 0;
+ char *signing_id = NULL;
+ int free_signing_id = 0;
+ mbuf_t connect_packet = NULL;
+
+ error = flow_divert_packet_init(fd_cb, FLOW_DIVERT_PKT_CONNECT, &connect_packet);
+ if (error) {
+ goto done;
+ }
+
+ error = EPERM;
+
+ if (fd_cb->connect_token != NULL && (fd_cb->flags & FLOW_DIVERT_HAS_HMAC)) {
+ uint32_t sid_size = 0;
+ int find_error = flow_divert_packet_get_tlv(fd_cb->connect_token, 0, FLOW_DIVERT_TLV_SIGNING_ID, 0, NULL, &sid_size);
+ if (find_error == 0 && sid_size > 0) {
+ MALLOC(signing_id, char *, sid_size + 1, M_TEMP, M_WAITOK | M_ZERO);
+ if (signing_id != NULL) {
+ flow_divert_packet_get_tlv(fd_cb->connect_token, 0, FLOW_DIVERT_TLV_SIGNING_ID, sid_size, signing_id, NULL);
+ FDLOG(LOG_INFO, fd_cb, "Got %s from token", signing_id);
+ free_signing_id = 1;
+ }
+ }
+ }
+
+ socket_unlock(so, 0);
+ if (g_signing_id_trie.root != NULL_TRIE_IDX) {
+ proc_t src_proc = p;
+ int release_proc = 0;
+
+ if (signing_id == NULL) {
+ release_proc = flow_divert_get_src_proc(so, &src_proc);
+ if (src_proc != PROC_NULL) {
+ proc_lock(src_proc);
+ if (src_proc->p_csflags & CS_VALID) {
+ const char * cs_id;
+ cs_id = cs_identity_get(src_proc);
+ signing_id = __DECONST(char *, cs_id);
+ } else {
+ FDLOG0(LOG_WARNING, fd_cb, "Signature is invalid");
+ }
+ } else {
+ FDLOG0(LOG_WARNING, fd_cb, "Failed to determine the current proc");
+ }
+ } else {
+ src_proc = PROC_NULL;
+ }
+
+ if (signing_id != NULL) {
+ uint16_t result = NULL_TRIE_IDX;
+ lck_rw_lock_shared(&g_flow_divert_group_lck);
+ result = flow_divert_trie_search(&g_signing_id_trie, (uint8_t *)signing_id);
+ lck_rw_done(&g_flow_divert_group_lck);
+ if (result != NULL_TRIE_IDX) {
+ error = 0;
+ FDLOG(LOG_INFO, fd_cb, "%s matched", signing_id);
+
+ error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_SIGNING_ID, strlen(signing_id), signing_id);
+ if (error == 0) {
+ if (src_proc != PROC_NULL) {
+ unsigned char cdhash[SHA1_RESULTLEN];
+ error = proc_getcdhash(src_proc, cdhash);
+ if (error == 0) {
+ error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_CDHASH, sizeof(cdhash), cdhash);
+ if (error) {
+ FDLOG(LOG_ERR, fd_cb, "failed to append the cdhash: %d", error);
+ }
+ } else {
+ FDLOG(LOG_ERR, fd_cb, "failed to get the cdhash: %d", error);
+ }
+ }
+ } else {
+ FDLOG(LOG_ERR, fd_cb, "failed to append the signing ID: %d", error);
+ }
+ } else {
+ FDLOG(LOG_WARNING, fd_cb, "%s did not match", signing_id);
+ }
+ } else {
+ FDLOG0(LOG_WARNING, fd_cb, "Failed to get the code signing identity");
+ }
+
+ if (src_proc != PROC_NULL) {
+ proc_unlock(src_proc);
+ if (release_proc) {
+ proc_rele(src_proc);
+ }
+ }
+ } else {
+ FDLOG0(LOG_WARNING, fd_cb, "The signing ID trie is empty");
+ }
+ socket_lock(so, 0);
+
+ if (free_signing_id) {
+ FREE(signing_id, M_TEMP);
+ }
+
+ if (error) {
+ goto done;
+ }
error = flow_divert_packet_append_tlv(connect_packet,
FLOW_DIVERT_TLV_TRAFFIC_CLASS,
}
}
+ if (fd_cb->local_address != NULL) {
+ error = EALREADY;
+ goto done;
+ } else {
+ struct inpcb *inp = sotoinpcb(so);
+ if (flow_divert_has_pcb_local_address(inp)) {
+ error = flow_divert_inp_to_sockaddr(inp, &fd_cb->local_address);
+ if (error) {
+ FDLOG0(LOG_ERR, fd_cb, "failed to get the local socket address.");
+ goto done;
+ }
+ }
+ }
+
if (fd_cb->local_address != NULL) {
/* socket is bound. */
- error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_LOCAL_ADDR,
- sizeof(struct sockaddr_storage), fd_cb->local_address);
- if (error) {
- goto done;
- }
- }
+ error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_LOCAL_ADDR,
+ sizeof(struct sockaddr_storage), fd_cb->local_address);
+ if (error) {
+ goto done;
+ }
+ }
- error = flow_divert_send_packet(fd_cb, connect_packet, TRUE);
- if (error) {
- goto done;
+ if (so->so_flags1 & SOF1_DATA_IDEMPOTENT) {
+ uint32_t flags = FLOW_DIVERT_TOKEN_FLAG_TFO;
+ error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_FLAGS, sizeof(flags), &flags);
+ if (error) {
+ goto done;
+ }
}
done:
+ if (!error) {
+ *out_connect_packet = connect_packet;
+ } else if (connect_packet != NULL) {
+ mbuf_freem(connect_packet);
+ }
+
return error;
}
size_t sent = 0;
mbuf_t remaining_data = data;
mbuf_t pkt_data = NULL;
- while (sent < to_send) {
+ while (sent < to_send && remaining_data != NULL) {
size_t pkt_data_len;
pkt_data = remaining_data;
lck_rw_done(&old_group->lck);
fd_cb->send_window = ntohl(send_window);
- flow_divert_send_buffered_data(fd_cb, FALSE);
set_socket_state:
if (!connect_error && !error) {
}
flow_divert_disconnect_socket(fd_cb->so);
} else {
+ flow_divert_send_buffered_data(fd_cb, FALSE);
soisconnected(fd_cb->so);
}
struct sockaddr_storage local_address;
int out_if_index = 0;
struct sockaddr_storage remote_address;
+ uint32_t app_data_length = 0;
FDLOG0(LOG_INFO, fd_cb, "received a properties update");
error = flow_divert_packet_get_tlv(packet, offset, FLOW_DIVERT_TLV_LOCAL_ADDR, sizeof(local_address), &local_address, NULL);
if (error) {
- FDLOG0(LOG_INFO, fd_cb, "No local address provided");
+ FDLOG0(LOG_INFO, fd_cb, "No local address provided in properties update");
}
error = flow_divert_packet_get_tlv(packet, offset, FLOW_DIVERT_TLV_REMOTE_ADDR, sizeof(remote_address), &remote_address, NULL);
if (error) {
- FDLOG0(LOG_INFO, fd_cb, "No remote address provided");
+ FDLOG0(LOG_INFO, fd_cb, "No remote address provided in properties update");
}
error = flow_divert_packet_get_tlv(packet, offset, FLOW_DIVERT_TLV_OUT_IF_INDEX, sizeof(out_if_index), &out_if_index, NULL);
if (error) {
- FDLOG0(LOG_INFO, fd_cb, "No output if index provided");
+ FDLOG0(LOG_INFO, fd_cb, "No output if index provided in properties update");
+ }
+
+ error = flow_divert_packet_get_tlv(packet, offset, FLOW_DIVERT_TLV_APP_DATA, 0, NULL, &app_data_length);
+ if (error) {
+ FDLOG0(LOG_INFO, fd_cb, "No application data provided in properties update");
}
FDLOCK(fd_cb);
if (fd_cb->so != NULL) {
- struct inpcb *inp = NULL;
- struct ifnet *ifp = NULL;
-
socket_lock(fd_cb->so, 0);
- inp = sotoinpcb(fd_cb->so);
-
if (local_address.ss_family != 0) {
if (local_address.ss_len > sizeof(local_address)) {
local_address.ss_len = sizeof(local_address);
}
+ if (fd_cb->local_address != NULL) {
+ FREE(fd_cb->local_address, M_SONAME);
+ fd_cb->local_address = NULL;
+ }
fd_cb->local_address = dup_sockaddr((struct sockaddr *)&local_address, 1);
}
if (remote_address.ss_len > sizeof(remote_address)) {
remote_address.ss_len = sizeof(remote_address);
}
+ if (fd_cb->remote_address != NULL) {
+ FREE(fd_cb->remote_address, M_SONAME);
+ fd_cb->remote_address = NULL;
+ }
fd_cb->remote_address = dup_sockaddr((struct sockaddr *)&remote_address, 1);
}
- ifnet_head_lock_shared();
- if (out_if_index > 0 && out_if_index <= if_index) {
- ifp = ifindex2ifnet[out_if_index];
+ if (out_if_index > 0) {
+ struct inpcb *inp = NULL;
+ struct ifnet *ifp = NULL;
+
+ inp = sotoinpcb(fd_cb->so);
+
+ ifnet_head_lock_shared();
+ if (out_if_index <= if_index) {
+ ifp = ifindex2ifnet[out_if_index];
+ }
+
+ if (ifp != NULL) {
+ inp->inp_last_outifp = ifp;
+ }
+ ifnet_head_done();
}
- if (ifp != NULL) {
- inp->inp_last_outifp = ifp;
+ if (app_data_length > 0) {
+ uint8_t *app_data = NULL;
+ MALLOC(app_data, uint8_t *, app_data_length, M_TEMP, M_WAITOK);
+ if (app_data != NULL) {
+ error = flow_divert_packet_get_tlv(packet, offset, FLOW_DIVERT_TLV_APP_DATA, app_data_length, app_data, NULL);
+ if (error == 0) {
+ if (fd_cb->app_data != NULL) {
+ FREE(fd_cb->app_data, M_TEMP);
+ }
+ fd_cb->app_data = app_data;
+ fd_cb->app_data_length = app_data_length;
+ } else {
+ FDLOG(LOG_ERR, fd_cb, "Failed to copy %u bytes of application data from the properties update packet", app_data_length);
+ FREE(app_data, M_TEMP);
+ }
+ } else {
+ FDLOG(LOG_ERR, fd_cb, "Failed to allocate a buffer of size %u to hold the application data from the properties update", app_data_length);
+ }
}
- ifnet_head_done();
socket_unlock(fd_cb->so, 0);
}
struct inpcb *inp = sotoinpcb(so);
struct sockaddr_in *sinp;
mbuf_t connect_packet = NULL;
- char *signing_id = NULL;
- int free_signing_id = 0;
+ int do_send = 1;
VERIFY((so->so_flags & SOF_FLOW_DIVERT) && so->so_fd_pcb != NULL);
goto done;
}
- sinp = (struct sockaddr_in *)(void *)to;
- if (sinp->sin_family == AF_INET && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
- error = EAFNOSUPPORT;
- goto done;
- }
-
if ((fd_cb->flags & FLOW_DIVERT_CONNECT_STARTED) && !(fd_cb->flags & FLOW_DIVERT_TRANSFERRED)) {
error = EALREADY;
goto done;
}
}
- if (fd_cb->local_address != NULL) {
- error = EALREADY;
- goto done;
- } else {
- if (flow_divert_has_pcb_local_address(inp)) {
- error = flow_divert_inp_to_sockaddr(inp, &fd_cb->local_address);
- if (error) {
- FDLOG0(LOG_ERR, fd_cb, "failed to get the local socket address.");
- goto done;
- }
- }
- }
-
-
- error = flow_divert_packet_init(fd_cb, FLOW_DIVERT_PKT_CONNECT, &connect_packet);
- if (error) {
- goto done;
- }
-
- error = EPERM;
+ FDLOG0(LOG_INFO, fd_cb, "Connecting");
- if (fd_cb->connect_token != NULL) {
- uint32_t sid_size = 0;
- int find_error = flow_divert_packet_get_tlv(fd_cb->connect_token, 0, FLOW_DIVERT_TLV_SIGNING_ID, 0, NULL, &sid_size);
- if (find_error == 0 && sid_size > 0) {
- MALLOC(signing_id, char *, sid_size + 1, M_TEMP, M_WAITOK | M_ZERO);
- if (signing_id != NULL) {
- flow_divert_packet_get_tlv(fd_cb->connect_token, 0, FLOW_DIVERT_TLV_SIGNING_ID, sid_size, signing_id, NULL);
- FDLOG(LOG_INFO, fd_cb, "Got %s from token", signing_id);
- free_signing_id = 1;
- }
+ if (fd_cb->connect_packet == NULL) {
+ if (to == NULL) {
+ FDLOG0(LOG_ERR, fd_cb, "No destination address available when creating connect packet");
+ error = EINVAL;
+ goto done;
}
- }
- socket_unlock(so, 0);
- if (g_signing_id_trie.root != NULL_TRIE_IDX) {
- proc_t src_proc = p;
- int release_proc = 0;
-
- if (signing_id == NULL) {
- release_proc = flow_divert_get_src_proc(so, &src_proc, FALSE);
- if (src_proc != PROC_NULL) {
- proc_lock(src_proc);
- if (src_proc->p_csflags & CS_VALID) {
- const char * cs_id;
- cs_id = cs_identity_get(src_proc);
- signing_id = __DECONST(char *, cs_id);
- } else {
- FDLOG0(LOG_WARNING, fd_cb, "Signature is invalid");
- }
- } else {
- FDLOG0(LOG_WARNING, fd_cb, "Failed to determine the current proc");
- }
- } else {
- src_proc = PROC_NULL;
+ sinp = (struct sockaddr_in *)(void *)to;
+ if (sinp->sin_family == AF_INET && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
+ error = EAFNOSUPPORT;
+ goto done;
}
- if (signing_id != NULL) {
- uint16_t result = NULL_TRIE_IDX;
- lck_rw_lock_shared(&g_flow_divert_group_lck);
- result = flow_divert_trie_search(&g_signing_id_trie, (uint8_t *)signing_id);
- lck_rw_done(&g_flow_divert_group_lck);
- if (result != NULL_TRIE_IDX) {
- error = 0;
- FDLOG(LOG_INFO, fd_cb, "%s matched", signing_id);
-
- error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_SIGNING_ID, strlen(signing_id), signing_id);
- if (error == 0) {
- if (src_proc != PROC_NULL) {
- unsigned char cdhash[SHA1_RESULTLEN];
- error = proc_getcdhash(src_proc, cdhash);
- if (error == 0) {
- error = flow_divert_packet_append_tlv(connect_packet, FLOW_DIVERT_TLV_CDHASH, sizeof(cdhash), cdhash);
- if (error) {
- FDLOG(LOG_ERR, fd_cb, "failed to append the cdhash: %d", error);
- }
- } else {
- FDLOG(LOG_ERR, fd_cb, "failed to get the cdhash: %d", error);
- }
- }
- } else {
- FDLOG(LOG_ERR, fd_cb, "failed to append the signing ID: %d", error);
- }
- } else {
- FDLOG(LOG_WARNING, fd_cb, "%s did not match", signing_id);
- }
- } else {
- FDLOG0(LOG_WARNING, fd_cb, "Failed to get the code signing identity");
+ error = flow_divert_create_connect_packet(fd_cb, to, so, p, &connect_packet);
+ if (error) {
+ goto done;
}
- if (src_proc != PROC_NULL) {
- proc_unlock(src_proc);
- if (release_proc) {
- proc_rele(src_proc);
- }
+ if (so->so_flags1 & SOF1_PRECONNECT_DATA) {
+ FDLOG0(LOG_INFO, fd_cb, "Delaying sending the connect packet until send or receive");
+ do_send = 0;
}
} else {
- FDLOG0(LOG_WARNING, fd_cb, "The signing ID trie is empty");
- }
- socket_lock(so, 0);
-
- if (free_signing_id) {
- FREE(signing_id, M_TEMP);
+ FDLOG0(LOG_INFO, fd_cb, "Sending saved connect packet");
+ connect_packet = fd_cb->connect_packet;
+ fd_cb->connect_packet = NULL;
}
- if (error) {
- goto done;
- }
-
- FDLOG0(LOG_INFO, fd_cb, "Connecting");
+ if (do_send) {
+ error = flow_divert_send_packet(fd_cb, connect_packet, TRUE);
+ if (error) {
+ goto done;
+ }
- error = flow_divert_send_connect(fd_cb, to, connect_packet);
- if (error) {
- goto done;
+ fd_cb->flags |= FLOW_DIVERT_CONNECT_STARTED;
+ } else {
+ fd_cb->connect_packet = connect_packet;
+ connect_packet = NULL;
}
- fd_cb->flags |= FLOW_DIVERT_CONNECT_STARTED;
-
soisconnecting(so);
done:
struct sockaddr_list **src_sl, struct sockaddr_list **dst_sl,
struct proc *p, uint32_t ifscope __unused, sae_associd_t aid __unused,
sae_connid_t *pcid, uint32_t flags __unused, void *arg __unused,
- uint32_t arglen __unused)
+ uint32_t arglen __unused, struct uio *auio, user_ssize_t *bytes_written)
{
struct sockaddr_entry *src_se = NULL, *dst_se = NULL;
struct inpcb *inp = sotoinpcb(so);
error = flow_divert_connect_out(so, dst_se->se_addr, p);
+ if (error != 0) {
+ return error;
+ }
+
+ /* if there is data, send it */
+ if (auio != NULL) {
+ user_ssize_t datalen = 0;
+
+ socket_unlock(so, 0);
+
+ VERIFY(bytes_written != NULL);
+
+ datalen = uio_resid(auio);
+ error = so->so_proto->pr_usrreqs->pru_sosend(so, NULL, (uio_t)auio, NULL, NULL, 0);
+ socket_lock(so, 0);
+
+ if (error == 0 || error == EWOULDBLOCK) {
+ *bytes_written = datalen - uio_resid(auio);
+ }
+
+ /*
+ * sosend returns EWOULDBLOCK if it's a non-blocking
+ * socket or a timeout occured (this allows to return
+ * the amount of queued data through sendit()).
+ *
+ * However, connectx() returns EINPROGRESS in case of a
+ * blocking socket. So we change the return value here.
+ */
+ if (error == EWOULDBLOCK) {
+ error = EINPROGRESS;
+ }
+ }
+
if (error == 0 && pcid != NULL) {
*pcid = 1; /* there is only 1 connection for a TCP */
}
{
#pragma unused(uio, bytes_written)
return (flow_divert_connectx_out_common(so, AF_INET, src_sl, dst_sl,
- p, ifscope, aid, pcid, flags, arg, arglen));
+ p, ifscope, aid, pcid, flags, arg, arglen, uio, bytes_written));
}
#if INET6
{
#pragma unused(uio, bytes_written)
return (flow_divert_connectx_out_common(so, AF_INET6, src_sl, dst_sl,
- p, ifscope, aid, pcid, flags, arg, arglen));
+ p, ifscope, aid, pcid, flags, arg, arglen, uio, bytes_written));
}
#endif /* INET6 */
}
static errno_t
-flow_divert_data_out(struct socket *so, int flags, mbuf_t data, struct sockaddr *to, mbuf_t control, struct proc *p __unused)
+flow_divert_data_out(struct socket *so, int flags, mbuf_t data, struct sockaddr *to, mbuf_t control, struct proc *p)
{
struct flow_divert_pcb *fd_cb = so->so_fd_pcb;
int error = 0;
/* Implicit connect */
if (!(fd_cb->flags & FLOW_DIVERT_CONNECT_STARTED)) {
FDLOG0(LOG_INFO, fd_cb, "implicit connect");
- error = flow_divert_connect_out(so, to, NULL);
+ error = flow_divert_connect_out(so, to, p);
if (error) {
goto done;
}
+
+ if (so->so_flags1 & SOF1_DATA_IDEMPOTENT) {
+ /* Open up the send window so that the data will get sent right away */
+ fd_cb->send_window = mbuf_pkthdr_len(data);
+ }
}
FDLOG(LOG_DEBUG, fd_cb, "app wrote %lu bytes", mbuf_pkthdr_len(data));
return error;
}
+static int
+flow_divert_preconnect(struct socket *so)
+{
+ struct flow_divert_pcb *fd_cb = so->so_fd_pcb;
+ int error = 0;
+
+ if (!(fd_cb->flags & FLOW_DIVERT_CONNECT_STARTED) && fd_cb->connect_packet != NULL) {
+ FDLOG0(LOG_INFO, fd_cb, "Pre-connect read: sending saved connect packet");
+ mbuf_t connect_packet = fd_cb->connect_packet;
+ fd_cb->connect_packet = NULL;
+
+ error = flow_divert_send_packet(fd_cb, connect_packet, TRUE);
+ if (error) {
+ mbuf_freem(connect_packet);
+ }
+
+ fd_cb->flags |= FLOW_DIVERT_CONNECT_STARTED;
+ }
+
+ so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+
+ return error;
+}
+
static void
flow_divert_set_protosw(struct socket *so)
{
uint32_t key_unit = 0;
uint32_t flow_id = 0;
int error = 0;
+ int hmac_error = 0;
mbuf_t token = NULL;
if (so->so_flags & SOF_FLOW_DIVERT) {
}
socket_unlock(so, 0);
- error = flow_divert_packet_verify_hmac(token, (key_unit != 0 ? key_unit : ctl_unit));
+ hmac_error = flow_divert_packet_verify_hmac(token, (key_unit != 0 ? key_unit : ctl_unit));
socket_lock(so, 0);
- if (error) {
- FDLOG(LOG_ERR, &nil_pcb, "HMAC verfication failed: %d", error);
+ if (hmac_error && hmac_error != ENOENT) {
+ FDLOG(LOG_ERR, &nil_pcb, "HMAC verfication failed: %d", hmac_error);
+ error = hmac_error;
goto done;
}
error = flow_divert_attach(so, flow_id, ctl_unit);
}
+ if (hmac_error == 0) {
+ struct flow_divert_pcb *fd_cb = so->so_fd_pcb;
+ if (fd_cb != NULL) {
+ fd_cb->flags |= FLOW_DIVERT_HAS_HMAC;
+ }
+ }
+
done:
if (token != NULL) {
mbuf_freem(token);
goto done;
}
+ if (fd_cb->app_data != NULL) {
+ error = flow_divert_packet_append_tlv(token, FLOW_DIVERT_TLV_APP_DATA, fd_cb->app_data_length, fd_cb->app_data);
+ if (error) {
+ goto done;
+ }
+ }
+
socket_unlock(so, 0);
lck_rw_lock_shared(&g_flow_divert_group_lck);
g_flow_divert_in_usrreqs.pru_send = flow_divert_data_out;
g_flow_divert_in_usrreqs.pru_shutdown = flow_divert_shutdown;
g_flow_divert_in_usrreqs.pru_sockaddr = flow_divert_getsockaddr;
+ g_flow_divert_in_usrreqs.pru_preconnect = flow_divert_preconnect;
g_flow_divert_in_protosw.pr_usrreqs = &g_flow_divert_in_usrreqs;
g_flow_divert_in_protosw.pr_ctloutput = flow_divert_ctloutput;
g_flow_divert_in_udp_usrreqs.pru_sockaddr = flow_divert_getsockaddr;
g_flow_divert_in_udp_usrreqs.pru_sosend_list = pru_sosend_list_notsupp;
g_flow_divert_in_udp_usrreqs.pru_soreceive_list = pru_soreceive_list_notsupp;
+ g_flow_divert_in_udp_usrreqs.pru_preconnect = flow_divert_preconnect;
g_flow_divert_in_udp_protosw.pr_usrreqs = &g_flow_divert_in_usrreqs;
g_flow_divert_in_udp_protosw.pr_ctloutput = flow_divert_ctloutput;
g_flow_divert_in6_usrreqs.pru_send = flow_divert_data_out;
g_flow_divert_in6_usrreqs.pru_shutdown = flow_divert_shutdown;
g_flow_divert_in6_usrreqs.pru_sockaddr = flow_divert_getsockaddr;
+ g_flow_divert_in6_usrreqs.pru_preconnect = flow_divert_preconnect;
g_flow_divert_in6_protosw.pr_usrreqs = &g_flow_divert_in6_usrreqs;
g_flow_divert_in6_protosw.pr_ctloutput = flow_divert_ctloutput;
g_flow_divert_in6_udp_usrreqs.pru_sockaddr = flow_divert_getsockaddr;
g_flow_divert_in6_udp_usrreqs.pru_sosend_list = pru_sosend_list_notsupp;
g_flow_divert_in6_udp_usrreqs.pru_soreceive_list = pru_soreceive_list_notsupp;
+ g_flow_divert_in6_udp_usrreqs.pru_preconnect = flow_divert_preconnect;
g_flow_divert_in6_udp_protosw.pr_usrreqs = &g_flow_divert_in6_udp_usrreqs;
g_flow_divert_in6_udp_protosw.pr_ctloutput = flow_divert_ctloutput;
uint32_t control_group_unit;
int32_t ref_count;
uint32_t bytes_written_by_app;
- uint32_t bytes_read_by_app;
+ uint32_t bytes_read_by_app;
uint32_t bytes_sent;
uint32_t bytes_received;
- uint8_t log_level;
+ uint8_t log_level;
SLIST_ENTRY(flow_divert_pcb) tmp_list_entry;
+ mbuf_t connect_packet;
+ uint8_t *app_data;
+ size_t app_data_length;
};
RB_HEAD(fd_pcb_tree, flow_divert_pcb);
#define FLOW_DIVERT_TLV_PREFIX_COUNT 28
#define FLOW_DIVERT_TLV_FLAGS 29
#define FLOW_DIVERT_TLV_FLOW_TYPE 30
+#define FLOW_DIVERT_TLV_APP_DATA 31
#define FLOW_DIVERT_FLOW_TYPE_TCP 1
#define FLOW_DIVERT_FLOW_TYPE_UDP 3
#define FLOW_DIVERT_DNS_SERVICE_SIGNING_ID "com.apple.mDNSResponder"
#define FLOW_DIVERT_TOKEN_FLAG_VALIDATED 0x0000001
+#define FLOW_DIVERT_TOKEN_FLAG_TFO 0x0000002
+#define FLOW_DIVERT_TOKEN_FLAG_MPTCP 0x0000004
struct flow_divert_packet_header {
uint8_t packet_type;
struct sockaddr_entry **src_se, struct sockaddr_list **dst_sl,
struct sockaddr_entry **dst_se)
{
- struct sockaddr_entry *se;
+ struct sockaddr_entry *se = NULL;
+ struct sockaddr_entry *tse = NULL;
int error = 0;
VERIFY(src_sl != NULL && dst_sl != NULL && *dst_sl != NULL);
}
}
/* get rid of the rest */
- TAILQ_FOREACH(se, &(*src_sl)->sl_head, se_link) {
+ TAILQ_FOREACH_SAFE(se, &(*src_sl)->sl_head, se_link, tse) {
sockaddrlist_remove(*src_sl, se);
sockaddrentry_free(se);
}
}
}
/* get rid of the rest */
- TAILQ_FOREACH(se, &(*dst_sl)->sl_head, se_link) {
+ TAILQ_FOREACH_SAFE(se, &(*dst_sl)->sl_head, se_link, tse) {
sockaddrlist_remove(*dst_sl, se);
sockaddrentry_free(se);
}
xt->ts_recent = tp->ts_recent;
xt->ts_recent_age = tp->ts_recent_age;
xt->last_ack_sent = tp->last_ack_sent;
- xt->cc_send = tp->cc_send;
- xt->cc_recv = tp->cc_recv;
+ xt->cc_send = 0;
+ xt->cc_recv = 0;
xt->snd_recover = tp->snd_recover;
xt->snd_cwnd_prev = tp->snd_cwnd_prev;
xt->snd_ssthresh_prev = tp->snd_ssthresh_prev;
struct ip *, ip, struct ip6_hdr *, NULL);
error = ipsec4_output(&ipsec_state, sp, flags);
+ if (ipsec_state.tunneled == 6) {
+ m0 = m = NULL;
+ error = 0;
+ goto bad;
+ }
m0 = m = ipsec_state.m;
/*
- * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
lck_mtx_unlock(&mppi->mppi_lock);
mptcplog((LOG_ERR, "MPTCP Socket: Reached MPTCP socket limit."),
MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
- return (ENOBUFS);
+ /*
+ * This limit may be reached either because of
+ * a leak or a transient condition where
+ * MPTCP connections are not released fast
+ * enough.
+ * We return EAFNOSUPPORT here to have user
+ * space library fallback to TCP.
+ * XXX We need to revist this when we get rid
+ * of the current low limit imposed on MPTCP.
+ */
+ return (EAFNOSUPPORT);
}
lck_mtx_unlock(&mppi->mppi_lock);
.pr_type = SOCK_STREAM,
.pr_protocol = IPPROTO_TCP,
.pr_flags = PR_CONNREQUIRED|PR_MULTICONN|PR_EVCONNINFO|
- PR_WANTRCVD|PR_PCBLOCK|PR_PROTOLOCK,
+ PR_WANTRCVD|PR_PCBLOCK|PR_PROTOLOCK|
+ PR_PRECONN_WRITE|PR_DATA_IDEMPOTENT,
.pr_ctloutput = mptcp_ctloutput,
.pr_init = mptcp_init,
.pr_usrreqs = &mptcp_usrreqs,
DTRACE_MPTCP3(output, struct mptses *, mpte, struct mptsub *, mpts,
struct socket *, mp_so);
error = mptcp_subflow_output(mpte, mpts);
- if (error) {
+ if (error && error != EWOULDBLOCK) {
/* can be a temporary loss of source address or other error */
mpts->mpts_flags |= MPTSF_FAILINGOVER;
mpts->mpts_flags &= ~MPTSF_ACTIVE;
mpts_tried = mpts;
MPTS_UNLOCK(mpts);
- mptcplog((LOG_INFO, "MPTCP Sender: Error = %d \n", error),
+ mptcplog((LOG_INFO, "MPTCP Sender: %s Error = %d \n",
+ __func__, error),
MPTCP_SENDER_DBG, MPTCP_LOGLVL_LOG);
goto try_again;
}
}
/*
- * Subflows with Fastjoin allow data to be written before
+ * Subflows with TFO or Fastjoin allow data to be written before
* the subflow is mp capable.
*/
if (!(mpts->mpts_flags & MPTSF_MP_CAPABLE) &&
- !(mpts->mpts_flags & MPTSF_FASTJ_REQD)) {
+ !(mpts->mpts_flags & MPTSF_FASTJ_REQD) &&
+ !(mpts->mpts_flags & MPTSF_TFO_REQD)) {
MPTS_UNLOCK(mpts);
continue;
}
u_int64_t full_dsn = 0;
struct mptcb *mp_tp = tptomptp(tp);
+ /*
+ * May happen, because the caller of this function does an soevent.
+ * Review after rdar://problem/24083886
+ */
+ if (!mp_tp)
+ return;
+
NTOHL(dss_info->mdss_dsn);
NTOHL(dss_info->mdss_subflow_seqn);
NTOHS(dss_info->mdss_data_len);
u_int64_t dsn = mptcp_ntoh64(dss_info->mdss_dsn);
struct mptcb *mp_tp = tptomptp(tp);
+ /*
+ * May happen, because the caller of this function does an soevent.
+ * Review after rdar://problem/24083886
+ */
+ if (!mp_tp)
+ return;
+
NTOHL(dss_info->mdss_subflow_seqn);
NTOHS(dss_info->mdss_data_len);
mptcp_update_rcv_state_meat(mp_tp, tp,
MPTCP_DATASEQ_LOW32(mp_tp->mpt_dsn_at_csum_fail);
infin_opt.mdss_subflow_seqn = mp_tp->mpt_ssn_at_csum_fail;
} else {
+ /*
+ * If MPTCP fallback happens, but TFO succeeds, the data on the
+ * SYN does not belong to the MPTCP data sequence space.
+ */
+ if ((tp->t_tfo_stats & TFO_S_SYN_DATA_ACKED) &&
+ ((mp_tp->mpt_local_idsn + 1) == mp_tp->mpt_snduna)) {
+ infin_opt.mdss_subflow_seqn = 1;
+
+ mptcplog((LOG_DEBUG, "MPTCP Socket: %s: idsn %llu"
+ "snduna %llu \n", __func__, mp_tp->mpt_local_idsn,
+ mp_tp->mpt_snduna),
+ (MPTCP_SOCKET_DBG | MPTCP_SENDER_DBG),
+ MPTCP_LOGLVL_LOG);
+ } else {
+ infin_opt.mdss_subflow_seqn = tp->snd_una - tp->iss;
+ }
infin_opt.mdss_dsn = (u_int32_t)
MPTCP_DATASEQ_LOW32(mp_tp->mpt_snduna);
- infin_opt.mdss_subflow_seqn = tp->snd_una - tp->iss;
}
MPT_UNLOCK(mp_tp);
if (error != 0)
mp_tp->mpt_flags |= MPTCPF_CHECKSUM;
rsp = (struct mptcp_mpcapable_opt_rsp *)cp;
- MPT_LOCK_SPIN(mp_tp);
+ MPT_LOCK(mp_tp);
mp_tp->mpt_remotekey = rsp->mmc_localkey;
/* For now just downgrade to the peer's version */
mp_tp->mpt_peer_version = rsp->mmc_common.mmco_version;
mp_tp->mpt_version = rsp->mmc_common.mmco_version;
tcpstat.tcps_mp_verdowngrade++;
}
+ if (mptcp_init_remote_parms(mp_tp) != 0) {
+ tcpstat.tcps_invalid_mpcap++;
+ MPT_UNLOCK(mp_tp);
+ return;
+ }
MPT_UNLOCK(mp_tp);
tp->t_mpflags |= TMPF_PREESTABLISHED;
if (mp_tp->mpt_state > MPTCPS_FIN_WAIT_2)
close_notify = 1;
MPT_UNLOCK(mp_tp);
- mptcp_notify_mpready(tp->t_inpcb->inp_socket);
- if (close_notify)
- mptcp_notify_close(tp->t_inpcb->inp_socket);
if (mp_tp->mpt_flags & MPTCPF_RCVD_64BITACK) {
mp_tp->mpt_flags &= ~MPTCPF_RCVD_64BITACK;
mp_tp->mpt_flags &= ~MPTCPF_SND_64BITDSN;
}
+ mptcp_notify_mpready(tp->t_inpcb->inp_socket);
+ if (close_notify)
+ mptcp_notify_close(tp->t_inpcb->inp_socket);
} else {
MPT_UNLOCK(mp_tp);
mptcplog((LOG_ERR,"MPTCP Socket: "
} \
}
+ /*
+ * mp_tp might become NULL after the call to mptcp_do_fin_opt().
+ * Review after rdar://problem/24083886
+ */
+ if (!mp_tp)
+ return;
+
if (mp_tp->mpt_flags & MPTCPF_CHECKSUM)
csum_len = 2;
/*
- * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2012-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
static void mptcp_attach_to_subf(struct socket *, struct mptcb *, uint8_t);
static void mptcp_detach_mptcb_from_subf(struct mptcb *, struct socket *);
static void mptcp_conn_properties(struct mptcb *);
-static void mptcp_init_statevars(struct mptcb *);
static uint32_t mptcp_gc(struct mppcbinfo *);
static int mptcp_subflow_socreate(struct mptses *, struct mptsub *,
static void mptcp_update_last_owner(struct mptsub *, struct socket *);
static void mptcp_output_needed(struct mptses *mpte, struct mptsub *to_mpts);
static void mptcp_get_rtt_measurement(struct mptsub *, struct mptses *);
+static void mptcp_drop_tfo_data(struct mptses *, struct mptsub *);
/*
* Possible return values for subflow event handlers. Note that success
static mptcp_key_t *mptcp_reserve_key(void);
static int mptcp_do_sha1(mptcp_key_t *, char *, int);
-static int mptcp_init_authparms(struct mptcb *);
+static void mptcp_init_local_parms(struct mptcb *);
static unsigned int mptsub_zone_size; /* size of mptsub */
static struct zone *mptsub_zone; /* zone for mptsub */
uint64_t *p_mpsofilt_hint);
} mptsub_ev_entry_t;
+/*
+ * XXX The order of the event handlers below is really
+ * really important.
+ * SO_FILT_HINT_DELETEOK event has to be handled first,
+ * else we may end up missing on this event.
+ * Please read radar://24043716 for more details.
+ */
static mptsub_ev_entry_t mpsub_ev_entry_tbl [] = {
+ {
+ .sofilt_hint_mask = SO_FILT_HINT_DELETEOK,
+ .sofilt_hint_ev_hdlr = mptcp_deleteok_ev,
+ },
{
.sofilt_hint_mask = SO_FILT_HINT_MPCANTRCVMORE,
.sofilt_hint_ev_hdlr = mptcp_subflow_mpcantrcvmore_ev,
.sofilt_hint_mask = SO_FILT_HINT_MPSTATUS,
.sofilt_hint_ev_hdlr = mptcp_subflow_mpstatus_ev,
},
- {
- .sofilt_hint_mask = SO_FILT_HINT_DELETEOK,
- .sofilt_hint_ev_hdlr = mptcp_deleteok_ev,
- },
{
.sofilt_hint_mask = SO_FILT_HINT_DISCONNECTED,
.sofilt_hint_ev_hdlr = mptcp_subflow_disconnected_ev,
(*so)->so_rcv.sb_flags |= SB_NOCOMPRESS;
(*so)->so_snd.sb_flags |= SB_NOCOMPRESS;
+ /* Inherit preconnect and TFO data flags */
+ if (mp_so->so_flags1 & SOF1_PRECONNECT_DATA)
+ (*so)->so_flags1 |= SOF1_PRECONNECT_DATA;
+
+ if (mp_so->so_flags1 & SOF1_DATA_IDEMPOTENT)
+ (*so)->so_flags1 |= SOF1_DATA_IDEMPOTENT;
+
bzero(&smpo, sizeof (smpo));
smpo.mpo_flags |= MPOF_SUBFLOW_OK;
smpo.mpo_level = SOL_SOCKET;
if ((error = mptcp_subflow_socreate(mpte, mpts, af, p, &so)) != 0)
goto out;
- /* If fastjoin is requested, set state in mpts */
- if ((so->so_flags & SOF_MPTCP_FASTJOIN) &&
- (mp_tp->mpt_state == MPTCPS_ESTABLISHED) &&
- (mpte->mpte_nummpcapflows == 0)) {
- mpts->mpts_flags |= MPTSF_FASTJ_REQD;
- mpts->mpts_rel_seq = 1;
- MPT_LOCK(mp_tp);
- mpts->mpts_sndnxt = mp_tp->mpt_snduna;
- MPT_UNLOCK(mp_tp);
- }
-
/*
* Increment the counter, while avoiding 0 (SAE_CONNID_ANY) and
* -1 (SAE_CONNID_ALL).
mpts->mpts_connid = mpte->mpte_connid_last;
VERIFY(mpts->mpts_connid != SAE_CONNID_ANY &&
mpts->mpts_connid != SAE_CONNID_ALL);
-
+
+ mpts->mpts_rel_seq = 1;
+
/* Allocate a unique address id per subflow */
mpte->mpte_addrid_last++;
if (mpte->mpte_addrid_last == 0)
MPT_LOCK(mp_tp);
if (mp_tp->mpt_state < MPTCPS_ESTABLISHED && mpte->mpte_numflows == 1) {
if (mp_tp->mpt_state == MPTCPS_CLOSED) {
- mp_tp->mpt_localkey = mptcp_reserve_key();
- mptcp_conn_properties(mp_tp);
+ mptcp_init_local_parms(mp_tp);
}
MPT_UNLOCK(mp_tp);
soisconnecting(mp_so);
mpcr.mpcr_type = MPTSUB_CONNREQ_MP_ADD;
}
+ /* If fastjoin or fastopen is requested, set state in mpts */
+ if (mpte->mpte_nummpcapflows == 0) {
+ if (so->so_flags1 & SOF1_PRECONNECT_DATA) {
+ MPT_LOCK(mp_tp);
+ if (mp_tp->mpt_state < MPTCPS_ESTABLISHED) {
+ mpts->mpts_flags |= MPTSF_TFO_REQD;
+ mpts->mpts_sndnxt = mp_tp->mpt_snduna;
+ }
+ MPT_UNLOCK(mp_tp);
+ }
+
+ if (so->so_flags & SOF_MPTCP_FASTJOIN) {
+ MPT_LOCK(mp_tp);
+ if (mp_tp->mpt_state == MPTCPS_ESTABLISHED) {
+ mpts->mpts_flags |= MPTSF_FASTJ_REQD;
+ mpts->mpts_sndnxt = mp_tp->mpt_snduna;
+ }
+ MPT_UNLOCK(mp_tp);
+ }
+ }
+
mpts->mpts_mpcr = mpcr;
mpts->mpts_flags |= MPTSF_CONNECTING;
mpts->mpts_connid),
MPTCP_RECEIVER_DBG, MPTCP_LOGLVL_ERR);
}
+ if (error == ENODATA) {
+ /*
+ * Don't ignore ENODATA so as to discover
+ * nasty middleboxes.
+ */
+ struct socket *mp_so =
+ mpte->mpte_mppcb->mpp_socket;
+ mp_so->so_error = ENODATA;
+ sorwakeup(mp_so);
+ }
}
MPTS_LOCK(mpts);
} else if (error == 0) {
struct mptses *mpte = mpts->mpts_mpte;
/*
- * mpte should never be NULL except in a race with
+ * mpte should never be NULL except in a race with
* mptcp_subflow_del which doesn't hold socket lock across critical
* section. This upcall is made after releasing the socket lock.
* Interleaving of socket operations becomes possible therefore.
struct mbuf *mpt_mbuf = NULL;
u_int64_t off = 0;
struct mbuf *head, *tail;
+ int tcp_zero_len_write = 0;
MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
MPTS_LOCK_ASSERT_HELD(mpts);
/* subflow socket is not MPTCP capable? */
if (!(mpts->mpts_flags & MPTSF_MP_CAPABLE) &&
!(mpts->mpts_flags & MPTSF_MP_DEGRADED) &&
- !(mpts->mpts_flags & MPTSF_FASTJ_SEND)) {
+ !(mpts->mpts_flags & MPTSF_FASTJ_SEND) &&
+ !(mpts->mpts_flags & MPTSF_TFO_REQD)) {
mptcplog((LOG_ERR, "MPTCP Sender: %s mp_so 0x%llx cid %d not "
"MPTCP capable\n", __func__,
(u_int64_t)VM_KERNEL_ADDRPERM(mp_so), mpts->mpts_connid),
mpte->mpte_flags &= ~MPTE_SND_REM_ADDR;
}
+ if (mpts->mpts_flags & MPTSF_TFO_REQD) {
+ mptcp_drop_tfo_data(mpte, mpts);
+ }
+
/*
* The mbuf chains containing the metadata (as well as pointing to
* the user data sitting at the MPTCP output queue) would then be
mpt_mbuf = sb_mb;
while (mpt_mbuf && mpt_mbuf->m_pkthdr.mp_rlen == 0) {
+ if (((so->so_state & SS_ISCONNECTED) == 0) &&
+ (mpt_mbuf->m_next == NULL) &&
+ (so->so_flags1 & SOF1_PRECONNECT_DATA)) {
+ /*
+ * If TFO, allow connection establishment with zero
+ * length write.
+ */
+ tcp_zero_len_write = 1;
+ goto zero_len_write;
+ }
mpt_mbuf = mpt_mbuf->m_next;
}
if (mpt_mbuf && (mpt_mbuf->m_pkthdr.pkt_flags & PKTF_MPTCP)) {
*/
if (MPTCP_SEQ_LT(mpts->mpts_sndnxt, mp_tp->mpt_snduna)) {
mpts->mpts_sndnxt = mp_tp->mpt_snduna;
- /*
- * With FastJoin, a write before the fastjoin event will use
- * an uninitialized relative sequence number.
- */
- if (mpts->mpts_rel_seq == 0)
- mpts->mpts_rel_seq = 1;
}
/*
}
if (head != NULL) {
+ struct tcpcb *tp = intotcpcb(sotoinpcb(so));
- if (mpts->mpts_flags & MPTSF_FASTJ_SEND) {
- struct tcpcb *tp = intotcpcb(sotoinpcb(so));
+ if ((mpts->mpts_flags & MPTSF_TFO_REQD) &&
+ (tp->t_tfo_stats == 0)) {
+ tp->t_mpflags |= TMPF_TFO_REQUEST;
+ } else if (mpts->mpts_flags & MPTSF_FASTJ_SEND) {
tp->t_mpflags |= TMPF_FASTJOIN_SEND;
}
struct sockbuf *, &so->so_snd,
struct mptses *, mpte, struct mptsub *, mpts,
size_t, tot_sent);
+ } else if (tcp_zero_len_write == 1) {
+zero_len_write:
+ socket_lock(so, 1);
+ /* Opting to call pru_send as no mbuf at subflow level */
+ error = (*so->so_proto->pr_usrreqs->pru_send)
+ (so, 0, NULL, NULL, NULL, current_proc());
+ socket_unlock(so, 1);
}
- if (error == 0) {
+ if ((error == 0) || (error == EWOULDBLOCK)) {
mpts->mpts_sndnxt += tot_sent;
if (mpts->mpts_probesoon && mpts->mpts_maxseg && tot_sent) {
mptcp_cancel_timer(mp_tp, MPTT_REXMT);
MPT_UNLOCK(mp_tp);
+ if (so->so_flags1 & SOF1_PRECONNECT_DATA)
+ so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+
/* Send once in SYN_SENT state to avoid sending SYN spam */
if (mpts->mpts_flags & MPTSF_FASTJ_SEND) {
- so->so_flags &= ~SOF_MPTCP_FASTJOIN;
+ so->so_flags &= ~SOF_MPTCP_FASTJOIN;
mpts->mpts_flags &= ~MPTSF_FASTJ_SEND;
}
* once it is handled
*/
for (i = 0; (i < mpsub_ev_entry_count) && events; i++) {
+ /*
+ * Always execute the DISCONNECTED event, because it will wakeup
+ * the app.
+ */
if ((events & mpsub_ev_entry_tbl[i].sofilt_hint_mask) &&
- (ret >= MPTS_EVRET_OK)) {
+ (ret >= MPTS_EVRET_OK ||
+ mpsub_ev_entry_tbl[i].sofilt_hint_mask == SO_FILT_HINT_DISCONNECTED)) {
ev_ret_t error =
mpsub_ev_entry_tbl[i].sofilt_hint_ev_hdlr(mpte, mpts, p_mpsofilt_hint);
events &= ~mpsub_ev_entry_tbl[i].sofilt_hint_mask;
if ((mpts->mpts_flags & MPTSF_DISCONNECTED) ||
(mpts->mpts_flags & MPTSF_DISCONNECTING)) {
- socket_lock(so, 0);
+ socket_lock(so, 0);
if (!(so->so_state & (SS_ISDISCONNECTING | SS_ISDISCONNECTED)) &&
(so->so_state & SS_ISCONNECTED)) {
mptcplog((LOG_DEBUG, "MPTCP Events: "
/*
* With MPTCP joins, a connection is connected at the subflow
* level, but the 4th ACK from the server elevates the MPTCP
- * subflow to connected state. So there is a small window
- * where the subflow could get disconnected before the
+ * subflow to connected state. So there is a small window
+ * where the subflow could get disconnected before the
* connected event is processed.
*/
socket_unlock(so, 0);
mpts->mpts_soerror = 0;
mpts->mpts_flags &= ~MPTSF_CONNECTING;
mpts->mpts_flags |= MPTSF_CONNECTED;
- if (sototcpcb(so)->t_mpflags & TMPF_MPTCP_TRUE)
+
+ if (!(so->so_flags1 & SOF1_DATA_IDEMPOTENT))
+ mpts->mpts_flags &= ~MPTSF_TFO_REQD;
+
+ struct tcpcb *tp = sototcpcb(so);
+ if (tp->t_mpflags & TMPF_MPTCP_TRUE)
mpts->mpts_flags |= MPTSF_MP_CAPABLE;
+ tp->t_mpflags &= ~TMPF_TFO_REQUEST;
+
VERIFY(mpts->mpts_dst_sl != NULL);
dst_se = TAILQ_FIRST(&mpts->mpts_dst_sl->sl_head);
VERIFY(dst_se != NULL && dst_se->se_addr != NULL &&
(void) mptcp_drop(mpte, mp_tp, EPROTO);
MPT_UNLOCK(mp_tp);
} else {
- if (mptcp_init_authparms(mp_tp) != 0) {
- mp_tp->mpt_flags |= MPTCPF_PEEL_OFF;
- (void) mptcp_drop(mpte, mp_tp, EPROTO);
- MPT_UNLOCK(mp_tp);
- mpok = FALSE;
- } else {
- mptcplog((LOG_DEBUG, "MPTCP State: "
- "MPTCPS_ESTABLISHED for mp_so 0x%llx \n",
- (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)),
- MPTCP_STATE_DBG, MPTCP_LOGLVL_LOG);
- mp_tp->mpt_state = MPTCPS_ESTABLISHED;
- mpte->mpte_associd = mpts->mpts_connid;
- DTRACE_MPTCP2(state__change,
- struct mptcb *, mp_tp,
- uint32_t, 0 /* event */);
- mptcp_init_statevars(mp_tp);
- MPT_UNLOCK(mp_tp);
-
- (void) mptcp_setconnorder(mpte,
- mpts->mpts_connid, 1);
- soisconnected(mp_so);
- }
+ MPT_UNLOCK(mp_tp);
+ mptcplog((LOG_DEBUG, "MPTCP State: "
+ "MPTCPS_ESTABLISHED for mp_so 0x%llx \n",
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)),
+ MPTCP_STATE_DBG, MPTCP_LOGLVL_LOG);
+ mp_tp->mpt_state = MPTCPS_ESTABLISHED;
+ mpte->mpte_associd = mpts->mpts_connid;
+ DTRACE_MPTCP2(state__change,
+ struct mptcb *, mp_tp,
+ uint32_t, 0 /* event */);
+
+ (void) mptcp_setconnorder(mpte, mpts->mpts_connid, 1);
+ soisconnected(mp_so);
}
MPTS_LOCK(mpts);
if (mpok) {
- /* Initialize the relative sequence number */
- mpts->mpts_rel_seq = 1;
mpts->mpts_flags |= MPTSF_MPCAP_CTRSET;
mpte->mpte_nummpcapflows++;
MPT_LOCK_SPIN(mp_tp);
- mpts->mpts_sndnxt = mp_tp->mpt_snduna;
+ /* With TFO, sndnxt may be initialized earlier */
+ if (mpts->mpts_sndnxt == 0)
+ mpts->mpts_sndnxt = mp_tp->mpt_snduna;
MPT_UNLOCK(mp_tp);
}
} else if (mpok) {
mpts->mpts_flags |= MPTSF_MPCAP_CTRSET;
mpts->mpts_flags &= ~MPTSF_FASTJ_REQD;
mpte->mpte_nummpcapflows++;
- /* With Fastjoin, rel sequence will be nonzero */
- if (mpts->mpts_rel_seq == 0)
- mpts->mpts_rel_seq = 1;
MPT_LOCK_SPIN(mp_tp);
/* With Fastjoin, sndnxt is updated before connected_ev */
if (mpts->mpts_sndnxt == 0) {
mpts->mpts_sndnxt = mp_tp->mpt_snduna;
+ mpts->mpts_rel_seq = 1;
}
MPT_UNLOCK(mp_tp);
mptcp_output_needed(mpte, mpts);
*/
if (mpts->mpts_sndnxt == 0) {
mpts->mpts_sndnxt = mp_tp->mpt_snduna;
- mpts->mpts_rel_seq = 1;
}
MPT_UNLOCK(mp_tp);
}
MPTS_LOCK(mpts);
MPTS_ADDREF_LOCKED(mpts); /* for us */
-
+
/* Update process ownership based on parent mptcp socket */
mptcp_update_last_owner(mpts, mp_so);
-
+
mptcp_subflow_input(mpte, mpts);
mptcp_get_rtt_measurement(mpts, mpte);
MPTS_UNLOCK(mpts);
continue;
}
+
+ if (mpts->mpts_flags & MPTSF_TFO_REQD)
+ mptcp_drop_tfo_data(mpte, mpts);
+
so = mpts->mpts_socket;
/*
tp->t_mpflags &=
~(TMPF_MPTCP_READY|TMPF_MPTCP_TRUE);
tp->t_mpflags |= TMPF_TCP_FALLBACK;
+
if (mpts->mpts_flags & MPTSF_ACTIVE) {
socket_unlock(so, 1);
MPTS_UNLOCK(mpts);
/*
* Authentication data generation
*/
-int
+void
mptcp_generate_token(char *sha_digest, int sha_digest_len, caddr_t token,
int token_len)
{
/* Most significant 32 bits of the SHA1 hash */
bcopy(sha_digest, token, sizeof (u_int32_t));
- return (TRUE);
+ return;
}
-int
+void
mptcp_generate_idsn(char *sha_digest, int sha_digest_len, caddr_t idsn,
int idsn_len)
{
idsn[2] = sha_digest[17];
idsn[1] = sha_digest[18];
idsn[0] = sha_digest[19];
- return (TRUE);
+ return;
}
-static int
-mptcp_init_authparms(struct mptcb *mp_tp)
+static void
+mptcp_conn_properties(struct mptcb *mp_tp)
+{
+ /* There is only Version 0 at this time */
+ mp_tp->mpt_version = MPTCP_STD_VERSION_0;
+
+ /* Set DSS checksum flag */
+ if (mptcp_dss_csum)
+ mp_tp->mpt_flags |= MPTCPF_CHECKSUM;
+
+ /* Set up receive window */
+ mp_tp->mpt_rcvwnd = mptcp_sbspace(mp_tp);
+
+ /* Set up gc ticks */
+ mp_tp->mpt_gc_ticks = MPT_GC_TICKS;
+}
+
+static void
+mptcp_init_local_parms(struct mptcb *mp_tp)
{
caddr_t local_digest = NULL;
+
+ mp_tp->mpt_localkey = mptcp_reserve_key();
+ local_digest = mptcp_get_stored_digest(mp_tp->mpt_localkey);
+ mptcp_generate_token(local_digest, SHA1_RESULTLEN,
+ (caddr_t)&mp_tp->mpt_localtoken, sizeof (mp_tp->mpt_localtoken));
+ mptcp_generate_idsn(local_digest, SHA1_RESULTLEN,
+ (caddr_t)&mp_tp->mpt_local_idsn, sizeof (u_int64_t));
+
+ /* The subflow SYN is also first MPTCP byte */
+ mp_tp->mpt_snduna = mp_tp->mpt_sndmax = mp_tp->mpt_local_idsn + 1;
+ mp_tp->mpt_sndnxt = mp_tp->mpt_snduna;
+
+ mptcp_conn_properties(mp_tp);
+}
+
+int
+mptcp_init_remote_parms(struct mptcb *mp_tp)
+{
char remote_digest[MPTCP_SHA1_RESULTLEN];
MPT_LOCK_ASSERT_HELD(mp_tp);
return (-1);
/* Setup local and remote tokens and Initial DSNs */
- local_digest = mptcp_get_stored_digest(mp_tp->mpt_localkey);
- mptcp_generate_token(local_digest, SHA1_RESULTLEN,
- (caddr_t)&mp_tp->mpt_localtoken, sizeof (mp_tp->mpt_localtoken));
- mptcp_generate_idsn(local_digest, SHA1_RESULTLEN,
- (caddr_t)&mp_tp->mpt_local_idsn, sizeof (u_int64_t));
if (!mptcp_do_sha1(&mp_tp->mpt_remotekey, remote_digest,
SHA1_RESULTLEN)) {
return (-1);
}
mptcp_generate_token(remote_digest, SHA1_RESULTLEN,
- (caddr_t)&mp_tp->mpt_remotetoken, sizeof (mp_tp->mpt_localtoken));
+ (caddr_t)&mp_tp->mpt_remotetoken, sizeof (mp_tp->mpt_remotetoken));
mptcp_generate_idsn(remote_digest, SHA1_RESULTLEN,
(caddr_t)&mp_tp->mpt_remote_idsn, sizeof (u_int64_t));
- return (0);
-}
-
-static void
-mptcp_init_statevars(struct mptcb *mp_tp)
-{
- MPT_LOCK_ASSERT_HELD(mp_tp);
-
- /* The subflow SYN is also first MPTCP byte */
- mp_tp->mpt_snduna = mp_tp->mpt_sndmax = mp_tp->mpt_local_idsn + 1;
- mp_tp->mpt_sndnxt = mp_tp->mpt_snduna;
-
mp_tp->mpt_rcvatmark = mp_tp->mpt_rcvnxt = mp_tp->mpt_remote_idsn + 1;
-}
-
-static void
-mptcp_conn_properties(struct mptcb *mp_tp)
-{
- /* There is only Version 0 at this time */
- mp_tp->mpt_version = MPTCP_STD_VERSION_0;
-
- /* Set DSS checksum flag */
- if (mptcp_dss_csum)
- mp_tp->mpt_flags |= MPTCPF_CHECKSUM;
-
- /* Set up receive window */
- mp_tp->mpt_rcvwnd = mptcp_sbspace(mp_tp);
- /* Set up gc ticks */
- mp_tp->mpt_gc_ticks = MPT_GC_TICKS;
+ return (0);
}
/*
__IGNORE_WCASTALIGN(mp_tp = &((struct mpp_mtp *)mpp)->mtcb);
MPT_LOCK(mp_tp);
- if (mp_tp->mpt_state < MPTCPS_ESTABLISHED) {
- MPT_UNLOCK(mp_tp);
- panic("%s: data write before establishment.",
- __func__);
- return;
- }
-
while (m) {
VERIFY(m->m_flags & M_PKTHDR);
m->m_pkthdr.pkt_flags |= (PKTF_MPTCP | PKTF_MPSO);
}
void
-mptcp_preproc_sbdrop(struct mbuf *m, unsigned int len)
+mptcp_preproc_sbdrop(struct socket *so, struct mbuf *m, unsigned int len)
{
u_int32_t sub_len = 0;
+ int rewinding = 0;
+
+ if (so->so_flags1 & SOF1_DATA_IDEMPOTENT) {
+ /* TFO makes things complicated. */
+ if (so->so_flags1 & SOF1_TFO_REWIND) {
+ rewinding = 1;
+ so->so_flags1 &= ~SOF1_TFO_REWIND;
+ }
+ }
while (m) {
VERIFY(m->m_flags & M_PKTHDR);
len -= sub_len;
} else {
/* sub_len >= len */
- m->m_pkthdr.mp_dsn += len;
+ if (rewinding == 0)
+ m->m_pkthdr.mp_dsn += len;
if (!(m->m_pkthdr.pkt_flags & PKTF_MPSO)) {
- m->m_pkthdr.mp_rseq += len;
+ if (rewinding == 0)
+ m->m_pkthdr.mp_rseq += len;
}
mptcplog((LOG_DEBUG, "MPTCP Sender: "
- "%s: dsn 0x%llu ssn %u len %d %d\n",
+ "%s: dsn 0x%llx ssn %u len %d %d\n",
__func__,
m->m_pkthdr.mp_dsn, m->m_pkthdr.mp_rseq,
m->m_pkthdr.mp_rlen, len),
(tp->t_mpflags & TMPF_SENT_JOIN) &&
(!(tp->t_mpflags & TMPF_MPTCP_TRUE)) &&
(!(tp->t_mpflags & TMPF_FASTJOINBY2_SEND))) {
- mdss_data_len = 0;
- tp->t_mpflags |= TMPF_FASTJOINBY2_SEND;
- }
+ mdss_data_len = 0;
+ tp->t_mpflags |= TMPF_FASTJOINBY2_SEND;
+ }
+
+ if ((tp->t_state > TCPS_SYN_SENT) &&
+ (tp->t_mpflags & TMPF_TFO_REQUEST)) {
+ mdss_data_len = 0;
+ tp->t_mpflags &= ~TMPF_TFO_REQUEST;
+ }
return (mdss_data_len);
}
/* little is known about the state of the network or wifi is good */
return (NULL);
}
+
+/* If TFO data is succesfully acked, it must be dropped from the mptcp so */
+static void
+mptcp_drop_tfo_data(struct mptses *mpte, struct mptsub *mpts)
+{
+ struct socket *mp_so = mpte->mpte_mppcb->mpp_socket;
+ struct socket *so = mpts->mpts_socket;
+ struct tcpcb *tp = intotcpcb(sotoinpcb(so));
+ struct mptcb *mp_tp = mpte->mpte_mptcb;
+
+ /* If data was sent with SYN, rewind state */
+ if (tp->t_tfo_stats & TFO_S_SYN_DATA_ACKED) {
+ mpts->mpts_flags &= ~MPTSF_TFO_REQD;
+ tp->t_mpflags &= ~TMPF_TFO_REQUEST;
+ MPT_LOCK(mp_tp);
+ u_int64_t mp_droplen = mpts->mpts_sndnxt - mp_tp->mpt_snduna;
+ unsigned int tcp_droplen = tp->snd_una - tp->iss - 1;
+ VERIFY(mp_droplen <= (UINT_MAX));
+ VERIFY(mp_droplen >= tcp_droplen);
+
+ if (mp_droplen > tcp_droplen) {
+ /* handle partial TCP ack */
+ mp_so->so_flags1 |= SOF1_TFO_REWIND;
+ mp_tp->mpt_sndnxt = mp_tp->mpt_snduna + (mp_droplen - tcp_droplen);
+ mpts->mpts_sndnxt = mp_tp->mpt_sndnxt;
+ mp_droplen = tcp_droplen;
+ } else {
+ /* all data on SYN was acked */
+ mpts->mpts_rel_seq = 1;
+ mp_tp->mpt_sndnxt = mp_tp->mpt_snduna;
+ mpts->mpts_sndnxt = mp_tp->mpt_snduna;
+ }
+ mp_tp->mpt_sndmax -= tcp_droplen;
+
+ MPT_UNLOCK(mp_tp);
+ if (mp_droplen != 0) {
+ VERIFY(mp_so->so_snd.sb_mb != NULL);
+ sbdrop(&mp_so->so_snd, (int)mp_droplen);
+ }
+ mptcplog((LOG_ERR, "MPTCP Sender: %s mp_so 0x%llx cid %d "
+ "TFO tcp len %d mptcp len %d\n", __func__,
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so), mpts->mpts_connid,
+ tcp_droplen, mp_droplen),
+ MPTCP_SENDER_DBG, MPTCP_LOGLVL_LOG);
+ }
+}
+
static int mptcp_getopt(struct mptses *, struct sockopt *);
static int mptcp_default_tcp_optval(struct mptses *, struct sockopt *, int *);
static void mptcp_connorder_helper(struct mptsub *mpts);
+static int mptcp_usr_preconnect(struct socket *so);
struct pr_usrreqs mptcp_usrreqs = {
.pru_attach = mptcp_usr_attach,
.pru_sosend = mptcp_usr_sosend,
.pru_soreceive = soreceive,
.pru_socheckopt = mptcp_usr_socheckopt,
+ .pru_preconnect = mptcp_usr_preconnect,
};
+/*
+ * Sysctl for testing and tuning mptcp connectx with data api.
+ * Mirrors tcp_preconnect_sbspace for now.
+ */
+#define MPTCP_PRECONNECT_SBSZ_MAX 1460
+#define MPTCP_PRECONNECT_SBSZ_MIN (TCP_MSS)
+#define MPTCP_PRECONNECT_SBSZ_DEF (TCP6_MSS)
+static int mptcp_preconnect_sbspace = MPTCP_PRECONNECT_SBSZ_DEF;
+SYSCTL_INT(_net_inet_mptcp, OID_AUTO, mp_preconn_sbsz, CTLFLAG_RW | CTLFLAG_LOCKED,
+ &mptcp_preconnect_sbspace, 0, "Maximum preconnect space");
+
+
/*
* Attaches an MPTCP control block to a socket.
*/
goto out;
}
+ if (mp_so->so_snd.sb_preconn_hiwat == 0) {
+ soreserve_preconnect(mp_so, imin(MPTCP_PRECONNECT_SBSZ_MAX,
+ imax(mptcp_preconnect_sbspace, MPTCP_PRECONNECT_SBSZ_MIN)));
+ }
+
/*
* MPTCP socket buffers cannot be compressed, due to the
* fact that each mbuf chained via m_next is a M_PKTHDR
mptcp_usr_connectx(struct socket *mp_so, struct sockaddr_list **src_sl,
struct sockaddr_list **dst_sl, struct proc *p, uint32_t ifscope,
sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
- uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written)
+ uint32_t arglen, struct uio *auio, user_ssize_t *bytes_written)
{
-#pragma unused(arg, arglen, uio, bytes_written)
struct mppcb *mpp = sotomppcb(mp_so);
struct mptses *mpte = NULL;
struct mptcb *mp_tp = NULL;
+ user_ssize_t datalen;
int error = 0;
error = mptcp_connectx(mpte, src_sl, dst_sl, p, ifscope,
aid, pcid, flags, arg, arglen);
+
+ /* If there is data, copy it */
+ if (auio != NULL) {
+ datalen = uio_resid(auio);
+ socket_unlock(mp_so, 0);
+ error = mp_so->so_proto->pr_usrreqs->pru_sosend(mp_so, NULL,
+ (uio_t) auio, NULL, NULL, 0);
+ /* check if this can be supported with fast Join also. XXX */
+ if (error == 0 || error == EWOULDBLOCK)
+ *bytes_written = datalen - uio_resid(auio);
+
+ if (error == EWOULDBLOCK)
+ error = EINPROGRESS;
+
+ socket_lock(mp_so, 0);
+ MPT_LOCK(mp_tp);
+ if (mp_tp->mpt_flags & MPTCPF_PEEL_OFF) {
+ *bytes_written = datalen - uio_resid(auio);
+ /*
+ * Override errors like EPIPE that occur as
+ * a result of doing TFO during TCP fallback.
+ */
+ error = EPROTO;
+ }
+ MPT_UNLOCK(mp_tp);
+ }
+
out:
return (error);
}
mpte = mptompte(mpp);
VERIFY(mpte != NULL);
- if (!(mp_so->so_state & SS_ISCONNECTED)) {
+ if (!(mp_so->so_state & SS_ISCONNECTED) &&
+ (!(mp_so->so_flags1 & SOF1_PRECONNECT_DATA))) {
error = ENOTCONN;
goto out;
}
(void) sbappendstream(&mp_so->so_snd, m);
m = NULL;
- if (mpte != NULL) {
- /*
- * XXX: adi@apple.com
- *
- * PRUS_MORETOCOME could be set, but we don't check it now.
- */
- error = mptcp_output(mpte);
+ /*
+ * XXX: adi@apple.com
+ *
+ * PRUS_MORETOCOME could be set, but we don't check it now.
+ */
+ error = mptcp_output(mpte);
+ if (error != 0)
+ goto out;
+
+ if (mp_so->so_state & SS_ISCONNECTING) {
+ if (mp_so->so_state & SS_NBIO)
+ error = EWOULDBLOCK;
+ else
+ error = sbwait(&mp_so->so_snd);
}
-
+
out:
if (error) {
if (m != NULL)
if (control != NULL)
m_freem(control);
+ /* clear SOF1_PRECONNECT_DATA after one write */
+ if (mp_so->so_flags1 & SOF1_PRECONNECT_DATA)
+ mp_so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+
return (error);
}
(void) snprintf(dst, size, "<%s,%s>", l, o);
return (dst);
}
+
+static int
+mptcp_usr_preconnect(struct socket *mp_so)
+{
+ struct mptsub *mpts = NULL;
+ struct mppcb *mpp = sotomppcb(mp_so);
+ struct mptses *mpte;
+ struct socket *so;
+ struct tcpcb *tp = NULL;
+
+ mpte = mptompte(mpp);
+ VERIFY(mpte != NULL);
+ MPTE_LOCK_ASSERT_HELD(mpte); /* same as MP socket lock */
+
+ mpts = mptcp_get_subflow(mpte, NULL, NULL);
+ if (mpts == NULL) {
+ mptcplog((LOG_ERR, "MPTCP Socket: "
+ "%s: mp_so 0x%llx invalid preconnect ", __func__,
+ (u_int64_t)VM_KERNEL_ADDRPERM(mp_so)),
+ MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
+ return (EINVAL);
+ }
+ MPTS_LOCK(mpts);
+ mpts->mpts_flags &= ~MPTSF_TFO_REQD;
+ so = mpts->mpts_socket;
+ socket_lock(so, 0);
+ tp = intotcpcb(sotoinpcb(so));
+ tp->t_mpflags &= ~TMPF_TFO_REQUEST;
+ int error = tcp_output(sototcpcb(so));
+ socket_unlock(so, 0);
+ MPTS_UNLOCK(mpts);
+ mp_so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+ return (error);
+}
#define MPTSF_FASTJ_SEND 0x100000 /* send data after SYN in MP_JOIN */
#define MPTSF_FASTJ_REQD 0x200000 /* fastjoin required */
#define MPTSF_USER_DISCONNECT 0x400000 /* User triggered disconnect */
+#define MPTSF_TFO_REQD 0x800000 /* TFO requested */
#define MPTSF_BITS \
"\020\1ATTACHED\2CONNECTING\3PENDING\4CONNECTED\5DISCONNECTING" \
extern void mptcp_set_raddr_rand(mptcp_addr_id, struct mptcb *, mptcp_addr_id,
u_int32_t);
extern u_int64_t mptcp_get_trunced_hmac(mptcp_addr_id, struct mptcb *mp_tp);
-extern int mptcp_generate_token(char *, int, caddr_t, int);
-extern int mptcp_generate_idsn(char *, int, caddr_t, int);
+extern void mptcp_generate_token(char *, int, caddr_t, int);
+extern void mptcp_generate_idsn(char *, int, caddr_t, int);
+extern int mptcp_init_remote_parms(struct mptcb *);
extern boolean_t mptcp_ok_to_keepalive(struct mptcb *);
extern void mptcp_insert_dsn(struct mppcb *, struct mbuf *);
extern void mptcp_output_getm_dsnmap32(struct socket *, int, uint32_t,
* has acknowledged immediately.
*/
if (SEQ_GT(tp->snd_nxt, th->th_ack))
- tp->snd_nxt = th->th_ack;
+ tp->snd_max = tp->snd_nxt = th->th_ack;
/*
* If there's data, delay ACK; if there's also a FIN
SEQ_LT(tp->snd_una, th->th_ack)) {
tp->t_tfo_stats |= TFO_S_SYN_DATA_ACKED;
tcpstat.tcps_tfo_syn_data_acked++;
-
+#if MPTCP
+ if (so->so_flags & SOF_MP_SUBFLOW)
+ so->so_flags1 |= SOF1_TFO_REWIND;
+#endif
if (!(tp->t_tfo_flags & TFO_F_NO_RCVPROBING))
tcp_tfo_rcv_probe(tp, tlen);
}
return (err);
}
-int tcp_ecn_outbound = 0;
+int tcp_ecn_outbound = 2;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, ecn_initiate_out,
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_ecn_outbound, 0,
sysctl_change_ecn_setting, "IU",
"Initiate ECN for outbound connections");
-int tcp_ecn_inbound = 0;
+int tcp_ecn_inbound = 2;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, ecn_negotiate_in,
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &tcp_ecn_inbound, 0,
sysctl_change_ecn_setting, "IU",
(tp->t_flagsext & TF_FASTOPEN)));
}
+#define TCP_ECN_SETUP_PERCENTAGE_MAX 5
void
tcp_set_ecn(struct tcpcb *tp, struct ifnet *ifp)
{
check_heuristic:
if (!tcp_heuristic_do_ecn(tp))
tp->ecn_flags &= ~TE_ENABLE_ECN;
+
+ /*
+ * If the interface setting, system-level setting and heuristics
+ * allow to enable ECN, randomly select 5% of connections to
+ * enable it
+ */
+ if ((tp->ecn_flags & (TE_ECN_MODE_ENABLE | TE_ECN_MODE_DISABLE
+ | TE_ENABLE_ECN)) == TE_ENABLE_ECN) {
+ /*
+ * Use the random value in iss for randomizing
+ * this selection
+ */
+ if ((tp->iss % 100) >= TCP_ECN_SETUP_PERCENTAGE_MAX)
+ tp->ecn_flags &= ~TE_ENABLE_ECN;
+ }
}
/*
tcpstat.tcps_sack_rexmits++;
tcpstat.tcps_sack_rexmit_bytes +=
min(len, tp->t_maxseg);
- if (nstat_collect) {
- nstat_route_tx(inp->inp_route.ro_rt, 1,
- min(len, tp->t_maxseg),
- NSTAT_TX_FLAG_RETRANSMIT);
- INP_ADD_STAT(inp, cell, wifi, wired,
- txpackets, 1);
- INP_ADD_STAT(inp, cell, wifi, wired,
- txbytes, min(len, tp->t_maxseg));
- tp->t_stat.txretransmitbytes += min(len, tp->t_maxseg);
- }
} else {
len = 0;
}
if ((so->so_flags & SOF_MP_SUBFLOW) &&
!(tp->t_mpflags & TMPF_TCP_FALLBACK)) {
int newlen = len;
- if (!(tp->t_mpflags & TMPF_PREESTABLISHED) &&
- (tp->t_state > TCPS_CLOSED) &&
+ if ((tp->t_state >= TCPS_ESTABLISHED) &&
((tp->t_mpflags & TMPF_SND_MPPRIO) ||
(tp->t_mpflags & TMPF_SND_REM_ADDR) ||
(tp->t_mpflags & TMPF_SND_MPFAIL) ||
INP_ADD_STAT(inp, cell, wifi, wired,
txbytes, len);
tp->t_stat.txretransmitbytes += len;
+ tp->t_stat.rxmitpkts++;
}
} else {
tcpstat.tcps_sndpack++;
{
u_int64_t curval, oldval;
struct inpcb *inp = tp->t_inpcb;
+ stat->total_txpkts += inp->inp_stat->txpackets;
+ stat->total_rxpkts += inp->inp_stat->rxpackets;
+ stat->total_rxmitpkts += tp->t_stat.rxmitpkts;
+ stat->total_oopkts += tp->t_rcvoopack;
+ stat->total_reorderpkts += (tp->t_reordered_pkts + tp->t_pawsdrop +
+ tp->t_dsack_sent + tp->t_dsack_recvd);
/* Average RTT */
curval = (tp->t_srtt >> TCP_RTT_SHIFT);
}
}
- /* Percentage of Out-of-order packets, shift by 10 for precision */
- curval = (tp->t_rcvoopack << 10);
- if (inp->inp_stat != NULL && inp->inp_stat->rxpackets > 0 &&
- curval > 0) {
- /* Compute percentage */
- curval = (curval * 100)/inp->inp_stat->rxpackets;
- if (stat->oo_percent == 0) {
- stat->oo_percent = curval;
- } else {
- oldval = stat->oo_percent;
- stat->oo_percent =
- ((oldval << 4) - oldval + curval) >> 4;
- }
- }
-
/* Total number of SACK recovery episodes */
stat->sack_episodes += tp->t_sack_recovery_episode;
- /* Percentage of reordered packets, shift by 10 for precision */
- curval = tp->t_reordered_pkts + tp->t_pawsdrop + tp->t_dsack_sent +
- tp->t_dsack_recvd;
- curval = curval << 10;
- if (inp->inp_stat != NULL && (inp->inp_stat->rxpackets > 0 ||
- inp->inp_stat->txpackets > 0) && curval > 0) {
- /* Compute percentage */
- curval = (curval * 100) /
- (inp->inp_stat->rxpackets + inp->inp_stat->txpackets);
- if (stat->reorder_percent == 0) {
- stat->reorder_percent = curval;
- } else {
- oldval = stat->reorder_percent;
- stat->reorder_percent =
- ((oldval << 4) - oldval + curval) >> 4;
- }
- }
-
- /* Percentage of retransmit bytes, shift by 10 for precision */
- curval = tp->t_stat.txretransmitbytes << 10;
- if (inp->inp_stat != NULL && inp->inp_stat->txbytes > 0
- && curval > 0) {
- curval = (curval * 100) / inp->inp_stat->txbytes;
- if (stat->rxmit_percent == 0) {
- stat->rxmit_percent = curval;
- } else {
- oldval = stat->rxmit_percent;
- stat->rxmit_percent =
- ((oldval << 4) - oldval + curval) >> 4;
- }
- }
+ if (inp->inp_socket->so_error == ECONNRESET)
+ stat->rst_drop++;
return;
}
ecn_peer_nosupport);
}
}
+ } else {
+ INP_INC_IFNET_STAT(inp, ecn_off_conn);
}
if (TCP_ECN_ENABLED(tp)) {
if (tp->ecn_flags & TE_RECV_ECN_CE) {
INP_INC_IFNET_STAT(inp, ecn_conn_plnoce);
}
}
-
}
/* Aggregate performance stats */
- if (inp->inp_last_outifp != NULL) {
+ if (inp->inp_last_outifp != NULL && !(tp->t_flags & TF_LOCAL)) {
struct ifnet *ifp = inp->inp_last_outifp;
ifnet_lock_shared(ifp);
if ((ifp->if_refflags & (IFRF_ATTACHED | IFRF_DETACHING)) ==
IFRF_ATTACHED) {
if (inp->inp_vflag & INP_IPV6) {
+ ifp->if_ipv6_stat->timestamp = net_uptime();
if (TCP_ECN_ENABLED(tp)) {
- ifp->if_ipv6_stat->timestamp
- = net_uptime();
tcp_update_ecn_perf_stats(tp,
&ifp->if_ipv6_stat->ecn_on);
} else {
- ifp->if_ipv6_stat->timestamp
- = net_uptime();
tcp_update_ecn_perf_stats(tp,
&ifp->if_ipv6_stat->ecn_off);
}
} else {
+ ifp->if_ipv4_stat->timestamp = net_uptime();
if (TCP_ECN_ENABLED(tp)) {
- ifp->if_ipv4_stat->timestamp
- = net_uptime();
tcp_update_ecn_perf_stats(tp,
&ifp->if_ipv4_stat->ecn_on);
} else {
- ifp->if_ipv4_stat->timestamp
- = net_uptime();
tcp_update_ecn_perf_stats(tp,
&ifp->if_ipv4_stat->ecn_off);
}
otp->ts_recent = tp->ts_recent;
otp->ts_recent_age = tp->ts_recent_age;
otp->last_ack_sent = tp->last_ack_sent;
- otp->cc_send = tp->cc_send;
- otp->cc_recv = tp->cc_recv;
+ otp->cc_send = 0;
+ otp->cc_recv = 0;
otp->snd_recover = tp->snd_recover;
otp->snd_cwnd_prev = tp->snd_cwnd_prev;
otp->snd_ssthresh_prev = tp->snd_ssthresh_prev;
otp->ts_recent = tp->ts_recent;
otp->ts_recent_age = tp->ts_recent_age;
otp->last_ack_sent = tp->last_ack_sent;
- otp->cc_send = tp->cc_send;
- otp->cc_recv = tp->cc_recv;
+ otp->cc_send = 0;
+ otp->cc_recv = 0;
otp->snd_recover = tp->snd_recover;
otp->snd_cwnd_prev = tp->snd_cwnd_prev;
otp->snd_ssthresh_prev = tp->snd_ssthresh_prev;
rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
}
- TCPT_RANGESET(tp->t_rxtcur, rexmt,
- tp->t_rttmin, TCPTV_REXMTMAX,
+ TCPT_RANGESET(tp->t_rxtcur, rexmt, tp->t_rttmin, TCPTV_REXMTMAX,
TCP_ADD_REXMTSLOP(tp));
tp->t_timer[TCPT_REXMT] = OFFSET_FROM_START(tp, tp->t_rxtcur);
tcp_lock(inp->inp_socket, 1, 0);
/* Release the want count */
- if (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING) {
+ if (inp->inp_ppcb == NULL ||
+ (in_pcb_checkstate(inp, WNT_RELEASE, 1) == WNT_STOPUSING)) {
tcp_unlock(inp->inp_socket, 1, 0);
continue;
}
-
tp = intotcpcb(inp);
if (enable)
tcp_enable_read_probe(tp, ifp);
error = ENETDOWN;
}
- /* Disable PRECONNECT_DATA, as we don't need to send a SYN anymore. */
- so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
return error;
}
#endif /* FLOW_DIVERT */
error = ENETDOWN;
}
- /* Disable PRECONNECT_DATA, as we don't need to send a SYN anymore. */
- so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
return error;
}
#endif /* FLOW_DIVERT */
static int
tcp_usr_preconnect(struct socket *so)
{
- int error = tcp_output(sototcpcb(so));
+ struct inpcb *inp = sotoinpcb(so);
+ int error = 0;
+
+#if NECP
+ if (necp_socket_should_use_flow_divert(inp)) {
+ /* May happen, if in tcp_usr_connect we did not had a chance
+ * to set the usrreqs (due to some error). So, let's get out
+ * of here.
+ */
+ goto out;
+ }
+#endif /* NECP */
+
+ error = tcp_output(sototcpcb(so));
/* One read has been done. This was enough. Get back to "normal" behavior. */
so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
struct tcpcb *otp;
struct sockaddr_in *sin = (struct sockaddr_in *)(void *)nam;
struct in_addr laddr;
- struct rmxp_tao *taop;
- struct rmxp_tao tao_noncached;
int error = 0;
struct ifnet *outif = NULL;
if (nstat_collect)
nstat_route_connect_attempt(inp->inp_route.ro_rt);
- /*
- * Generate a CC value for this connection and
- * check whether CC or CCnew should be used.
- */
- if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
- taop = &tao_noncached;
- bzero(taop, sizeof(*taop));
- }
-
- tp->cc_send = CC_INC(tcp_ccgen);
- if (taop->tao_ccsent != 0 &&
- CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
- taop->tao_ccsent = tp->cc_send;
- } else {
- taop->tao_ccsent = 0;
- tp->t_flags |= TF_SENDCCNEW;
- }
-
done:
if (outif != NULL)
ifnet_release(outif);
struct tcpcb *otp;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)nam;
struct in6_addr addr6;
- struct rmxp_tao *taop;
- struct rmxp_tao tao_noncached;
int error = 0;
struct ifnet *outif = NULL;
if (nstat_collect)
nstat_route_connect_attempt(inp->inp_route.ro_rt);
- /*
- * Generate a CC value for this connection and
- * check whether CC or CCnew should be used.
- */
- if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
- taop = &tao_noncached;
- bzero(taop, sizeof(*taop));
- }
-
- tp->cc_send = CC_INC(tcp_ccgen);
- if (taop->tao_ccsent != 0 &&
- CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
- taop->tao_ccsent = tp->cc_send;
- } else {
- taop->tao_ccsent = 0;
- tp->t_flags |= TF_SENDCCNEW;
- }
-
done:
if (outif != NULL)
ifnet_release(outif);
#define TF_NOPUSH 0x01000 /* don't push */
#define TF_REQ_CC 0x02000 /* have/will request CC */
#define TF_RCVD_CC 0x04000 /* a CC was received in SYN */
-#define TF_SENDCCNEW 0x08000 /* send CCnew instead of CC in SYN */
+#define TF_SENDCCNEW 0x08000 /* Unused */
#define TF_MORETOCOME 0x10000 /* More data to be appended to sock */
#define TF_LOCAL 0x20000 /* connection to a host on local link */
#define TF_RXWIN0SENT 0x40000 /* sent a receiver win 0 in response */
u_int32_t ts_recent_age; /* when last updated */
tcp_seq last_ack_sent;
-/* RFC 1644 variables */
- tcp_cc cc_send; /* send connection count */
- tcp_cc cc_recv; /* receive connection count */
/* RFC 3465 variables */
u_int32_t t_bytes_acked; /* ABC "bytes_acked" parameter */
u_int8_t synrxtshift;
u_int8_t unused;
u_int16_t unused_pad_to_8;
+ u_int32_t rxmitpkts;
} t_stat;
/* Background congestion related state */
#define TMPF_FASTJOIN_SEND 0x00400000 /* Fast join early data send */
#define TMPF_FASTJOINBY2_SEND 0x00800000 /* Fast join send after 3 WHS */
#define TMPF_MPCAP_RETRANSMIT 0x01000000 /* Retransmission of 3rd ACK */
+#define TMPF_TFO_REQUEST 0x02000000 /* TFO Requested */
tcp_seq t_mpuna; /* unacknowledged sequence */
void *t_mptcb; /* pointer to MPTCP TCB */
#define TF_NOPUSH 0x01000 /* don't push */
#define TF_REQ_CC 0x02000 /* have/will request CC */
#define TF_RCVD_CC 0x04000 /* a CC was received in SYN */
-#define TF_SENDCCNEW 0x08000 /* send CCnew instead of CC in SYN */
+#define TF_SENDCCNEW 0x08000 /* Not implemented */
#define TF_MORETOCOME 0x10000 /* More data to be appended to sock */
#define TF_LQ_OVERFLOW 0x20000 /* listen queue overflow */
#define TF_RXWIN0SENT 0x40000 /* sent a receiver win 0 in response */
m->m_len -= stripsiz;
m->m_pkthdr.len -= stripsiz;
ip6 = mtod(m, __typeof__(ip6));
- ip6->ip6_plen = ip6->ip6_plen - stripsiz;
+ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
ip6->ip6_nxt = IPPROTO_ESP;
return ip6;
}
}
}
- if (proto_input(PF_INET6, m) != 0)
+ if (proto_input(ifamily == AF_INET ? PF_INET : PF_INET6, m) != 0)
goto bad;
nxt = IPPROTO_DONE;
} else {
goto bad;
}
+ /*
+ * Set the csum valid flag, if we authenticated the
+ * packet, the payload shouldn't be corrupt unless
+ * it was corrupted before being signed on the other
+ * side.
+ */
+ if (nxt == IPPROTO_TCP || nxt == IPPROTO_UDP) {
+ m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xFFFF;
+ }
+
// Input via IPSec interface
if (sav->sah->ipsec_if != NULL) {
if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) {
*/
src_in6 = ip6->ip6_src;
if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
+ RT_REMREF_LOCKED(rt);
+ RT_UNLOCK(rt);
/* XXX: this should not happen */
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
return (NULL);
}
if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
+ RT_REMREF_LOCKED(rt);
+ RT_UNLOCK(rt);
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 ||
in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 ||
inzone != outzone) {
+ RT_REMREF_LOCKED(rt);
+ RT_UNLOCK(rt);
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
{
u_int8_t nxt = nxt0 & 0xff;
struct sockaddr_in6 *sin6;
- struct sockaddr_in6 osrc, odst, isrc, idst;
+ struct sockaddr_in i4src, i4dst;
+ struct sockaddr_in6 osrc, odst, i6src, i6dst;
struct secpolicy *sp;
struct ip6_hdr *oip6;
/* XXX slow */
bzero(&osrc, sizeof(osrc));
bzero(&odst, sizeof(odst));
- bzero(&isrc, sizeof(isrc));
- bzero(&idst, sizeof(idst));
- osrc.sin6_family = odst.sin6_family = isrc.sin6_family =
- idst.sin6_family = *ifamily = AF_INET6;
- osrc.sin6_len = odst.sin6_len = isrc.sin6_len = idst.sin6_len =
- sizeof(struct sockaddr_in6);
+ osrc.sin6_family = odst.sin6_family = AF_INET6;
+ osrc.sin6_len = odst.sin6_len = sizeof(struct sockaddr_in6);
osrc.sin6_addr = oip6->ip6_src;
odst.sin6_addr = oip6->ip6_dst;
- m_copydata(m, off + offsetof(struct ip6_hdr, ip6_src),
- sizeof(isrc.sin6_addr), (caddr_t)&isrc.sin6_addr);
- m_copydata(m, off + offsetof(struct ip6_hdr, ip6_dst),
- sizeof(idst.sin6_addr), (caddr_t)&idst.sin6_addr);
/*
* regarding to inner source address validation, see a long comment
* in ipsec4_tunnel_validate.
*/
- sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
- (struct sockaddr *)&isrc, (struct sockaddr *)&idst);
+ if (nxt == IPPROTO_IPV4) {
+ bzero(&i4src, sizeof(struct sockaddr_in));
+ bzero(&i4dst, sizeof(struct sockaddr_in));
+ i4src.sin_family = i4dst.sin_family = *ifamily = AF_INET;
+ i4src.sin_len = i4dst.sin_len = sizeof(struct sockaddr_in);
+ m_copydata(m, off + offsetof(struct ip, ip_src), sizeof(i4src.sin_addr),
+ (caddr_t)&i4src.sin_addr);
+ m_copydata(m, off + offsetof(struct ip, ip_dst), sizeof(i4dst.sin_addr),
+ (caddr_t)&i4dst.sin_addr);
+ sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
+ (struct sockaddr *)&i4src, (struct sockaddr *)&i4dst);
+ } else if (nxt == IPPROTO_IPV6) {
+ bzero(&i6src, sizeof(struct sockaddr_in6));
+ bzero(&i6dst, sizeof(struct sockaddr_in6));
+ i6src.sin6_family = i6dst.sin6_family = *ifamily = AF_INET6;
+ i6src.sin6_len = i6dst.sin6_len = sizeof(struct sockaddr_in6);
+ m_copydata(m, off + offsetof(struct ip6_hdr, ip6_src), sizeof(i6src.sin6_addr),
+ (caddr_t)&i6src.sin6_addr);
+ m_copydata(m, off + offsetof(struct ip6_hdr, ip6_dst), sizeof(i6dst.sin6_addr),
+ (caddr_t)&i6dst.sin6_addr);
+ sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
+ (struct sockaddr *)&i6src, (struct sockaddr *)&i6dst);
+ } else
+ return 0; /* unsupported family */
/*
* when there is no suitable inbound policy for the packet of the ipsec
* tunnel mode, the kernel never decapsulate the tunneled packet
return (search);
}
-static void
-purge_detached(struct ifnet *ifp)
-{
- struct nd_prefix *pr, *pr_next;
- struct in6_ifaddr *ia;
- struct ifaddr *ifa, *ifa_next;
- boolean_t removed = FALSE;
-
- lck_mtx_lock(nd6_mutex);
-
- pr = nd_prefix.lh_first;
-repeat:
- while (pr) {
- NDPR_LOCK(pr);
- pr_next = pr->ndpr_next;
- if (pr->ndpr_ifp != ifp ||
- IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) ||
- ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
- !LIST_EMPTY(&pr->ndpr_advrtrs))) {
- NDPR_UNLOCK(pr);
- pr = pr_next;
- continue;
- }
- NDPR_ADDREF_LOCKED(pr);
- NDPR_UNLOCK(pr);
- ifnet_lock_shared(ifp);
- for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa_next) {
- IFA_LOCK(ifa);
- ifa_next = ifa->ifa_list.tqe_next;
- if (ifa->ifa_addr->sa_family != AF_INET6) {
- IFA_UNLOCK(ifa);
- continue;
- }
- ia = (struct in6_ifaddr *)ifa;
- if ((ia->ia6_flags & IN6_IFF_AUTOCONF) ==
- IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) {
- IFA_ADDREF_LOCKED(ifa); /* for us */
- IFA_UNLOCK(ifa);
- /*
- * Purging the address requires writer access
- * to the address list, so drop the ifnet lock
- * now and repeat from beginning.
- */
- ifnet_lock_done(ifp);
- lck_mtx_unlock(nd6_mutex);
- in6_purgeaddr(ifa);
- IFA_REMREF(ifa); /* drop ours */
- lck_mtx_lock(nd6_mutex);
- NDPR_REMREF(pr);
- pr = nd_prefix.lh_first;
- goto repeat;
- }
- IFA_UNLOCK(ifa);
- }
- ifnet_lock_done(ifp);
- NDPR_LOCK(pr);
- if (pr->ndpr_addrcnt == 0 &&
- !(pr->ndpr_stateflags & NDPRF_DEFUNCT)) {
- prelist_remove(pr);
- NDPR_UNLOCK(pr);
- removed = TRUE;
- /*
- * Reset the search from the beginning because
- * nd6_mutex may have been dropped in
- * prelist_remove().
- */
- pr_next = nd_prefix.lh_first;
- } else {
- NDPR_UNLOCK(pr);
- }
- NDPR_REMREF(pr);
- pr = pr_next;
- }
- if (removed)
- pfxlist_onlink_check();
- lck_mtx_unlock(nd6_mutex);
-}
-
int
nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr,
struct nd_prefix **newp, boolean_t force_scoped)
ndi = ND_IFINFO(ifp);
VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
lck_mtx_lock(&ndi->lock);
- if (ndi->nprefixes >= ip6_maxifprefixes / 2) {
- lck_mtx_unlock(&ndi->lock);
- purge_detached(ifp);
- lck_mtx_lock(&ndi->lock);
- }
if (ndi->nprefixes >= ip6_maxifprefixes) {
lck_mtx_unlock(&ndi->lock);
return (ENOMEM);
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
- if (uh->uh_sum == 0) {
+ if (!(m->m_pkthdr.csum_flags & CSUM_DATA_VALID) &&
+ uh->uh_sum == 0) {
/* UDP/IPv6 checksum is mandatory (RFC2460) */
+
+ /*
+ * If checksum was already validated, ignore this check.
+ * This is necessary for transport-mode ESP, which may be
+ * getting UDP payloads without checksums when the network
+ * has a NAT64.
+ */
udpstat.udps_nosum++;
goto badsum;
}
#if 1
/*
- * allow IPv6 over IPv4 tunnels using ESP -
+ * allow IPv6 over IPv4 or IPv4 over IPv6 tunnels using ESP -
* otherwise reject if inner and outer address families not equal
*/
if (newsp->req && newsp->req->saidx.src.ss_family) {
struct sockaddr *sa;
sa = (struct sockaddr *)(src0 + 1);
if (sa->sa_family != newsp->req->saidx.src.ss_family) {
- if (newsp->req->saidx.mode != IPSEC_MODE_TUNNEL || newsp->req->saidx.proto != IPPROTO_ESP
- || sa->sa_family != AF_INET6 || newsp->req->saidx.src.ss_family != AF_INET) {
+ if (newsp->req->saidx.mode != IPSEC_MODE_TUNNEL || newsp->req->saidx.proto != IPPROTO_ESP) {
keydb_delsecpolicy(newsp);
if (internal_if) {
ifnet_release(internal_if);
struct sockaddr *sa;
sa = (struct sockaddr *)(dst0 + 1);
if (sa->sa_family != newsp->req->saidx.dst.ss_family) {
- if (newsp->req->saidx.mode != IPSEC_MODE_TUNNEL || newsp->req->saidx.proto != IPPROTO_ESP
- || sa->sa_family != AF_INET6 || newsp->req->saidx.dst.ss_family != AF_INET) {
+ if (newsp->req->saidx.mode != IPSEC_MODE_TUNNEL || newsp->req->saidx.proto != IPPROTO_ESP) {
keydb_delsecpolicy(newsp);
if (internal_if) {
ifnet_release(internal_if);
kern_overrides.h \
mbuf.h \
mman.h \
+ persona.h \
priv.h \
proc.h \
proc_info.h \
mach_swapon.h \
msgbuf.h \
eventvar.h \
+ persona.h \
proc_info.h \
pthread_shims.h \
quota.h \
#include <sys/codesign.h>
-const
-CS_CodeDirectory *findCodeDirectory(
- const CS_SuperBlob *embedded,
- const char *lower_bound,
- const char *upper_bound);
-
#endif
#define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */
#define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */
#define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */
+#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
#define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER)
CSSLOT_APPLICATION = 4,
CSSLOT_ENTITLEMENTS = 5,
+ CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */
+ CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */
+ CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */
+
CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */
CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */
CS_HASHTYPE_SHA1 = 1,
CS_HASHTYPE_SHA256 = 2,
CS_HASHTYPE_SHA256_TRUNCATED = 3,
+ CS_HASHTYPE_SHA384 = 4,
CS_SHA1_LEN = 20,
CS_SHA256_TRUNCATED_LEN = 20,
- CS_CDHASH_LEN = 20,
- CS_HASH_MAX_SIZE = 32, /* max size of the hash we'll support */
+ CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */
+ CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */
};
* Copyright (c) 2014 Apple 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
* 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,
* 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_OSREFERENCE_LICENSE_HEADER_END@
*/
void csr_init(void);
#endif
-#if KERNEL_PRIVATE
-void csr_set_allow_all(int value);
-#endif
-
/* Syscalls */
int csr_check(csr_config_t mask);
int csr_get_active_config(csr_config_t *config);
/* 14 was used for NAMESPACE_HANDLER_GETDATA which has now been
removed as it is no longer used. */
+#define FSIOC_ROUTEFS_SETROUTEID _IO('A', 15)
+#define FSCTL_ROUTEFS_SETROUTEID IOCBASECMD(FSIOC_ROUTEFS_SETROUTEID)
+
//
-// IO commands 15, 16, and 17 are currently unused
+// IO commands 16 and 17 are currently unused
//
//
void *ip_px_sfa;
void *ip_px_spa;
void *ip_px_smpx; /* MAC-specific spawn attrs. */
- void *ip_reserved;
+ void *ip_px_persona; /* persona args */
};
/*
#define DBG_ARIADNE 43
#define DBG_DAEMON 44
#define DBG_ENERGYTRACE 45
+#define DBG_IMG 49
#define DBG_MIG 255
#define MACH_THREAD_BIND 0x2a /* Thread was bound (or unbound) to a processor */
#define MACH_WAITQ_PROMOTE 0x2b /* Thread promoted by waitq boost */
#define MACH_WAITQ_DEMOTE 0x2c /* Thread demoted from waitq boost */
+#define MACH_SCHED_LOAD 0x2d /* load update */
+#define MACH_REC_CORES_FAILSAFE 0x2e /* recommended processor failsafe kicked in */
+#define MACH_SCHED_QUANTUM_EXPIRED 0x2f /* thread quantum expired */
/* Variants for MACH_MULTIQ_DEQUEUE */
#define MACH_MULTIQ_BOUND 1
/* Codes for BANK_ACCOUNT_INFO */
#define BANK_SETTLE_CPU_TIME 0x1 /* Bank ledger(chit) rolled up to tasks. */
+#define BANK_SECURE_ORIGINATOR_CHANGED 0x2 /* Secure Originator changed. */
/* Codes for ATM_SUBAID_INFO */
#define ATM_MIN_CALLED 0x1
#define MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES 8 /* Get memory limits plus attributes */
#define MEMORYSTATUS_CMD_PRIVILEGED_LISTENER_ENABLE 9 /* Set the task's status as a privileged listener w.r.t memory notifications */
#define MEMORYSTATUS_CMD_PRIVILEGED_LISTENER_DISABLE 10 /* Reset the task's status as a privileged listener w.r.t memory notifications */
+#define MEMORYSTATUS_CMD_AGGRESSIVE_JETSAM_LENIENT_MODE_ENABLE 11 /* Enable the 'lenient' mode for aggressive jetsam. See comments in kern_memorystatus.c near the top. */
+#define MEMORYSTATUS_CMD_AGGRESSIVE_JETSAM_LENIENT_MODE_DISABLE 12 /* Disable the 'lenient' mode for aggressive jetsam. */
/* Commands that act on a group of processes */
#define MEMORYSTATUS_CMD_GRP_SET_PROPERTIES 100
#define VQ_VERYLOWDISK 0x0200 /* file system has *very* little disk space left */
#define VQ_SYNCEVENT 0x0400 /* a sync just happened (not set by kernel starting Mac OS X 10.9) */
#define VQ_SERVEREVENT 0x0800 /* server issued notification/warning */
-#define VQ_FLAG1000 0x1000 /* placeholder */
+#define VQ_QUOTA 0x1000 /* a user quota has been hit */
#define VQ_FLAG2000 0x2000 /* placeholder */
#define VQ_FLAG4000 0x4000 /* placeholder */
#define VQ_FLAG8000 0x8000 /* placeholder */
/* Private NFS spi */
#define KERNEL_MOUNT_NOAUTH 0x01 /* Don't check the UID of the directory we are mounting on */
#define KERNEL_MOUNT_PERMIT_UNMOUNT 0x02 /* Allow (non-forced) unmounts by users other the one who mounted the volume */
-#if NFSCLIENT || DEVFS
+#if NFSCLIENT || DEVFS || ROUTEFS
/*
* NOTE: kernel_mount() does not force MNT_NOSUID, MNT_NOEXEC, or MNT_NODEC for non-privileged
* mounting credentials, as the mount(2) system call does.
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple 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. 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_OSREFERENCE_LICENSE_HEADER_END@
+ */
+#ifndef _SYS_PERSONA_H_
+#define _SYS_PERSONA_H_
+
+#ifdef PRIVATE
+#include <sys/param.h>
+
+enum {
+ PERSONA_INVALID = 0,
+ PERSONA_GUEST = 1,
+ PERSONA_MANAGED = 2,
+ PERSONA_PRIV = 3,
+ PERSONA_SYSTEM = 4,
+
+ PERSONA_TYPE_MAX = PERSONA_SYSTEM,
+};
+
+#define PERSONA_ID_NONE ((uid_t)-1)
+
+struct kpersona_info {
+ uint32_t persona_info_version;
+
+ uid_t persona_id; /* overlaps with UID */
+ int persona_type;
+ gid_t persona_gid;
+ uint32_t persona_ngroups;
+ gid_t persona_groups[NGROUPS];
+ uid_t persona_gmuid;
+ char persona_name[MAXLOGNAME+1];
+
+ /* TODO: MAC policies?! */
+};
+
+#define PERSONA_INFO_V1 1
+#define PERSONA_INFO_V1_SIZE (sizeof(struct kpersona_info))
+
+
+#define PERSONA_OP_ALLOC 1
+#define PERSONA_OP_DEALLOC 2
+#define PERSONA_OP_GET 3
+#define PERSONA_OP_INFO 4
+#define PERSONA_OP_PIDINFO 5
+#define PERSONA_OP_FIND 6
+
+#ifndef KERNEL
+/*
+ * user space persona interface
+ */
+
+/*
+ * kpersona_alloc: Allocate a new in-kernel persona
+ *
+ * Parameters:
+ * info: Pointer to persona info structure describing the
+ * attributes of the persona to create / allocate.
+ *
+ * id: output: set to the ID of the created persona
+ *
+ * Note:
+ * The 'persona_id' field of the 'info' parameter is ignored.
+ *
+ * Return:
+ * != 0: ERROR
+ * == 0: Success
+ */
+int kpersona_alloc(struct kpersona_info *info, uid_t *id);
+
+/*
+ * kpersona_dealloc: delete / destroy an in-kernel persona
+ *
+ * Parameters:
+ * id: the ID of the persona to destroy
+ *
+ * Return:
+ * < 0: ERROR
+ * 0: Success
+ */
+int kpersona_dealloc(uid_t id);
+
+
+/*
+ * kpersona_get: retrieve the persona with which the current thread is running
+ *
+ * Parameters:
+ * id: output: will be filled with current thread's persona
+ * (or current processes persona) on success.
+ *
+ * Return:
+ * < 0: Thread is not running under any persona
+ * 0: Success (uuid is filled with running persona UUID)
+ */
+int kpersona_get(uid_t *id);
+
+
+/*
+ * kpersona_info: gather info about the given persona
+ *
+ * Parameters:
+ * id: ID of the persona to investigate
+ *
+ * info: output: filled in with persona attributes on success.
+ *
+ * Return:
+ * < 0: ERROR
+ * 0: Success
+ */
+int kpersona_info(uid_t id, struct kpersona_info *info);
+
+
+/*
+ * kpersona_pidinfo: gather persona info about the given PID
+ *
+ * Parameters:
+ * pid: PID of the process whose persona info we're to return
+ *
+ * info: output: filled in with persona attributes on success.
+ *
+ * Return:
+ * < 0: ERROR
+ * 0: Success
+ */
+int kpersona_pidinfo(pid_t pid, struct kpersona_info *info);
+
+
+/*
+ * kpersona_find: lookup the kernel's UUID of a persona
+ *
+ * Parameters:
+ * name: Local login name of the persona.
+ * Set this to NULL to find personas by 'uid'.
+ *
+ * uid: UID of the persona.
+ * Set this to -1 to find personas by 'name'
+ *
+ * id: output: the ID(s) matching the input parameters
+ * idlen: input - size of 'id' buffer (in number of IDs)
+ * output - the total required size of the 'id' buffer
+ * (in number of IDs) - may be larger than input size
+ * Note:
+ * At least one of 'name' or 'uid' must be set.
+ *
+ * Return:
+ * < 0: ERROR
+ * >= 0: The number of IDs found to match the input parameters
+ */
+int kpersona_find(const char *name, uid_t uid, uid_t *id, size_t *idlen);
+#endif /* !KERNEL */
+
+#ifdef KERNEL_PRIVATE
+/* XNU + kext private interface */
+#include <sys/cdefs.h>
+#include <sys/kauth.h>
+#include <libkern/libkern.h>
+
+#ifdef PERSONA_DEBUG
+#define persona_dbg(fmt, ...) \
+ printf("[%4d] %s: " fmt "\n", \
+ current_proc() ? current_proc()->p_pid : -1, \
+ __func__, ## __VA_ARGS__)
+#else
+#define persona_dbg(fmt, ...) do { } while (0)
+#endif
+
+/*
+ * Persona
+ */
+#ifdef XNU_KERNEL_PRIVATE
+/* only XNU proper needs to see the persona structure */
+struct persona {
+ int32_t pna_refcount;
+ int32_t pna_valid;
+
+ uid_t pna_id;
+ int pna_type;
+ char pna_login[MAXLOGNAME+1];
+
+ kauth_cred_t pna_cred;
+ uid_t pna_pgid;
+
+ int pna_cred_locked; /* set upon first adoption */
+
+ LIST_ENTRY(persona) pna_list;
+
+ /* this could go away if we used a coalition */
+ LIST_HEAD(, proc) pna_members;
+
+ lck_mtx_t pna_lock;
+
+ /*
+ * We can add things here such as PID maps, UID maps, etc.
+ */
+#ifdef PERSONA_DEBUG
+ char pna_desc[128];
+#endif
+};
+
+#define persona_lock(persona) lck_mtx_lock(&(persona)->pna_lock)
+#define persona_unlock(persona) lck_mtx_unlock(&(persona)->pna_lock)
+#define persona_try_lock(persona) lck_mtx_try_lock(&(persona)->pna_lock)
+
+#define persona_lock_assert_held(persona) \
+ lck_mtx_assert(&(persona)->pna_lock, LCK_MTX_ASSERT_OWNED)
+
+#ifdef PERSONA_DEBUG
+static inline const char *persona_desc(struct persona *persona, int locked)
+{
+ if (!persona)
+ return "<none>";
+
+ if (persona->pna_desc[0] != 0)
+ return persona->pna_desc;
+
+ if (!locked)
+ persona_lock(persona);
+ if (persona->pna_desc[0] != 0)
+ goto out_unlock;
+
+ char *p = &persona->pna_desc[0];
+ char *end = p + sizeof(persona->pna_desc) - 1;
+
+ *end = 0;
+ p += snprintf(p, end - p, "%s/%d:%d",
+ persona->pna_login,
+ kauth_cred_getuid(persona->pna_cred),
+ kauth_cred_getgid(persona->pna_cred));
+
+ if (p <= end)
+ *p = 0;
+out_unlock:
+ if (!locked)
+ persona_unlock(persona);
+
+ return persona->pna_desc;
+}
+#else /* !PERSONA_DEBUG */
+static inline const char *persona_desc(struct persona *persona, int locked)
+{
+ (void)persona;
+ (void)locked;
+ return "<persona>";
+}
+#endif
+
+#else /* !XNU_KERNEL_PRIVATE */
+/* kexts should only see an opaque persona structure */
+struct persona;
+#endif
+
+__BEGIN_DECLS
+
+#ifndef _KAUTH_CRED_T
+#define _KAUTH_CRED_T
+typedef struct ucred *kauth_cred_t;
+#endif /* !_KAUTH_CRED_T */
+
+/* returns the persona ID for the given pesona structure */
+uid_t persona_get_id(struct persona *persona);
+
+/* returns the type of the persona (see enum above: PERSONA_GUEST, etc.) */
+int persona_get_type(struct persona *persona);
+
+/* returns ref on kauth_cred_t that must be dropped via kauth_cred_unref() */
+kauth_cred_t persona_get_cred(struct persona *persona);
+
+/* returns a reference that must be released with persona_put() */
+struct persona *persona_lookup(uid_t id);
+
+/*
+ * returns non-zero on error, on success returns 0 and updates 'plen' to
+ * total found (could be more than original value of 'plen')
+ */
+int persona_find(const char *login, uid_t uid,
+ struct persona **persona, size_t *plen);
+
+/* returns a reference to the persona tied to the current thread */
+struct persona *current_persona_get(void);
+
+/* get a reference to a persona structure */
+struct persona *persona_get(struct persona *persona);
+
+/* release a reference to a persona structure */
+void persona_put(struct persona *persona);
+
+#ifdef XNU_KERNEL_PRIVATE
+
+#if CONFIG_PERSONAS
+#include <sys/proc_internal.h>
+
+/*
+ * In-kernel persona API
+ */
+extern uint32_t g_max_personas;
+extern struct persona *g_system_persona;
+
+void personas_bootstrap(void);
+
+struct persona *persona_alloc(uid_t id, const char *login,
+ int type, int *error);
+int persona_invalidate(struct persona *persona);
+
+static inline int proc_has_persona(proc_t p)
+{
+ if (p && p->p_persona)
+ return 1;
+ return 0;
+}
+
+static inline uid_t persona_id_from_proc(proc_t p)
+{
+ if (p && p->p_persona)
+ return p->p_persona->pna_id;
+ return PERSONA_ID_NONE;
+}
+
+int persona_proc_inherit(proc_t child, proc_t parent);
+
+int persona_proc_adopt_id(proc_t p, uid_t id,
+ kauth_cred_t auth_override);
+int persona_proc_adopt(proc_t p, struct persona *persona,
+ kauth_cred_t auth_override);
+int persona_proc_drop(proc_t p);
+
+int persona_set_cred(struct persona *persona, kauth_cred_t cred);
+int persona_set_cred_from_proc(struct persona *persona, proc_t proc);
+
+uid_t persona_get_uid(struct persona *persona);
+
+int persona_set_gid(struct persona *persona, gid_t gid);
+gid_t persona_get_gid(struct persona *persona);
+
+int persona_set_groups(struct persona *persona, gid_t *groups, int ngroups, uid_t gmuid);
+int persona_get_groups(struct persona *persona, int *ngroups, gid_t *groups, int groups_sz);
+
+uid_t persona_get_gmuid(struct persona *persona);
+
+int persona_get_login(struct persona *persona, char login[MAXLOGNAME+1]);
+
+/* returns a reference that must be released with persona_put() */
+struct persona *persona_proc_get(pid_t pid);
+
+#else /* !CONFIG_PERSONAS */
+
+static inline int proc_has_persona(__unused proc_t p)
+{
+ return 0;
+}
+
+static inline uid_t persona_id_from_proc(__unused proc_t p)
+{
+ return PERSONA_ID_NONE;
+}
+
+#endif /* CONFIG_PERSONAS */
+#endif /* XNU_KERNEL_PRIVATE */
+__END_DECLS
+
+#endif /* KERNEL_PRIVATE */
+
+#endif /* PRIVATE */
+#endif /* _SYS_PERSONA_H_ */
#define P_THCWD 0x01000000 /* process has thread cwd */
#define P_RESV9 0x02000000 /* (P_VFORK)process has vfork children */
-#define P_RESV10 0x04000000 /* reserved flag */
+#define P_ADOPTPERSONA 0x04000000 /* process adopted a persona (used to be P_NOATTACH) */
#define P_RESV11 0x08000000 /* (P_INVFORK) proc in vfork */
#define P_NOSHLIB 0x10000000 /* no shared libs are in use for proc */
extern int proc_selfpid(void);
/* this routine returns the pid of the parent of the current process */
extern int proc_selfppid(void);
+/* this routine returns the csflags of the current process */
+extern int proc_selfcsflags(void);
/* this routine returns sends a signal signum to the process identified by the pid */
extern void proc_signal(int pid, int signum);
/* this routine checks whether any signal identified by the mask are pending in the process identified by the pid. The check is on all threads of the process. */
task_t proc_task(proc_t);
extern int proc_pidversion(proc_t);
+extern uint32_t proc_persona_id(proc_t);
+extern uint32_t proc_getuid(proc_t);
+extern uint32_t proc_getgid(proc_t);
extern int proc_getcdhash(proc_t, unsigned char *);
/*!
LIST_ENTRY(proc) p_hash; /* Hash chain. (LL)*/
TAILQ_HEAD( ,eventqelt) p_evlist; /* (PL) */
+#if CONFIG_PERSONAS
+ struct persona *p_persona;
+ LIST_ENTRY(proc) p_persona_list;
+#endif
+
lck_mtx_t p_fdmlock; /* proc lock to protect fdesc */
lck_mtx_t p_ucred_mlock; /* mutex lock to protect p_ucred */
#define _SYS_REBOOT_H_
#include <sys/appleapiopts.h>
+#include <sys/cdefs.h>
+#include <stdint.h>
/*
* Arguments to reboot system call.
#define RB_QUICK 0x400 /* quick and ungraceful reboot with file system caches flushed*/
#define RB_PANIC 0x800 /* panic the kernel */
+#ifndef KERNEL
+__BEGIN_DECLS
+/* userspace reboot control */
+int usrctl(uint32_t flags);
+__END_DECLS
+#endif
+
#endif /* __APPLE_API_PRIVATE */
#ifdef __APPLE_API_OBSOLETE
#ifdef PRIVATE
#define SO_AWDL_UNRESTRICTED 0x1113 /* try to use AWDL in restricted mode */
#define SO_EXTENDED_BK_IDLE 0x1114 /* extended time to keep socket idle after app is suspended (int) */
+#define SO_MARK_CELLFALLBACK 0x1115 /* Mark as initiated by cell fallback */
#endif /* PRIVATE */
typedef __uint32_t sae_associd_t;
/*
- * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#define SOF1_EXTEND_BK_IDLE_INPROG 0x00000080 /* socket */
#define SOF1_CACHED_IN_SOCK_LAYER 0x00000100 /* bundled with inpcb and
tcpcb */
-
+#define SOF1_TFO_REWIND 0x00000200 /* rewind mptcp meta data */
+#define SOF1_CELLFALLBACK 0x00000400 /* Initiated by cell fallback */
u_int64_t so_extended_bk_start;
};
"\020\1LOCKED\2CONNRESET\3CANTRCVMORE\4CANTSENDMORE\5TIMEOUT" \
"\6NOSRCADDR\7IFDENIED\10SUSPEND\11RESUME\12KEEPALIVE\13AWTIMO" \
"\14ARTIMO\15CONNECTED\16DISCONNECTED\17CONNINFO_UPDATED" \
- "\20MPFAILOVER\21MPSTATUS\22MUSTRST\23MPFASTJ\24DELETEOK" \
- "\25MPCANTRCVMORE"
+ "\20MPFAILOVER\21MPSTATUS\22MUSTRST\23MPFASTJ\25DELETEOK" \
+ "\26MPCANTRCVMORE"
/* Mask for hints that have corresponding kqueue events */
#define SO_FILT_HINT_EV \
extern int soopt_mcopyout(struct sockopt *sopt, struct mbuf *m);
extern boolean_t so_cache_timer(void);
-extern void mptcp_preproc_sbdrop(struct mbuf *, unsigned int);
+extern void mptcp_preproc_sbdrop(struct socket *, struct mbuf *, unsigned int);
extern void mptcp_postproc_sbdrop(struct mbuf *, u_int64_t, u_int32_t,
u_int32_t);
extern int mptcp_adj_rmap(struct socket *, struct mbuf *);
} psci_info[COALITION_NUM_TYPES];
};
+/*
+ * Persona attributes
+ */
+struct _posix_spawn_persona_info {
+ uid_t pspi_id; /* persona ID (unix UID) */
+ uint32_t pspi_flags; /* spawn persona flags */
+ uid_t pspi_uid; /* alternate posix/unix UID */
+ gid_t pspi_gid; /* alternate posix/unix GID */
+ uint32_t pspi_ngroups; /* alternate advisory groups */
+ gid_t pspi_groups[NGROUPS];
+ uid_t pspi_gmuid; /* group membership UID */
+};
+
+#define POSIX_SPAWN_PERSONA_FLAGS_NONE 0x0
+#define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 0x1
+#define POSIX_SPAWN_PERSONA_FLAGS_VERIFY 0x2
+
+#define POSIX_SPAWN_PERSONA_ALL_FLAGS \
+ (POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE \
+ | POSIX_SPAWN_PERSONA_FLAGS_VERIFY \
+ )
+
+#define POSIX_SPAWN_PERSONA_UID 0x00010000
+#define POSIX_SPAWN_PERSONA_GID 0x00020000
+#define POSIX_SPAWN_PERSONA_GROUPS 0x00040000
+
+
/*
* A posix_spawnattr structure contains all of the attribute elements that
* can be set, as well as any metadata whose validity is signalled by the
_posix_spawn_port_actions_t psa_ports; /* special/exception ports */
_posix_spawn_mac_policy_extensions_t psa_mac_extensions; /* MAC policy-specific extensions. */
struct _posix_spawn_coalition_info *psa_coalition_info; /* coalition info */
- void *reserved;
+ struct _posix_spawn_persona_info *psa_persona_info; /* spawn new process into given persona */
} *_posix_spawnattr_t;
/*
__darwin_size_t coal_info_size;
struct _posix_spawn_coalition_info *coal_info; /* pointer to coalition info */
- __darwin_size_t reserved_size;
- void *reserved;
+ __darwin_size_t persona_info_size;
+ struct _posix_spawn_persona_info *persona_info;
};
#ifdef KERNEL
uint32_t mac_extensions;
uint32_t coal_info_size;
uint32_t coal_info;
- uint32_t reserved_size;
- uint32_t reserved;
+ uint32_t persona_info_size;
+ uint32_t persona_info;
};
struct user__posix_spawn_args_desc {
user_addr_t mac_extensions; /* pointer to block */
user_size_t coal_info_size;
user_addr_t coal_info;
- user_size_t reserved_size;
- user_addr_t reserved;
+ user_size_t persona_info_size;
+ user_addr_t persona_info;
};
vm_offset_t csb_mem_offset;
vm_address_t csb_mem_kaddr;
unsigned char csb_cdhash[CS_CDHASH_LEN];
- struct cs_hash *csb_hashtype;
+ struct cs_hash *csb_hashtype;
+ const CS_CodeDirectory *csb_cd;
const char *csb_teamid;
unsigned int csb_platform_binary:1;
unsigned int csb_platform_path:1;
@result Non-zero to indicate that the vnode represents a tty device. Zero otherwise.
*/
int vnode_istty(vnode_t vp);
+
+/*
+ * Get the context for the first kernel thread (private SPI)
+ */
+vfs_context_t vfs_context_kernel(void); /* get from 1st kernel thread */
#endif /* KERNEL_PRIVATE */
#ifdef BSD_KERNEL_PRIVATE
int check_mountedon(dev_t dev, enum vtype type, int *errorp);
int vn_getcdhash(struct vnode *vp, off_t offset, unsigned char *cdhash);
void vnode_reclaim(vnode_t);
-vfs_context_t vfs_context_kernel(void); /* get from 1st kernel thread */
int vfs_context_issuser(vfs_context_t);
vnode_t vfs_context_cwd(vfs_context_t);
vnode_t current_rootdir(void);
/*
* NAME_CACHE_LOCK holds these fields stable
+ *
+ * We can't cache KAUTH_VNODE_SEARCHBYANYONE for root correctly
+ * so we make an ugly check for root here. root is always
+ * allowed and breaking out of here only to find out that is
+ * authorized by virtue of being root is very very expensive.
*/
if ((dp->v_cred != ucred || !(dp->v_authorized_actions & KAUTH_VNODE_SEARCH)) &&
- !(dp->v_authorized_actions & KAUTH_VNODE_SEARCHBYANYONE))
+ !(dp->v_authorized_actions & KAUTH_VNODE_SEARCHBYANYONE) &&
+ !vfs_context_issuser(ctx))
break;
/*
extern struct vfsops afs_vfsops;
extern struct vfsops null_vfsops;
extern struct vfsops devfs_vfsops;
+extern struct vfsops routefs_vfsops;
#if MOCKFS
extern struct vfsops mockfs_vfsops;
FT_HFS = 17,
FT_DEVFS = 19,
FT_SYNTHFS = 20,
+ FT_ROUTEFS = 21,
FT_MOCKFS = 0x6D6F636B
};
{ &mockfs_vfsops, "mockfs", FT_MOCKFS, 0, MNT_LOCAL, mockfs_mountroot, NULL, 0, 0, VFC_VFSGENERICARGS, NULL, 0, NULL},
#endif /* MOCKFS */
+#if ROUTEFS
+ /* If we are configured for it, mockfs should always be the last standard entry (and thus the last FS we attempt mountroot with) */
+ { &routefs_vfsops, "routefs", FT_ROUTEFS, 0, MNT_LOCAL, NULL, NULL, 0, 0, VFC_VFSGENERICARGS | VFC_VFS64BITREADY, NULL, 0, NULL},
+#endif /* ROUTEFS */
{NULL, "<unassigned>", 0, 0, 0, NULL, NULL, 0, 0, 0, NULL, 0, NULL},
{NULL, "<unassigned>", 0, 0, 0, NULL, NULL, 0, 0, 0, NULL, 0, NULL},
};
* deny execute, we can synthesize a global right that allows anyone to
* traverse this directory during a pathname lookup without having to
* match the credential associated with this cache of rights.
+ *
+ * Note that we can correctly cache KAUTH_VNODE_SEARCHBYANYONE
+ * only if we actually check ACLs which we don't for root. As
+ * a workaround, the lookup fast path checks for root.
*/
if (!VATTR_IS_SUPPORTED(&va, va_mode) ||
((va.va_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ==
#include <sys/ubc_internal.h>
#include <sys/disk.h>
#include <sys/content_protection.h>
+#include <sys/priv.h>
#include <machine/cons.h>
#include <machine/limits.h>
#include <miscfs/specfs/specdev.h>
#include <pexpert/pexpert.h>
#include <IOKit/IOBSD.h>
+#if ROUTEFS
+#include <miscfs/routefs/routefs.h>
+#endif /* ROUTEFS */
+
#if CONFIG_MACF
#include <security/mac.h>
#include <security/mac_framework.h>
* Virtual File System System Calls
*/
-#if NFSCLIENT || DEVFS
+#if NFSCLIENT || DEVFS || ROUTEFS
/*
* Private in-kernel mounting spi (NFS only, not exported)
*/
/* XXX 3762912 hack to support HFS filesystem 'owner' - filesystem may update later */
vfs_setowner(mp, KAUTH_UID_NONE, KAUTH_GID_NONE);
-#if NFSCLIENT || DEVFS
+#if NFSCLIENT || DEVFS || ROUTEFS
if (kernelmount)
mp->mnt_kern_flag |= MNTK_KERNEL_MOUNT;
if ((internal_flags & KERNEL_MOUNT_PERMIT_UNMOUNT) != 0)
int pathlen = 0;
vfs_context_t ctx = vfs_context_current();
+ if ((error = priv_check_cred(vfs_context_ucred(ctx), PRIV_VFS_OPEN_BY_ID, 0))) {
+ return (error);
+ }
+
if ((error = copyin(uap->fsid, (caddr_t)&fsid, sizeof(fsid)))) {
return (error);
}
}
break;
+ case FSCTL_ROUTEFS_SETROUTEID: {
+#if ROUTEFS
+ char routepath[MAXPATHLEN];
+ size_t len = 0;
+
+ if ((error = suser(kauth_cred_get(), &(current_proc()->p_acflag)))) {
+ break;
+ }
+ bzero(routepath, MAXPATHLEN);
+ error = copyinstr(udata, &routepath[0], MAXPATHLEN, &len);
+ if (error) {
+ break;
+ }
+ error = routefs_kernel_mount(routepath);
+ if (error) {
+ break;
+ }
+#endif
+ }
+ break;
+
case FSCTL_SET_PACKAGE_EXTS: {
user_addr_t ext_strings;
uint32_t num_entries;
/*
- * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
int _shared_region_map_and_slide(struct proc*, int, unsigned int, struct shared_file_mapping_np*, uint32_t, user_addr_t, user_addr_t);
int shared_region_copyin_mappings(struct proc*, user_addr_t, unsigned int, struct shared_file_mapping_np *);
+
#if DEVELOPMENT || DEBUG
extern int radar_20146450;
SYSCTL_INT(_vm, OID_AUTO, radar_20146450, CTLFLAG_RW | CTLFLAG_LOCKED, &radar_20146450, 0, "");
options FIFO # fifo support # <fifo>
options FDESC # fdesc_fs support # <fdesc>
options DEVFS # devfs support # <devfs>
+options ROUTEFS # routefs support # <routefs>
options JOURNALING # journaling support # <journaling>
options HFS_COMPRESSION # hfs compression # <hfs_compression>
options CONFIG_HFS_STD # hfs standard support # <config_hfs_std>
#
options CONFIG_KEXT_BASEMENT # # <config_kext_basement>
+#
+# Persona Management
+#
+options CONFIG_PERSONAS # Persona management # <config_personas>
+options PERSONA_DEBUG # Persona debugging # <persona_debug>
+
#
# security configuration options
#
# KERNEL_DEV = [ KERNEL_BASE development mach_assert config_xnupost proc_ref_debug]
# KERNEL_DEBUG = [ KERNEL_BASE debug mach_assert config_waitq_stats config_waitq_debug ]
# BSD = [ mach_bsd sysv_sem sysv_msg sysv_shm config_imageboot config_workqueue psynch config_proc_uuid_policy ]
-# FILESYS = [ devfs hfs journaling fdesc config_dev_kmem config_fse quota namedstreams fifo config_volfs hfs_compression config_hfs_std config_hfs_alloc_rbtree config_hfs_trim config_imgsrc_access config_triggers config_ext_resolver config_searchfs config_hfs_dirlink config_appledouble ]
+# FILESYS_BASE = [ devfs hfs journaling fdesc config_dev_kmem config_fse quota namedstreams fifo config_volfs hfs_compression config_hfs_std config_hfs_alloc_rbtree config_hfs_trim config_imgsrc_access config_triggers config_ext_resolver config_searchfs config_hfs_dirlink config_appledouble ]
+# FILESYS_RELEASE= [ FILESYS_BASE ]
+# FILESYS_DEV = [ FILESYS_BASE ]
+# FILESYS_DEBUG = [ FILESYS_BASE ]
# NFS = [ nfsclient nfsserver ]
# NETWORKING = [ inet inet6 ipv6send tcpdrop_synfin bpfilter ipdivert ipfirewall ipv6firewall ipfw2 dummynet traffic_mgt sendfile ah_all_crypto bond vlan gif stf ifnet_input_chk config_mbuf_jumbo if_bridge ipcomp_zlib MULTIPATH packet_mangler ]
# VPN = [ ipsec flow_divert necp content_filter ]
# SCHED_DEBUG = [ SCHED_BASE config_sched_grrr config_sched_proto ]
# VM = [ vm_pressure_events memorystatus dynamic_codesigning config_code_decryption encrypted_swap phantom_cache]
# SECURITY = [ config_macf config_audit config_csr ]
-# RELEASE = [ KERNEL_RELEASE BSD FILESYS NFS NETWORKING PF VPN IOKIT_RELEASE LIBKERN_RELEASE PERF_DBG MACH_RELEASE SCHED_RELEASE VM SECURITY ]
-# DEVELOPMENT = [ KERNEL_DEV BSD FILESYS NFS NETWORKING PF VPN IOKIT_DEV LIBKERN_DEV PERF_DBG MACH_DEV SCHED_DEV VM SECURITY ]
-# DEBUG = [ KERNEL_DEBUG BSD FILESYS NFS NETWORKING PF VPN IOKIT_DEBUG LIBKERN_DEBUG PERF_DBG MACH_DEBUG SCHED_DEBUG VM SECURITY ]
+# RELEASE = [ KERNEL_RELEASE BSD FILESYS_RELEASE NFS NETWORKING PF VPN IOKIT_RELEASE LIBKERN_RELEASE PERF_DBG MACH_RELEASE SCHED_RELEASE VM SECURITY ]
+# DEVELOPMENT = [ KERNEL_DEV BSD FILESYS_DEV NFS NETWORKING PF VPN IOKIT_DEV LIBKERN_DEV PERF_DBG MACH_DEV SCHED_DEV VM SECURITY ]
+# DEBUG = [ KERNEL_DEBUG BSD FILESYS_DEBUG NFS NETWORKING PF VPN IOKIT_DEBUG LIBKERN_DEBUG PERF_DBG MACH_DEBUG SCHED_DEBUG VM SECURITY ]
#
######################################################################
#
-15.3.0
+15.4.0
# The first line of this file contains the master version number for the kernel.
# All other instances of the kernel version in xnu are derived from this file.
_net_del_domain:_net_del_domain_old
_net_del_proto:_net_del_proto_old
_netboot_root
+_persona_find
+_persona_get
+_persona_get_id
+_persona_get_type
+_persona_get_cred
+_persona_lookup
+_current_persona_get
+_persona_put
_pffinddomain:_pffinddomain_old
_pffindproto:_pffindproto_old
_port_name_to_task
_vfs_context_bind
_vfs_context_get_special_port
_vfs_context_set_special_port
+_vfs_context_kernel
_vfs_devvp
_vfs_getattr
_vfs_getbyid
_cpuid_info
_csr_check
_csr_get_active_config
-_csr_set_allow_all
_hv_ept_pmap_create
_hv_get*
_hv_release*
kIOClassNameOverrideNone = 0x00000001,
};
+#define kIOServiceLegacyMatchingRegistryIDKey "IOServiceLegacyMatchingRegistryID"
+
#endif /* ! _IOKIT_IOKITKEYSPRIVATE_H */
enum
{
- kIOPolledPreflightState = 1,
- kIOPolledBeforeSleepState = 2,
- kIOPolledAfterSleepState = 3,
- kIOPolledPostflightState = 4,
+ kIOPolledPreflightState = 1,
+ kIOPolledBeforeSleepState = 2,
+ kIOPolledAfterSleepState = 3,
+ kIOPolledPostflightState = 4,
kIOPolledPreflightCoreDumpState = 5,
+ kIOPolledPostflightCoreDumpState = 6,
};
#if defined(__cplusplus)
uint64_t getAuthorizationID( void );
IOReturn setAuthorizationID( uint64_t authorizationID );
void cpusRunning(void);
+ void scheduleFinalize(bool now);
private:
static IOReturn waitMatchIdle( UInt32 ms );
bool terminatePhase1( IOOptionBits options = 0 );
void scheduleTerminatePhase2( IOOptionBits options = 0 );
void scheduleStop( IOService * provider );
- void scheduleFinalize( void );
static void terminateThread( void * arg, wait_result_t unused );
static void terminateWorker( IOOptionBits options );
static void actionWillTerminate( IOService * victim, IOOptionBits options,
OSSet * mappings;
UInt8 sharedInstance;
UInt8 closed;
- UInt8 __reservedA[2];
+ UInt8 __ipcFinal;
+ UInt8 __reservedA[1];
+ volatile SInt32 __ipc;
+#if __LP64__
void * __reserved[7];
+#else
+ void * __reserved[6];
+#endif
public:
virtual IOReturn externalMethod( uint32_t selector, IOExternalMethodArguments * arguments,
public:
static void initialize( void );
static void destroyUserReferences( OSObject * obj );
+ static bool finalizeUserReferences( OSObject * obj );
IOMemoryMap * mapClientMemory64( IOOptionBits type,
task_t task,
IOOptionBits mapFlags = kIOMapAnywhere,
extern "C" {
#include <machine/machine_routines.h>
#include <pexpert/pexpert.h>
+#include <kern/cpu_number.h>
}
#include <machine/machine_routines.h>
if (target->getCPUNumber() == kBootCPUNumber)
{
bootCPU = target;
- } else if (target->getCPUState() == kIOCPUStateRunning)
+ } else if (target->getCPUState() == kIOCPUStateRunning)
{
- target->haltCPU();
+ target->haltCPU();
}
}
+ assert(bootCPU != NULL);
+ assert(cpu_number() == 0);
+
rootDomain->tracePoint( kIOPMTracePointSleepPlatformDriver );
// Now sleep the boot CPU.
- if (bootCPU)
- bootCPU->haltCPU();
+ bootCPU->haltCPU();
rootDomain->tracePoint( kIOPMTracePointWakePlatformActions );
bool IOCPU::setProperty(const OSSymbol *aKey, OSObject *anObject)
{
- OSString *stateStr;
-
if (aKey == gIOCPUStateKey) {
- stateStr = OSDynamicCast(OSString, anObject);
- if (stateStr == 0) return false;
-
- if (_cpuNumber == 0) return false;
-
- if (stateStr->isEqualTo("running")) {
- if (_cpuState == kIOCPUStateStopped) {
- processor_start(machProcessor);
- } else if (_cpuState != kIOCPUStateRunning) {
- return false;
- }
- } else if (stateStr->isEqualTo("stopped")) {
- if (_cpuState == kIOCPUStateRunning) {
- haltCPU();
- } else if (_cpuState != kIOCPUStateStopped) {
- return false;
- }
- } else return false;
-
- return true;
+ return false;
}
-
+
return super::setProperty(aKey, anObject);
}
#include <pexpert/device_tree.h>
+typedef UInt32 dtptr_t;
+
#include <machine/machine_routines.h>
extern "C" {
{
IORegistryEntry *chosen;
OSData *propObj;
- unsigned int *propPtr;
+ dtptr_t *propPtr;
unsigned int propSize;
chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane );
if ( propObj == 0 ) return -1;
propSize = propObj->getLength();
- if ( propSize != (2 * sizeof(UInt32)) ) return -1;
+ if ( propSize != (2 * sizeof(dtptr_t)) ) return -1;
- propPtr = (unsigned int *)propObj->getBytesNoCopy();
+ propPtr = (dtptr_t *)propObj->getBytesNoCopy();
if ( propPtr == 0 ) return -1;
*infoAddr = (void *)(uintptr_t) (propPtr[0]);
_wireCount++; // Physical MDs are, by definition, wired
else { /* kIOMemoryTypeVirtual | kIOMemoryTypeVirtual64 | kIOMemoryTypeUIO */
ioGMDData *dataP;
- mach_vm_size_t dataSize = computeDataSize(_pages, /* upls */ count * 2);
- if (dataSize != ((unsigned) dataSize)) return false; /* overflow */
+ unsigned dataSize;
+ if (_pages > atop_64(max_mem)) return false;
+
+ dataSize = computeDataSize(_pages, /* upls */ count * 2);
if (!initMemoryEntries(dataSize, mapper)) return false;
dataP = getDataP(_memoryEntries);
dataP->fPageCnt = _pages;
sysctl_consoleoptions
(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
{
- int error;
- int new_value, changed;
+ int error, changed;
+ uint32_t new_value;
- error = sysctl_io_number(req, vc_user_options, sizeof(int), &new_value, &changed);
+ error = sysctl_io_number(req, vc_user_options.options, sizeof(uint32_t), &new_value, &changed);
- if (changed) vc_set_options(new_value);
+ if (changed) vc_user_options.options = new_value;
return (error);
}
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED,
0, 0, sysctl_consoleoptions, "I", "");
+
+static int
+sysctl_progressoptions SYSCTL_HANDLER_ARGS
+{
+ return sysctl_io_opaque(req, &vc_user_options, sizeof(vc_user_options), NULL);
+}
+
+static SYSCTL_PROC(_kern, OID_AUTO, progressoptions,
+ CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED | CTLFLAG_ANYBODY,
+ NULL, 0, sysctl_progressoptions, "S,vc_progress_user_options", "");
+
+
static int
sysctl_wakereason SYSCTL_HANDLER_ARGS
{
sysctl_register_oid(&sysctl__kern_progressmeter);
sysctl_register_oid(&sysctl__kern_wakereason);
sysctl_register_oid(&sysctl__kern_consoleoptions);
+ sysctl_register_oid(&sysctl__kern_progressoptions);
#if HIBERNATION
IOHibernateSystemInit(this);
bool io;
IOReturn ioStatus;
uint32_t openCount;
- uint32_t openState;
static IOPolledFilePollers * copyPollers(IOService * media);
};
{
poller = (IOPolledInterface *) vars->pollers->getObject(idx);
err = poller->open(state, ioBuffer);
- if ((kIOReturnSuccess != err) && (kIOPolledPreflightCoreDumpState == state))
- {
- err = poller->open(kIOPolledPreflightState, ioBuffer);
- }
if (kIOReturnSuccess != err)
{
HIBLOG("IOPolledInterface::open[%d] 0x%x\n", idx, err);
break;
}
}
- if (kIOReturnSuccess == err)
+ if ((kIOReturnSuccess == err) && (kIOPolledPreflightState == state))
{
next = vars->media;
while (next)
(void) IOPolledFilePollersIODone(vars, false);
- if (kIOPolledPostflightState == state)
+ if ((kIOPolledPostflightState == state) || (kIOPolledPostflightCoreDumpState == state))
{
vars->openCount--;
- if (vars->openCount)
- {
- // 21207427
- IOPolledFilePollersOpen(filevars, vars->openState, vars->abortable);
- return (kIOReturnSuccess);
- }
}
for (idx = 0, err = kIOReturnSuccess;
}
if (kIOPolledPostflightState == state)
- {
+ {
next = vars->media;
while (next)
{
next->removeProperty(kIOPolledInterfaceActiveKey);
next = next->getParentEntry(gIOServicePlane);
}
+ }
+ if ((kIOPolledPostflightState == state) || (kIOPolledPostflightCoreDumpState == state)) do
+ {
+ if (vars->openCount) break;
if (vars->ioBuffer)
{
vars->ioBuffer->release();
vars->ioBuffer = 0;
}
}
+ while (false);
+
return (err);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
{
err = IOPolledFilePollersProbe(vars->pollers);
if (kIOReturnSuccess != err) break;
- err = IOPolledFilePollersOpen(vars, openState, false);
- if (kIOReturnSuccess != err) break;
- vars->pollers->openState = openState;
}
- vars->pollers->openCount++;
+ err = IOPolledFilePollersOpen(vars, openState, false);
+ if (kIOReturnSuccess != err) break;
+ if ((kIOPolledPreflightState == openState) || (kIOPolledPreflightCoreDumpState == openState))
+ {
+ vars->pollers->openCount++;
+ }
vars->pollers->io = false;
vars->buffer = (uint8_t *) vars->pollers->ioBuffer->getBytesNoCopy();
vars->bufferHalf = 0;
if( adjParent) provider->_adjustBusy( -1 );
if( (provider->__state[1] & kIOServiceTermPhase3State)
&& (0 == provider->getClient())) {
- provider->scheduleFinalize();
+ provider->scheduleFinalize(false);
}
provider->unlockForArbitration();
}
IOLockUnlock( gJobsLock );
}
-void IOService::scheduleFinalize( void )
+void IOService::scheduleFinalize(bool now)
{
uint64_t regID1 = getRegistryEntryID();
(uintptr_t) (regID1 >> 32),
0, 0);
- IOLockLock( gJobsLock );
- gIOFinalizeList->tailQ( this );
-
- if( 0 == gIOTerminateWork++) {
- if( !gIOTerminateThread)
- kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread);
- else
- IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
+ if (now || IOUserClient::finalizeUserReferences(this))
+ {
+ IOLockLock( gJobsLock );
+ gIOFinalizeList->tailQ(this);
+ if( 0 == gIOTerminateWork++)
+ {
+ if( !gIOTerminateThread)
+ kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread);
+ else
+ IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false );
+ }
+ IOLockUnlock( gJobsLock );
}
-
- IOLockUnlock( gJobsLock );
}
bool IOService::willTerminate( IOService * provider, IOOptionBits options )
}
if( 0 == victim->getClient()) {
+
// no clients - will go to finalize
- IOLockLock( gJobsLock );
- gIOFinalizeList->tailQ( victim );
- IOLockUnlock( gJobsLock );
+ victim->scheduleFinalize(false);
+
} else {
_workLoopAction( (IOWorkLoop::Action) &actionWillTerminate,
victim, (void *)(uintptr_t) options, (void *)(uintptr_t) doPhase2List );
const OSSymbol * sym = OSSymbol::withString(str);
OSMetaClass::applyToInstancesOfClassName(sym, instanceMatch, &ctx);
sym->release();
-
}
else
{
{
count = table->getCount();
done = 0;
-
str = OSDynamicCast(OSString, table->getObject(gIOProviderClassKey));
if (str) {
assert( table );
+ OSArray* aliasServiceRegIds = NULL;
+ IOService* foundAlternateService = NULL;
+
#if MATCH_DEBUG
OSDictionary * root = table;
#endif
// do family specific matching
match = where->matchPropertyTable( table, &score );
-
+
if( !match) {
#if IOMATCHDEBUG
if( kIOLogMatch & getDebugFlags( table ))
nextTable = OSDynamicCast(OSDictionary,
table->getObject( gIOParentMatchKey ));
if(nextTable) {
-
// look for a matching entry anywhere up to root
match = false;
matchParent = true;
break;
}
while (true);
+
+ if(match == true) {
+ break;
+ }
+
+ if(matchParent == true) {
+ // check if service has an alias to search its other "parents" if a parent match isn't found
+ OSNumber* alternateRegistryID = OSDynamicCast(OSNumber, where->getProperty(kIOServiceLegacyMatchingRegistryIDKey));
+ if(alternateRegistryID != NULL) {
+ if(aliasServiceRegIds == NULL)
+ {
+ aliasServiceRegIds = OSArray::withCapacity(sizeof(alternateRegistryID));
+ }
+ aliasServiceRegIds->setObject(alternateRegistryID);
+ }
+ }
+ else {
+ break;
+ }
+
+ where = where->getProvider();
+ if(where == NULL) {
+ // there were no matching parent services, check to see if there are aliased services that have a matching parent
+ if(aliasServiceRegIds != NULL) {
+ unsigned int numAliasedServices = aliasServiceRegIds->getCount();
+ if(numAliasedServices != 0) {
+ OSNumber* alternateRegistryID = OSDynamicCast(OSNumber, aliasServiceRegIds->getObject(numAliasedServices - 1));
+ if(alternateRegistryID != NULL) {
+ OSDictionary* alternateMatchingDict = IOService::registryEntryIDMatching(alternateRegistryID->unsigned64BitValue());
+ aliasServiceRegIds->removeObject(numAliasedServices - 1);
+ if(alternateMatchingDict != NULL) {
+ OSSafeReleaseNULL(foundAlternateService);
+ foundAlternateService = IOService::copyMatchingService(alternateMatchingDict);
+ alternateMatchingDict->release();
+ if(foundAlternateService != NULL) {
+ where = foundAlternateService;
+ }
+ }
+ }
+ }
+ }
+ }
}
- while( matchParent && (!match) && (where = where->getProvider()) );
+ while( where != NULL );
+
+ OSSafeRelease(foundAlternateService);
+ OSSafeRelease(aliasServiceRegIds);
#if MATCH_DEBUG
- if (where != this)
+ if (where != this)
{
OSSerialize * s = OSSerialize::withCapacity(128);
root->serialize(s);
obj->release();
}
+void
+iokit_add_connect_reference( io_object_t obj )
+{
+ IOUserClient * uc;
+
+ if (!obj) return;
+
+ if ((uc = OSDynamicCast(IOUserClient, obj))) OSIncrementAtomic(&uc->__ipc);
+
+ obj->retain();
+}
+
+void
+iokit_remove_connect_reference( io_object_t obj )
+{
+ IOUserClient * uc;
+ bool finalize = false;
+
+ if (!obj) return;
+
+ if ((uc = OSDynamicCast(IOUserClient, obj)))
+ {
+ if (1 == OSDecrementAtomic(&uc->__ipc) && uc->isInactive())
+ {
+ IOLockLock(gIOObjectPortLock);
+ if ((finalize = uc->__ipcFinal)) uc->__ipcFinal = false;
+ IOLockUnlock(gIOObjectPortLock);
+ }
+ if (finalize) uc->scheduleFinalize(true);
+ }
+
+ obj->release();
+}
+
+bool
+IOUserClient::finalizeUserReferences(OSObject * obj)
+{
+ IOUserClient * uc;
+ bool ok = true;
+
+ if ((uc = OSDynamicCast(IOUserClient, obj)))
+ {
+ IOLockLock(gIOObjectPortLock);
+ if ((uc->__ipcFinal = (0 != uc->__ipc))) ok = false;
+ IOLockUnlock(gIOObjectPortLock);
+ }
+ return (ok);
+}
+
ipc_port_t
iokit_port_for_object( io_object_t obj, ipc_kobject_type_t type )
{
io_name_t className )
{
const OSMetaClass* my_obj = NULL;
- const char * my_class_name = NULL;
if( !object)
return( kIOReturnBadArgument );
- if ( !my_class_name ) {
- my_obj = object->getMetaClass();
- if (!my_obj) {
- return (kIOReturnNotFound);
- }
-
- my_class_name = my_obj->getClassName();
+ my_obj = object->getMetaClass();
+ if (!my_obj) {
+ return (kIOReturnNotFound);
}
-
- strlcpy( className, my_class_name, sizeof(io_name_t));
+
+ strlcpy( className, my_obj->getClassName(), sizeof(io_name_t));
return( kIOReturnSuccess );
}
obj = matching_size ? OSUnserializeXML(matching, matching_size)
: OSUnserializeXML(matching);
if( (dict = OSDynamicCast( OSDictionary, obj))) {
-
*matches = service->passiveMatch( dict );
kr = kIOReturnSuccess;
} else
}
}
- userClient->release();
+ iokit_remove_connect_reference(userClient);
}
return result;
return (err);
}
-
#if __LP64__
OSMetaClassDefineReservedUnused(IOUserClient, 0);
OSMetaClassDefineReservedUnused(IOUserClient, 1);
static void
IOClosePolledCoreFile(void)
{
- IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightState);
+ IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightCoreDumpState);
IOPolledFileClose(&gIOPolledCoreFileVars, NULL, NULL, 0, 0, 0);
}
include $(MakeInc_def)
INSTINC_SUBDIRS = \
- libkern
-INSTINC_SUBDIRS_X86_64 = libkern
-INSTINC_SUBDIRS_X86_64H = libkern
-INSTINC_SUBDIRS_ARM = libkern
-INSTINC_SUBDIRS_ARM64 = libkern
+ libkern os
+INSTINC_SUBDIRS_X86_64 = libkern os
+INSTINC_SUBDIRS_X86_64H = libkern os
+INSTINC_SUBDIRS_ARM = libkern os
+INSTINC_SUBDIRS_ARM64 = libkern os
EXPINC_SUBDIRS = \
- libkern
-EXPINC_SUBDIRS_X86_64 = libkern
-EXPINC_SUBDIRS_X86_64H = libkern
-EXPINC_SUBDIRS_ARM = libkern
-EXPINC_SUBDIRS_ARM64 = libkern
+ libkern os
+EXPINC_SUBDIRS_X86_64 = libkern os
+EXPINC_SUBDIRS_X86_64H = libkern os
+EXPINC_SUBDIRS_ARM = libkern os
+EXPINC_SUBDIRS_ARM64 = libkern os
COMP_SUBDIRS = conf
--- /dev/null
+export MakeInc_cmd=${SRCROOT}/makedefs/MakeInc.cmd
+export MakeInc_def=${SRCROOT}/makedefs/MakeInc.def
+export MakeInc_rule=${SRCROOT}/makedefs/MakeInc.rule
+export MakeInc_dir=${SRCROOT}/makedefs/MakeInc.dir
+
+include $(MakeInc_cmd)
+include $(MakeInc_def)
+
+LCLDIR = /usr/local/include
+
+DATAFILES =
+
+KERNELFILES = \
+ ${DATAFILES} \
+ overflow.h
+
+PRIVATE_KERNELFILES =
+
+PRIVATE_DATAFILES = \
+ ${PRIVATE_KERNELFILES} \
+ overflow.h
+
+INSTALL_MI_LIST = ${DATAFILES}
+
+INSTALL_MI_DIR = os
+
+INSTALL_MI_LCL_LIST = \
+ ${PRIVATE_DATAFILES}
+
+INSTALL_KF_MI_LIST = ${KERNELFILES}
+
+INSTALL_KF_MI_LCL_LIST = ${KERNELFILES} ${PRIVATE_KERNELFILES}
+
+EXPORT_MI_LIST = \
+ $(sort ${KERNELFILES} ${PRIVATE_DATAFILES})
+
+EXPORT_MI_GEN_LIST =
+
+EXPORT_MI_DIR = os
+
+include $(MakeInc_rule)
+include $(MakeInc_dir)
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple 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. 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_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Facilities for performing type- and overflow-checked arithmetic. These
+ * functions return non-zero if overflow occured, zero otherwise. In either case,
+ * the potentially overflowing operation is fully performed, mod the size of the
+ * output type. See:
+ * http://clang.llvm.org/docs/LanguageExtensions.html#checked-arithmetic-builtins
+ * for full details.
+ *
+ * The compiler enforces that users of os_*_overflow() check the return value to
+ * determine whether overflow occured.
+ */
+
+#ifndef _OS_OVERFLOW_H
+#define _OS_OVERFLOW_H
+
+#include <sys/cdefs.h>
+
+/* compile-time assertion that 'x' and 'y' are equivalent types */
+#define __OS_TYPE_CHECK(x, y) do { \
+ _Static_assert(__builtin_types_compatible_p(typeof(x),typeof(y)), \
+ "overflow arithmetic: incompatible types"); \
+} while (0)
+
+#define __os_add_overflow_func(T,U,V) _Generic((T), \
+ unsigned: __builtin_uadd_overflow, \
+ unsigned long: __builtin_uaddl_overflow, \
+ unsigned long long: __builtin_uaddll_overflow, \
+ int: __builtin_sadd_overflow, \
+ long: __builtin_saddl_overflow, \
+ long long: __builtin_saddll_overflow \
+ )(T,U,V)
+
+#define __os_sub_overflow_func(T,U,V) _Generic((T), \
+ unsigned: __builtin_usub_overflow, \
+ unsigned long: __builtin_usubl_overflow, \
+ unsigned long long: __builtin_usubll_overflow, \
+ int: __builtin_ssub_overflow, \
+ long: __builtin_ssubl_overflow, \
+ long long: __builtin_ssubll_overflow \
+ )(T,U,V)
+
+#define __os_mul_overflow_func(T,U,V) _Generic((T), \
+ unsigned: __builtin_umul_overflow, \
+ unsigned long: __builtin_umull_overflow, \
+ unsigned long long: __builtin_umulll_overflow, \
+ int: __builtin_smul_overflow, \
+ long: __builtin_smull_overflow, \
+ long long: __builtin_smulll_overflow \
+ )(T,U,V)
+
+int __header_always_inline __attribute__((__warn_unused_result__))
+__os_warn_unused(const int x)
+{
+ return x;
+}
+
+#define os_add_overflow(a, b, res) __os_warn_unused(({ \
+ __OS_TYPE_CHECK((a), (b)); \
+ __OS_TYPE_CHECK((b), *(res)); \
+ __os_add_overflow_func((a), (b), (res)); \
+}))
+
+#define os_add3_overflow(a, b, c, res) __os_warn_unused(({ \
+ typeof(a) _tmp; \
+ int _s, _t; \
+ _s = os_add_overflow((a), (b), &_tmp); \
+ _t = os_add_overflow((c), _tmp, (res)); \
+ _s | _t; \
+}))
+
+#define os_sub_overflow(a, b, res) __os_warn_unused(({ \
+ __OS_TYPE_CHECK((a), (b)); \
+ __OS_TYPE_CHECK((b), *(res)); \
+ __os_sub_overflow_func((a), (b), (res)); \
+}))
+
+#define os_mul_overflow(a, b, res) __os_warn_unused(({ \
+ __OS_TYPE_CHECK((a), (b)); \
+ __OS_TYPE_CHECK((b), *(res)); \
+ __os_mul_overflow_func((a), (b), (res)); \
+}))
+
+#endif /* _OS_OVERFLOW_H */
29A59AE6183B110C00E8B896 /* unlinkat.c in Sources */ = {isa = PBXBuildFile; fileRef = 29A59AE5183B110C00E8B896 /* unlinkat.c */; };
2BA88DCC1810A3CE00EB63F6 /* coalition.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BA88DCB1810A3CE00EB63F6 /* coalition.c */; };
374A36E314748F1300AAF39D /* varargs_wrappers.s in Sources */ = {isa = PBXBuildFile; fileRef = 374A36E214748EE400AAF39D /* varargs_wrappers.s */; };
+ 3F538F891A659C5600B37EFD /* persona.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F538F881A659C5600B37EFD /* persona.c */; };
435F3CAA1B06B7BA005ED9EF /* work_interval.c in Sources */ = {isa = PBXBuildFile; fileRef = 435F3CA91B06B7BA005ED9EF /* work_interval.c */; };
467DAFD4157E8AF200CE68F0 /* guarded_open_np.c in Sources */ = {isa = PBXBuildFile; fileRef = 467DAFD3157E8AF200CE68F0 /* guarded_open_np.c */; };
4BDD5F1D1891AB2F004BF300 /* mach_approximate_time.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */; };
2BA88DCB1810A3CE00EB63F6 /* coalition.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coalition.c; sourceTree = "<group>"; };
374A36E214748EE400AAF39D /* varargs_wrappers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = varargs_wrappers.s; sourceTree = "<group>"; };
37DDFB7614748713009D3355 /* syscall.map */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = syscall.map; sourceTree = "<group>"; };
+ 3F538F881A659C5600B37EFD /* persona.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = persona.c; sourceTree = "<group>"; };
435F3CA91B06B7BA005ED9EF /* work_interval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = work_interval.c; sourceTree = "<group>"; };
467DAFD3157E8AF200CE68F0 /* guarded_open_np.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = guarded_open_np.c; sourceTree = "<group>"; };
4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mach_approximate_time.c; sourceTree = "<group>"; };
4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */,
4BDD5F1C1891AB2F004BF300 /* mach_approximate_time.s */,
030B179A135377B400DAD1F0 /* open_dprotected_np.c */,
+ 3F538F881A659C5600B37EFD /* persona.c */,
C6BEE9171806840200D25AAB /* posix_sem_obsolete.c */,
24B8C2611237F53900D36CC3 /* remove-counter.c */,
248AA966122C7CDA0085F5B1 /* rename.c */,
291D3C291354FDD100D46061 /* mach_vm.c in Sources */,
EE3F605A149A6D66003BAEBA /* getaudit.c in Sources */,
C9B6A5ED153795DE00749EBA /* alloc_once.c in Sources */,
+ 3F538F891A659C5600B37EFD /* persona.c in Sources */,
467DAFD4157E8AF200CE68F0 /* guarded_open_np.c in Sources */,
729B7D0A15C8938C000E2501 /* carbon_delete.c in Sources */,
A59CB9581666A1A200B064B3 /* munmap.c in Sources */,
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
+#include <TargetConditionals.h>
#include <machine/cpu_capabilities.h>
#include <mach/kern_return.h>
#include <mach/mach_host.h>
return KERN_SUCCESS;
}
+kern_return_t
+host_get_multiuser_config_flags(host_t host __unused,
+ uint32_t *multiuser_flags)
+{
+ (void)multiuser_flags;
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t
+host_check_multiuser_mode(host_t host __unused,
+ uint32_t *multiuser_mode)
+{
+ (void)multiuser_mode;
+ return KERN_NOT_SUPPORTED;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_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.
+ *
+ * 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_HEADER_END@
+ */
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <sys/persona.h>
+
+#include "strings.h"
+
+/* syscall entry point */
+int __persona(uint32_t operation, uint32_t flags, struct kpersona_info *info, uid_t *id, size_t *idlen);
+
+int kpersona_alloc(struct kpersona_info *info, uid_t *id)
+{
+ size_t idlen = 1;
+ return __persona(PERSONA_OP_ALLOC, 0, info, id, &idlen);
+}
+
+int kpersona_dealloc(uid_t id)
+{
+ size_t idlen = 1;
+ return __persona(PERSONA_OP_DEALLOC, 0, NULL, &id, &idlen);
+}
+
+int kpersona_get(uid_t *id)
+{
+ /* persona is a process-static identifier: cache it in a global */
+ static uid_t p_id = PERSONA_ID_NONE;
+ if (p_id == PERSONA_ID_NONE) {
+ int ret = 0;
+ size_t idlen = 1;
+ ret = __persona(PERSONA_OP_GET, 0, NULL, &p_id, &idlen);
+ if (ret != 0)
+ return ret;
+ }
+ *id = p_id;
+ return 0;
+}
+
+int kpersona_info(uid_t id, struct kpersona_info *info)
+{
+ size_t idlen = 1;
+ return __persona(PERSONA_OP_INFO, 0, info, &id, &idlen);
+}
+
+int kpersona_pidinfo(pid_t pid, struct kpersona_info *info)
+{
+ size_t idlen = 1;
+ uid_t id = (uid_t)pid;
+ return __persona(PERSONA_OP_PIDINFO, 0, info, &id, &idlen);
+}
+
+int kpersona_find(const char *name, uid_t uid, uid_t *id, size_t *idlen)
+{
+ int ret;
+ struct kpersona_info kinfo;
+ kinfo.persona_info_version = PERSONA_INFO_V1;
+ kinfo.persona_id = uid;
+ kinfo.persona_type = 0;
+ kinfo.persona_gid = 0;
+ kinfo.persona_ngroups = 0;
+ kinfo.persona_groups[0] = 0;
+ kinfo.persona_name[0] = 0;
+ if (name)
+ strlcpy(kinfo.persona_name, name, sizeof(kinfo.persona_name));
+ ret = __persona(PERSONA_OP_FIND, 0, &kinfo, id, idlen);
+ if (ret < 0)
+ return ret;
+ return (int)(*idlen);
+}
/* Default is to inherit parent's coalition(s) */
(*psattrp)->psa_coalition_info = NULL;
- (*psattrp)->reserved = NULL;
+ (*psattrp)->psa_persona_info = NULL;
/*
* old coalition field
*/
static int posix_spawn_destroyportactions_np(posix_spawnattr_t *);
static int posix_spawn_destroycoalition_info_np(posix_spawnattr_t *);
-
+static int posix_spawn_destroypersona_info_np(posix_spawnattr_t *);
int
posix_spawnattr_destroy(posix_spawnattr_t *attr)
psattr = *(_posix_spawnattr_t *)attr;
posix_spawn_destroyportactions_np(attr);
posix_spawn_destroycoalition_info_np(attr);
+ posix_spawn_destroypersona_info_np(attr);
free(psattr);
*attr = NULL;
return 0;
}
+/*
+ * posix_spawn_destroypersona_info_np
+ * Description: clean up persona_info struct in posix_spawnattr_t attr
+ */
+static int
+posix_spawn_destroypersona_info_np(posix_spawnattr_t *attr)
+{
+ _posix_spawnattr_t psattr;
+ struct _posix_spawn_persona_info *persona;
+
+ if (attr == NULL || *attr == NULL)
+ return EINVAL;
+
+ psattr = *(_posix_spawnattr_t *)attr;
+ persona = psattr->psa_persona_info;
+ if (persona == NULL)
+ return EINVAL;
+
+ psattr->psa_persona_info = NULL;
+ free(persona);
+ return 0;
+}
+
/*
* posix_spawn_appendportaction_np
* Description: append a port action, grow the array if necessary
return (0);
}
+
+int
+posix_spawnattr_set_persona_np(const posix_spawnattr_t * __restrict attr, uid_t persona_id, uint32_t flags)
+{
+ _posix_spawnattr_t psattr;
+ struct _posix_spawn_persona_info *persona;
+
+ if (attr == NULL || *attr == NULL)
+ return EINVAL;
+
+ if (flags & ~POSIX_SPAWN_PERSONA_ALL_FLAGS)
+ return EINVAL;
+
+ psattr = *(_posix_spawnattr_t *)attr;
+
+ persona = psattr->psa_persona_info;
+ if (!persona) {
+ persona = (struct _posix_spawn_persona_info *)malloc(sizeof(*persona));
+ if (!persona)
+ return ENOMEM;
+ persona->pspi_uid = 0;
+ persona->pspi_gid = 0;
+ persona->pspi_ngroups = 0;
+ persona->pspi_groups[0] = 0;
+
+ psattr->psa_persona_info = persona;
+ }
+
+ persona->pspi_id = persona_id;
+ persona->pspi_flags = flags;
+
+ return 0;
+}
+
+int
+posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t * __restrict attr, uid_t uid)
+{
+ _posix_spawnattr_t psattr;
+ struct _posix_spawn_persona_info *persona;
+
+ if (attr == NULL || *attr == NULL)
+ return EINVAL;
+
+ psattr = *(_posix_spawnattr_t *)attr;
+ persona = psattr->psa_persona_info;
+ if (!persona)
+ return EINVAL;
+
+ if (!(persona->pspi_flags & (POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE | POSIX_SPAWN_PERSONA_FLAGS_VERIFY)))
+ return EINVAL;
+
+ persona->pspi_uid = uid;
+
+ persona->pspi_flags |= POSIX_SPAWN_PERSONA_UID;
+
+ return 0;
+}
+
+int
+posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t * __restrict attr, gid_t gid)
+{
+ _posix_spawnattr_t psattr;
+ struct _posix_spawn_persona_info *persona;
+
+ if (attr == NULL || *attr == NULL)
+ return EINVAL;
+
+ psattr = *(_posix_spawnattr_t *)attr;
+ persona = psattr->psa_persona_info;
+ if (!persona)
+ return EINVAL;
+
+ if (!(persona->pspi_flags & (POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE | POSIX_SPAWN_PERSONA_FLAGS_VERIFY)))
+ return EINVAL;
+
+ persona->pspi_gid = gid;
+
+ persona->pspi_flags |= POSIX_SPAWN_PERSONA_GID;
+
+ return 0;
+}
+
+int
+posix_spawnattr_set_persona_groups_np(const posix_spawnattr_t * __restrict attr, int ngroups, gid_t *gidarray, uid_t gmuid)
+{
+ _posix_spawnattr_t psattr;
+ struct _posix_spawn_persona_info *persona;
+
+ if (attr == NULL || *attr == NULL)
+ return EINVAL;
+
+ if (gidarray == NULL)
+ return EINVAL;
+
+ if (ngroups > NGROUPS)
+ return EINVAL;
+
+ psattr = *(_posix_spawnattr_t *)attr;
+ persona = psattr->psa_persona_info;
+ if (!persona)
+ return EINVAL;
+
+ if (!(persona->pspi_flags & (POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE | POSIX_SPAWN_PERSONA_FLAGS_VERIFY)))
+ return EINVAL;
+
+ persona->pspi_ngroups = ngroups;
+ for (int i = 0; i < ngroups; i++)
+ persona->pspi_groups[i] = gidarray[i];
+
+ persona->pspi_gmuid = gmuid;
+
+ persona->pspi_flags |= POSIX_SPAWN_PERSONA_GROUPS;
+
+ return 0;
+}
+
+
+
/*
* posix_spawn
*
ad.coal_info_size = sizeof(struct _posix_spawn_coalition_info);
ad.coal_info = psattr->psa_coalition_info;
}
+ if (psattr->psa_persona_info != NULL) {
+ ad.persona_info_size = sizeof(struct _posix_spawn_persona_info);
+ ad.persona_info = psattr->psa_persona_info;
+ }
}
if (file_actions != NULL && *file_actions != NULL) {
_posix_spawn_file_actions_t psactsp =
int posix_spawnattr_set_darwin_role_np(const posix_spawnattr_t * __restrict, uint64_t) __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
int posix_spawnattr_get_darwin_role_np(const posix_spawnattr_t * __restrict, uint64_t * __restrict) __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+int posix_spawnattr_set_persona_np(const posix_spawnattr_t * __restrict, uid_t, uint32_t) __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t * __restrict, uid_t) __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t * __restrict, gid_t) __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+int posix_spawnattr_set_persona_groups_np(const posix_spawnattr_t * __restrict, int, gid_t *, uid_t) __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+
#endif /* !defined _SPAWN_PRIVATE_H_*/
vm \
libsa \
kdp \
+ console \
kperf \
prng
mach \
arm \
arm64
+
INSTINC_SUBDIRS_ARM64 = \
mach \
arm \
mach_voucher_attr_content_t recipe,
mach_voucher_attr_content_size_t recipe_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher);
kern_return_t
.ivam_extract_content = atm_extract_content,
.ivam_command = atm_command,
.ivam_release = atm_release,
+ .ivam_flags = IVAM_FLAGS_NONE,
};
#if DEVELOPMENT || DEBUG
mach_voucher_attr_content_t __unused recipe,
mach_voucher_attr_content_size_t __unused recipe_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher)
{
atm_value_t atm_value = ATM_VALUE_NULL;
/* never an out voucher */
*out_value_voucher = IPC_VOUCHER_NULL;
+ *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
if (disable_atm || (atm_get_diagnostic_config() & ATM_TRACE_DISABLE))
return KERN_NOT_SUPPORTED;
#include <kern/kalloc.h>
#include <kern/ledger.h>
#include <sys/kdebug.h>
-
+#include <IOKit/IOBSD.h>
#include <mach/mach_voucher_attr_control.h>
static zone_t bank_task_zone, bank_account_zone;
#define HANDLE_TO_BANK_ELEMENT(x) (CAST_DOWN(bank_element_t, (x)))
/* Need macro since bank_element_t is 4 byte aligned on release kernel and direct type case gives compilation error */
+#define CAST_TO_BANK_ELEMENT(x) ((bank_element_t)((void *)(x)))
#define CAST_TO_BANK_TASK(x) ((bank_task_t)((void *)(x)))
#define CAST_TO_BANK_ACCOUNT(x) ((bank_account_t)((void *)(x)))
static ledger_template_t bank_ledger_template = NULL;
struct _bank_ledger_indices bank_ledgers = { -1 };
-static bank_task_t bank_task_alloc_init(void);
-static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant);
-static bank_task_t get_bank_task_context(task_t task);
+static bank_task_t bank_task_alloc_init(task_t task);
+static bank_account_t bank_account_alloc_init(bank_task_t bank_holder, bank_task_t bank_merchant,
+ bank_task_t bank_secureoriginator, bank_task_t bank_proximateprocess);
+static bank_task_t get_bank_task_context(task_t task, boolean_t initialize);
static void bank_task_dealloc(bank_task_t bank_task, mach_voucher_attr_value_reference_t sync);
static kern_return_t bank_account_dealloc_with_sync(bank_account_t bank_account, mach_voucher_attr_value_reference_t sync);
static void bank_rollup_chit_to_tasks(ledger_t bill, bank_task_t bank_holder, bank_task_t bank_merchant);
static void init_bank_ledgers(void);
+static boolean_t bank_task_is_propagate_entitled(task_t t);
+
+static lck_spin_t g_bank_task_lock_data; /* lock to protect task->bank_context transition */
+
+#define global_bank_task_lock_init() \
+ lck_spin_init(&g_bank_task_lock_data, &bank_lock_grp, &bank_lock_attr)
+#define global_bank_task_lock_destroy() \
+ lck_spin_destroy(&g_bank_task_lock_data, &bank_lock_grp)
+#define global_bank_task_lock() \
+ lck_spin_lock(&g_bank_task_lock_data)
+#define global_bank_task_lock_try() \
+ lck_spin_try_lock(&g_bank_task_lock_data)
+#define global_bank_task_unlock() \
+ lck_spin_unlock(&g_bank_task_lock_data)
+
+extern uint64_t proc_uniqueid(void *p);
+extern int32_t proc_pid(void *p);
+extern int32_t proc_pidversion(void *p);
+extern uint32_t proc_persona_id(void *p);
+extern uint32_t proc_getuid(void *p);
+extern uint32_t proc_getgid(void *p);
+extern void proc_getexecutableuuid(void *p, unsigned char *uuidbuf, unsigned long size);
+extern int kauth_cred_issuser(void *cred);
+extern void* kauth_cred_get(void);
+
kern_return_t
bank_release_value(
mach_voucher_attr_content_t recipe,
mach_voucher_attr_content_size_t recipe_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher);
kern_return_t
.ivam_extract_content = bank_extract_content,
.ivam_command = bank_command,
.ivam_release = bank_release,
+ .ivam_flags = (IVAM_FLAGS_SUPPORT_SEND_PREPROCESS | IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS),
};
lck_grp_attr_setdefault(&bank_lock_grp_attr);
lck_grp_init(&bank_lock_grp, "bank_lock", &bank_lock_grp_attr);
lck_attr_setdefault(&bank_lock_attr);
+ global_bank_task_lock_init();
#if DEVELOPMENT || DEBUG
/* Initialize global bank development lock group and lock attributes. */
bank_element = HANDLE_TO_BANK_ELEMENT(value);
- if (bank_element == BANK_DEFAULT_VALUE) {
- /* Return success for default value */
+ /* Voucher system should never release the default or persistent value */
+ assert(bank_element != BANK_DEFAULT_VALUE && bank_element != BANK_DEFAULT_TASK_VALUE);
+
+ if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE) {
+ /* Return success for default and default task value */
return KERN_SUCCESS;
}
mach_voucher_attr_content_t __unused recipe,
mach_voucher_attr_content_size_t __unused recipe_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher)
{
bank_task_t bank_task = BANK_TASK_NULL;
bank_task_t bank_holder = BANK_TASK_NULL;
bank_task_t bank_merchant = BANK_TASK_NULL;
+ bank_task_t bank_secureoriginator = BANK_TASK_NULL;
+ bank_task_t bank_proximateprocess = BANK_TASK_NULL;
bank_element_t bank_element = BANK_ELEMENT_NULL;
bank_account_t bank_account = BANK_ACCOUNT_NULL;
bank_account_t old_bank_account = BANK_ACCOUNT_NULL;
/* never an out voucher */
*out_value_voucher = IPC_VOUCHER_NULL;
+ *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
switch (command) {
case MACH_VOUCHER_ATTR_BANK_CREATE:
- /* Get the bank context from the current task and take a reference on it. */
- task = current_task();
- bank_task = get_bank_task_context(task);
- if (bank_task == BANK_TASK_NULL)
- return KERN_RESOURCE_SHORTAGE;
+ /* Return the default task value instead of bank task */
+ *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
+ *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
+ break;
- bank_task_reference(bank_task);
- bank_task_made_reference(bank_task);
+ case MACH_VOUCHER_ATTR_AUTO_REDEEM:
+
+ for (i = 0; i < prev_value_count; i++) {
+ bank_handle = prev_values[i];
+ bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
+
+ /* Should not have received default task value from an IPC */
+ if (bank_element == BANK_DEFAULT_VALUE || bank_element == BANK_DEFAULT_TASK_VALUE)
+ continue;
+
+ task = current_task();
+ if (bank_element->be_type == BANK_TASK) {
+ bank_holder = CAST_TO_BANK_TASK(bank_element);
+ bank_secureoriginator = bank_holder;
+ bank_proximateprocess = bank_holder;
+ } else if (bank_element->be_type == BANK_ACCOUNT) {
+ old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
+ bank_holder = old_bank_account->ba_holder;
+ bank_secureoriginator = old_bank_account->ba_secureoriginator;
+ bank_proximateprocess = old_bank_account->ba_proximateprocess;
+ } else {
+ panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
+ }
- *out_value = BANK_ELEMENT_TO_HANDLE(bank_task);
+ bank_merchant = get_bank_task_context(task, FALSE);
+ if (bank_merchant == BANK_TASK_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ /* Check if trying to redeem for self task, return the bank task */
+ if (bank_holder == bank_merchant &&
+ bank_holder == bank_secureoriginator &&
+ bank_holder == bank_proximateprocess) {
+ bank_task_reference(bank_holder);
+ bank_task_made_reference(bank_holder);
+ *out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
+ return kr;
+ }
+
+ bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
+ bank_secureoriginator, bank_proximateprocess);
+ if (bank_account == BANK_ACCOUNT_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
+ return kr;
+ }
+
+ *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
break;
- case MACH_VOUCHER_ATTR_REDEEM:
+ case MACH_VOUCHER_ATTR_SEND_PREPROCESS:
for (i = 0; i < prev_value_count; i++) {
bank_handle = prev_values[i];
continue;
task = current_task();
+ if (bank_element == BANK_DEFAULT_TASK_VALUE) {
+ bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(task, FALSE));
+ }
+
if (bank_element->be_type == BANK_TASK) {
bank_holder = CAST_TO_BANK_TASK(bank_element);
+ bank_secureoriginator = bank_holder;
} else if (bank_element->be_type == BANK_ACCOUNT) {
old_bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
bank_holder = old_bank_account->ba_holder;
+ bank_secureoriginator = old_bank_account->ba_secureoriginator;
} else {
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
}
- bank_merchant = get_bank_task_context(task);
+ bank_merchant = get_bank_task_context(task, FALSE);
if (bank_merchant == BANK_TASK_NULL)
return KERN_RESOURCE_SHORTAGE;
+ /*
+ * If the process doesn't have secure persona entitlement,
+ * then replace the secure originator to current task.
+ */
+ if (bank_merchant->bt_hasentitlement == 0) {
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+ (BANK_CODE(BANK_ACCOUNT_INFO, (BANK_SECURE_ORIGINATOR_CHANGED))) | DBG_FUNC_NONE,
+ bank_secureoriginator->bt_pid, bank_merchant->bt_pid, 0, 0, 0);
+ bank_secureoriginator = bank_merchant;
+ }
+
+ bank_proximateprocess = bank_merchant;
+
/* Check if trying to redeem for self task, return the bank task */
- if (bank_holder == bank_merchant) {
+ if (bank_holder == bank_merchant &&
+ bank_holder == bank_secureoriginator &&
+ bank_holder == bank_proximateprocess) {
bank_task_reference(bank_holder);
bank_task_made_reference(bank_holder);
*out_value = BANK_ELEMENT_TO_HANDLE(bank_holder);
return kr;
}
-
- bank_account = bank_account_alloc_init(bank_holder, bank_merchant);
+ bank_account = bank_account_alloc_init(bank_holder, bank_merchant,
+ bank_secureoriginator, bank_proximateprocess);
if (bank_account == BANK_ACCOUNT_NULL)
return KERN_RESOURCE_SHORTAGE;
*out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
break;
+
+ case MACH_VOUCHER_ATTR_REDEEM:
+
+ for (i = 0; i < prev_value_count; i++) {
+ bank_handle = prev_values[i];
+ bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
+
+ if (bank_element == BANK_DEFAULT_VALUE)
+ continue;
+
+ task = current_task();
+ if (bank_element == BANK_DEFAULT_TASK_VALUE) {
+ *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_TASK_VALUE);
+ *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST;
+ return kr;
+ }
+ if (bank_element->be_type == BANK_TASK) {
+ bank_task = CAST_TO_BANK_TASK(bank_element);
+ if (bank_task != get_bank_task_context(task, FALSE)) {
+ panic("Found a bank task of another task with bank_context: %p", bank_task);
+ }
+
+ bank_task_reference(bank_task);
+ bank_task_made_reference(bank_task);
+ *out_value = BANK_ELEMENT_TO_HANDLE(bank_task);
+ return kr;
+
+ } else if (bank_element->be_type == BANK_ACCOUNT) {
+ bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
+ bank_merchant = bank_account->ba_merchant;
+ if (bank_merchant != get_bank_task_context(task, FALSE)) {
+ panic("Found another bank task: %p as a bank merchant\n", bank_merchant);
+ }
+
+ bank_account_reference(bank_account);
+ bank_account_made_reference(bank_account);
+ *out_value = BANK_ELEMENT_TO_HANDLE(bank_account);
+ return kr;
+ } else {
+ panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
+ }
+ }
+
+ *out_value = BANK_ELEMENT_TO_HANDLE(BANK_DEFAULT_VALUE);
+ break;
+
default:
kr = KERN_INVALID_ARGUMENT;
break;
if (bank_element == BANK_DEFAULT_VALUE)
continue;
+ if (bank_element == BANK_DEFAULT_TASK_VALUE) {
+ bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
+ }
+
if (MACH_VOUCHER_BANK_CONTENT_SIZE > *in_out_recipe_size) {
*in_out_recipe_size = 0;
return KERN_NO_SPACE;
} else if (bank_element->be_type == BANK_ACCOUNT) {
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
snprintf(buf, MACH_VOUCHER_BANK_CONTENT_SIZE,
- " Bank Account linking holder pid %d with merchant pid %d\n",
+ " Bank Account linking holder pid %d with merchant pid %d, originator PID/persona: %d, %u and proximate PID/persona: %d, %u\n",
bank_account->ba_holder->bt_pid,
- bank_account->ba_merchant->bt_pid);
+ bank_account->ba_merchant->bt_pid,
+ bank_account->ba_secureoriginator->bt_pid,
+ bank_account->ba_secureoriginator->bt_persona_id,
+ bank_account->ba_proximateprocess->bt_pid,
+ bank_account->ba_proximateprocess->bt_persona_id);
} else {
panic("Bogus bank type: %d passed in get_value\n", bank_element->be_type);
}
mach_voucher_attr_content_size_t __unused *out_content_size)
{
bank_task_t bank_task = BANK_TASK_NULL;
+ bank_task_t bank_secureoriginator = BANK_TASK_NULL;
+ bank_task_t bank_proximateprocess = BANK_TASK_NULL;
+ struct persona_token *token = NULL;
bank_element_t bank_element = BANK_ELEMENT_NULL;
bank_account_t bank_account = BANK_ACCOUNT_NULL;
mach_voucher_attr_value_handle_t bank_handle;
if (bank_element == BANK_DEFAULT_VALUE)
continue;
+ if (bank_element == BANK_DEFAULT_TASK_VALUE) {
+ bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
+ }
+
if (bank_element->be_type == BANK_TASK) {
bank_task = CAST_TO_BANK_TASK(bank_element);
} else if (bank_element->be_type == BANK_ACCOUNT) {
return KERN_INVALID_VALUE;
break;
+
+ case BANK_PERSONA_TOKEN:
+
+ if ((sizeof(struct persona_token)) > *out_content_size) {
+ *out_content_size = 0;
+ return KERN_NO_SPACE;
+ }
+ for (i = 0; i < value_count; i++) {
+ bank_handle = values[i];
+ bank_element = HANDLE_TO_BANK_ELEMENT(bank_handle);
+ if (bank_element == BANK_DEFAULT_VALUE)
+ continue;
+
+ if (bank_element == BANK_DEFAULT_TASK_VALUE) {
+ bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
+ }
+
+ if (bank_element->be_type == BANK_TASK) {
+ *out_content_size = 0;
+ return KERN_INVALID_OBJECT;
+ } else if (bank_element->be_type == BANK_ACCOUNT) {
+ bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
+ bank_secureoriginator = bank_account->ba_secureoriginator;
+ bank_proximateprocess = bank_account->ba_proximateprocess;
+ } else {
+ panic("Bogus bank type: %d passed in voucher_command\n", bank_element->be_type);
+ }
+ token = (struct persona_token *)(void *)&out_content[0];
+ memcpy(&token->originator, &bank_secureoriginator->bt_proc_persona, sizeof(struct proc_persona_info));
+ memcpy(&token->proximate, &bank_proximateprocess->bt_proc_persona, sizeof(struct proc_persona_info));
+
+ *out_content_size = (mach_voucher_attr_content_size_t)sizeof(*token);
+ return KERN_SUCCESS;
+ }
+ /* In the case of no value, return error KERN_INVALID_VALUE */
+ *out_content_size = 0;
+ return KERN_INVALID_VALUE;
+
+ break;
+
default:
return KERN_INVALID_ARGUMENT;
}
needs to take 1 extra ref after the task field is initialized.
*/
static bank_task_t
-bank_task_alloc_init(void)
+bank_task_alloc_init(task_t task)
{
bank_task_t new_bank_task;
new_bank_task->bt_type = BANK_TASK;
new_bank_task->bt_refs = 1;
new_bank_task->bt_made = 0;
- new_bank_task->bt_pid = 0;
new_bank_task->bt_creditcard = NULL;
+ new_bank_task->bt_hasentitlement = bank_task_is_propagate_entitled(task);
queue_init(&new_bank_task->bt_accounts_to_pay);
queue_init(&new_bank_task->bt_accounts_to_charge);
lck_mtx_init(&new_bank_task->bt_acc_to_pay_lock, &bank_lock_grp, &bank_lock_attr);
lck_mtx_init(&new_bank_task->bt_acc_to_charge_lock, &bank_lock_grp, &bank_lock_attr);
+ /*
+ * Initialize the persona_id struct
+ */
+ bzero(&new_bank_task->bt_proc_persona, sizeof(new_bank_task->bt_proc_persona));
+ new_bank_task->bt_flags = 0;
+ new_bank_task->bt_unique_pid = proc_uniqueid(task->bsd_info);
+ new_bank_task->bt_pid = proc_pid(task->bsd_info);
+ new_bank_task->bt_pidversion = proc_pidversion(task->bsd_info);
+ new_bank_task->bt_persona_id = proc_persona_id(task->bsd_info);
+ new_bank_task->bt_uid = proc_getuid(task->bsd_info);
+ new_bank_task->bt_gid = proc_getgid(task->bsd_info);
+ proc_getexecutableuuid(task->bsd_info, new_bank_task->bt_macho_uuid, sizeof(new_bank_task->bt_macho_uuid));
+
#if DEVELOPMENT || DEBUG
new_bank_task->bt_task = NULL;
lck_mtx_lock(&bank_tasks_list_lock);
return (new_bank_task);
}
+/*
+ * Routine: proc_is_propagate_entitled
+ * Purpose: Check if the process has persona propagate entitlement.
+ * Returns: TRUE if entitled.
+ * FALSE if not.
+ */
+static boolean_t
+bank_task_is_propagate_entitled(task_t t)
+{
+ /* Return TRUE if root process */
+ if (0 == kauth_cred_issuser(kauth_cred_get())) {
+ /* If it's a non-root process, it needs to have the entitlement for secure originator propagation */
+ boolean_t entitled = FALSE;
+ entitled = IOTaskHasEntitlement(t, ENTITLEMENT_PERSONA_PROPAGATE);
+ return entitled;
+ } else {
+ return TRUE;
+ }
+}
+
/*
* Routine: bank_account_alloc_init
* Purpose: Allocate and Initialize the bank account struct.
static bank_account_t
bank_account_alloc_init(
bank_task_t bank_holder,
- bank_task_t bank_merchant)
+ bank_task_t bank_merchant,
+ bank_task_t bank_secureoriginator,
+ bank_task_t bank_proximateprocess)
{
bank_account_t new_bank_account;
bank_account_t bank_account;
new_bank_account->ba_type = BANK_ACCOUNT;
new_bank_account->ba_refs = 1;
new_bank_account->ba_made = 1;
- new_bank_account->ba_pid = 0;
new_bank_account->ba_bill = new_ledger;
new_bank_account->ba_merchant = bank_merchant;
new_bank_account->ba_holder = bank_holder;
+ new_bank_account->ba_secureoriginator = bank_secureoriginator;
+ new_bank_account->ba_proximateprocess = bank_proximateprocess;
/* Iterate through accounts need to pay list to find the existing entry */
lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
queue_iterate(&bank_holder->bt_accounts_to_pay, bank_account, bank_account_t, ba_next_acc_to_pay) {
- if (bank_account->ba_merchant != bank_merchant)
+ if (bank_account->ba_merchant != bank_merchant ||
+ bank_account->ba_secureoriginator != bank_secureoriginator ||
+ bank_account->ba_proximateprocess != bank_proximateprocess)
continue;
entry_found = TRUE;
bank_task_reference(bank_holder);
bank_task_reference(bank_merchant);
+ bank_task_reference(bank_secureoriginator);
+ bank_task_reference(bank_proximateprocess);
#if DEVELOPMENT || DEBUG
new_bank_account->ba_task = NULL;
* Note: Initialize bank context if NULL.
*/
static bank_task_t
-get_bank_task_context(task_t task)
+get_bank_task_context
+ (task_t task,
+ boolean_t initialize)
{
bank_task_t bank_task;
- if (task->bank_context)
+ if (task->bank_context || !initialize) {
+ assert(task->bank_context != NULL);
return (task->bank_context);
+ }
- bank_task = bank_task_alloc_init();
+ bank_task = bank_task_alloc_init(task);
/* Grab the task lock and check if we won the race. */
task_lock(task);
}
/* We won the race. Take a ref on the ledger and initialize bank task. */
bank_task->bt_creditcard = task->ledger;
- bank_task->bt_pid = task_pid(task);
#if DEVELOPMENT || DEBUG
bank_task->bt_task = task;
#endif
ledger_reference(task->ledger);
+ /* Grab the global bank task lock before setting the bank context on a task */
+ global_bank_task_lock();
task->bank_context = bank_task;
+ global_bank_task_unlock();
+
task_unlock(task);
return (bank_task);
{
bank_task_t bank_holder = bank_account->ba_holder;
bank_task_t bank_merchant = bank_account->ba_merchant;
+ bank_task_t bank_secureoriginator = bank_account->ba_secureoriginator;
+ bank_task_t bank_proximateprocess = bank_account->ba_proximateprocess;
/* Grab the acc to pay list lock and check the sync value */
lck_mtx_lock(&bank_holder->bt_acc_to_pay_lock);
/* Drop the reference of bank holder and merchant */
bank_task_dealloc(bank_holder, 1);
bank_task_dealloc(bank_merchant, 1);
+ bank_task_dealloc(bank_secureoriginator, 1);
+ bank_task_dealloc(bank_proximateprocess, 1);
#if DEVELOPMENT || DEBUG
lck_mtx_lock(&bank_accounts_list_lock);
ledger_amount_t debit;
kern_return_t ret;
+ if (bank_holder == bank_merchant)
+ return;
+
ret = ledger_get_entries(bill, bank_ledgers.cpu_time, &credit, &debit);
if (ret != KERN_SUCCESS) {
return;
* Returns: None.
*/
void
-bank_task_destroy(bank_task_t bank_task)
+bank_task_destroy(task_t task)
{
+ bank_task_t bank_task;
+
+ /* Grab the global bank task lock before dropping the ref on task bank context */
+ global_bank_task_lock();
+ bank_task = task->bank_context;
+ task->bank_context = NULL;
+ global_bank_task_unlock();
+
bank_task_dealloc(bank_task, 1);
}
+/*
+ * Routine: bank_task_initialize
+ * Purpose: Initialize the bank context of a task.
+ * Returns: None.
+ */
+void
+bank_task_initialize(task_t task)
+{
+ get_bank_task_context(task, TRUE);
+}
+
/*
* Routine: init_bank_ledgers
* Purpose: Initialize template for bank ledgers.
bank_ledger_template = t;
}
+/* Routine: bank_billed_time_safe
+ * Purpose: Walk through all the bank accounts billed to me by other tasks and get the current billing balance.
+ * Called from another task. It takes global bank task lock to make sure the bank context is
+ not deallocated while accesing it.
+ * Returns: balance.
+ */
+uint64_t
+bank_billed_time_safe(task_t task)
+{
+ bank_task_t bank_task = BANK_TASK_NULL;
+ ledger_amount_t credit, debit;
+ uint64_t balance = 0;
+ kern_return_t kr;
+
+ /* Task might be in exec, grab the global bank task lock before accessing bank context. */
+ global_bank_task_lock();
+ /* Grab a reference on bank context */
+ if (task->bank_context != NULL) {
+ bank_task = task->bank_context;
+ bank_task_reference(bank_task);
+ }
+ global_bank_task_unlock();
+
+ if (bank_task) {
+ balance = bank_billed_time(bank_task);
+ bank_task_dealloc(bank_task, 1);
+ } else {
+ kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_me,
+ &credit, &debit);
+ if (kr == KERN_SUCCESS) {
+ balance = credit - debit;
+ }
+ }
+
+ return balance;
+}
+
/*
* Routine: bank_billed_time
- * Purpose: Walk throught the Accounts need to pay account list and get the current billing balance.
+ * Purpose: Walk through the Accounts need to pay account list and get the current billing balance.
* Returns: balance.
*/
uint64_t
return (uint64_t)balance;
}
+/* Routine: bank_serviced_time_safe
+ * Purpose: Walk through the bank accounts billed to other tasks by me and get the current balance to be charged.
+ * Called from another task. It takes global bank task lock to make sure the bank context is
+ not deallocated while accesing it.
+ * Returns: balance.
+ */
+uint64_t
+bank_serviced_time_safe(task_t task)
+{
+ bank_task_t bank_task = BANK_TASK_NULL;
+ ledger_amount_t credit, debit;
+ uint64_t balance = 0;
+ kern_return_t kr;
+
+ /* Task might be in exec, grab the global bank task lock before accessing bank context. */
+ global_bank_task_lock();
+ /* Grab a reference on bank context */
+ if (task->bank_context != NULL) {
+ bank_task = task->bank_context;
+ bank_task_reference(bank_task);
+ }
+ global_bank_task_unlock();
+
+ if (bank_task) {
+ balance = bank_serviced_time(bank_task);
+ bank_task_dealloc(bank_task, 1);
+ } else {
+ kr = ledger_get_entries(task->ledger, task_ledgers.cpu_time_billed_to_others,
+ &credit, &debit);
+ if (kr == KERN_SUCCESS) {
+ balance = credit - debit;
+ }
+ }
+
+ return balance;
+}
+
/*
* Routine: bank_serviced_time
- * Purpose: Walk throught the Account need to charge account list and get the current balance to be charged.
+ * Purpose: Walk through the Account need to charge account list and get the current balance to be charged.
* Returns: balance.
*/
uint64_t
mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
mach_voucher_attr_value_handle_array_size_t val_count;
ledger_t bankledger = NULL;
+ bank_task_t bank_merchant;
kern_return_t kr;
val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
if (bank_element == BANK_DEFAULT_VALUE)
return NULL;
+ if (bank_element == BANK_DEFAULT_TASK_VALUE) {
+ bank_element = CAST_TO_BANK_ELEMENT(get_bank_task_context(current_task(), FALSE));
+ }
+
if (bank_element->be_type == BANK_TASK) {
bankledger = NULL;
} else if (bank_element->be_type == BANK_ACCOUNT) {
bank_account = CAST_TO_BANK_ACCOUNT(bank_element);
- bankledger = bank_account->ba_bill;
+ if (bank_account->ba_holder != bank_account->ba_merchant) {
+ /* Return the ledger, if the voucher is redeemed by currrent process. */
+ bank_merchant = get_bank_task_context(current_task(), FALSE);
+ if (bank_account->ba_merchant == bank_merchant) {
+ bankledger = bank_account->ba_bill;
+ }
+ }
} else {
panic("Bogus bank type: %d passed in bank_get_voucher_ledger\n", bank_element->be_type);
}
/* Default value for Voucher Attribute Manager for BANK */
#define BANK_DEFAULT_VALUE NULL
+#define BANK_DEFAULT_TASK_VALUE ((void *) 1)
+
typedef mach_voucher_attr_value_handle_t bank_handle_t;
#define BANK_TASK 0
int be_type; /* Type of element */
int be_refs; /* Ref count */
int be_made; /* Made refs for voucher, Actual ref is also taken for each Made ref */
- int32_t be_pid; /* Customer task's pid. */
#if DEVELOPMENT || DEBUG
task_t be_task; /* Customer task, do not use it since ref is not taken on task */
#endif
#define BANK_ELEMENT_NULL ((bank_element_t) 0)
struct bank_task {
- struct bank_element bt_elem; /* Bank element */
- ledger_t bt_creditcard; /* Ledger of the customer task */
- queue_head_t bt_accounts_to_pay; /* List of accounts worked for me and need to pay */
- queue_head_t bt_accounts_to_charge; /* List of accounts I did work and need to charge */
- decl_lck_mtx_data(, bt_acc_to_pay_lock) /* Lock to protect accounts to pay list */
- decl_lck_mtx_data(, bt_acc_to_charge_lock) /* Lock to protect accounts to charge list */
+ struct bank_element bt_elem; /* Bank element */
+ struct proc_persona_info bt_proc_persona; /* Persona of the process */
+ ledger_t bt_creditcard; /* Ledger of the customer task */
+ queue_head_t bt_accounts_to_pay; /* List of accounts worked for me and need to pay */
+ queue_head_t bt_accounts_to_charge; /* List of accounts I did work and need to charge */
+ decl_lck_mtx_data(, bt_acc_to_pay_lock) /* Lock to protect accounts to pay list */
+ decl_lck_mtx_data(, bt_acc_to_charge_lock) /* Lock to protect accounts to charge list */
+ uint8_t bt_hasentitlement; /* If the secure persona entitlement is set on the task */
#if DEVELOPMENT || DEBUG
- queue_chain_t bt_global_elt; /* Element on the global bank task chain */
+ queue_chain_t bt_global_elt; /* Element on the global bank task chain */
#endif
};
#define bt_type bt_elem.be_type
#define bt_refs bt_elem.be_refs
#define bt_made bt_elem.be_made
-#define bt_pid bt_elem.be_pid
+
+#define bt_flags bt_proc_persona.flags
+#define bt_unique_pid bt_proc_persona.unique_pid
+#define bt_pid bt_proc_persona.pid
+#define bt_pidversion bt_proc_persona.pidversion
+#define bt_persona_id bt_proc_persona.persona_id
+#define bt_uid bt_proc_persona.uid
+#define bt_gid bt_proc_persona.gid
+#define bt_macho_uuid bt_proc_persona.macho_uuid
#if DEVELOPMENT || DEBUG
#define bt_task bt_elem.be_task
ledger_t ba_bill; /* Temporary ledger i.e. chit */
bank_task_t ba_merchant; /* Task who worked for me, who will charge me on behalf of */
bank_task_t ba_holder; /* Credit Card task holder */
+ bank_task_t ba_secureoriginator; /* Bank task of the secure originator */
+ bank_task_t ba_proximateprocess; /* Process who propagated the voucher to us */
queue_chain_t ba_next_acc_to_pay; /* Next account I need to pay to */
queue_chain_t ba_next_acc_to_charge; /* Next account I need to charge to */
#if DEVELOPMENT || DEBUG
#define ba_type ba_elem.be_type
#define ba_refs ba_elem.be_refs
#define ba_made ba_elem.be_made
-#define ba_pid ba_elem.be_pid
#if DEVELOPMENT || DEBUG
#define ba_task ba_elem.be_task
extern struct _bank_ledger_indices bank_ledgers;
extern void bank_init(void);
-extern void bank_task_destroy(bank_task_t);
+extern void bank_task_destroy(task_t);
+extern void bank_task_initialize(task_t task);
+extern uint64_t bank_billed_time_safe(task_t task);
extern uint64_t bank_billed_time(bank_task_t bank_task);
+extern uint64_t bank_serviced_time_safe(task_t task);
extern uint64_t bank_serviced_time(bank_task_t bank_task);
extern ledger_t bank_get_voucher_ledger(ipc_voucher_t voucher);
extern void bank_swap_thread_bank_ledger(thread_t thread, ledger_t ledger);
#define MACH_VOUCHER_BANK_CONTENT_SIZE (500)
typedef uint32_t bank_action_t;
-#define BANK_ORIGINATOR_PID 0x1
+#define BANK_ORIGINATOR_PID 0x1
+#define BANK_PERSONA_TOKEN 0x2
+
+struct proc_persona_info {
+ uint64_t unique_pid;
+ int32_t pid;
+ uint32_t flags;
+ uint32_t pidversion;
+ uint32_t persona_id;
+ uint32_t uid;
+ uint32_t gid;
+ uint8_t macho_uuid[16];
+};
+
+struct persona_token {
+ struct proc_persona_info originator;
+ struct proc_persona_info proximate;
+};
+
+#ifdef PRIVATE
+#define ENTITLEMENT_PERSONA_PROPAGATE "com.apple.private.personas.propagate"
+#endif /* PRIVATE */
#endif /* _BANK_BANK_TYPES_H_ */
DATAFILES =
-EXPORT_ONLY_FILES = \
+PRIVATE_DATAFILES = \
video_console.h
-INSTALL_MI_LIST = ${DATAFILES}
+INSTALL_MI_LCL_LIST = ${PRIVATE_DATAFILES}
-INSTALL_MI_DIR = console
+INSTALL_KF_MI_LCL_LIST = ${PRIVATE_DATAFILES}
-EXPORT_MI_LIST = ${DATAFILES} ${EXPORT_ONLY_FILES}
+EXPORT_MI_LIST = ${PRIVATE_DATAFILES}
+INSTALL_MI_DIR = console
EXPORT_MI_DIR = console
include $(MakeInc_rule)
* non-reentrant.
*/
mp_disable_preemption();
- if (!hw_lock_to(&cnputc_lock, LockTimeOutTSC)) {
+ /* Use the maximum available spinlock timeout. Some configurations
+ * exhibit non-deterministic stalls across console output.
+ */
+ if (!hw_lock_to(&cnputc_lock, UINT32_MAX)) {
/* If we timed out on the lock, and we're in the debugger,
* break the lock.
*/
char ch;
if (!state)
handle_pending_TLB_flushes();
+ ml_set_interrupts_enabled(FALSE);
SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
ch = console_ring_get();
simple_unlock(&console_ring.write_lock);
+ ml_set_interrupts_enabled(state);
if (ch == 0)
break;
_cnputc(ch);
static void * vc_saveunder;
static vm_size_t vc_saveunder_len;
static int8_t vc_uiscale = 1;
-int vc_user_options;
+vc_progress_user_options vc_progress_options;
+vc_progress_user_options vc_user_options;
+
decl_simple_lock_data(,vc_progress_lock)
static int vc_progress_withmeter = 3;
static void
vc_progress_task(__unused void *arg0, __unused void *arg)
{
- spl_t s;
- int x, y, width, height;
+ spl_t s;
+ int x, y, width, height;
+ uint64_t x_pos, y_pos;
const unsigned char * data;
s = splhigh();
simple_lock(&vc_progress_lock);
- if( vc_progress_enable) {
+ if( vc_progress_enable) do {
vc_progress_count++;
if( vc_progress_count >= vc_progress->count) {
width = (vc_progress->width * vc_uiscale);
height = (vc_progress->height * vc_uiscale);
- x = (vc_progress->dx * vc_uiscale);
- y = (vc_progress->dy * vc_uiscale);
- data = vc_progress_data[vc_uiscale - 1];
- if (data)
- {
- data += vc_progress_count * width * height;
+ data = vc_progress_data[vc_uiscale - 1];
+ if (!data) break;
+
+ if (kVCUsePosition & vc_progress_options.options) {
+ /* Rotation: 0:normal, 1:right 90, 2:left 180, 3:left 90 */
+ switch (3 & vinfo.v_rotate) {
+ case 0:
+ x_pos = vc_progress_options.x_pos;
+ y_pos = vc_progress_options.y_pos;
+ break;
+ case 2:
+ x_pos = 0xFFFFFFFF - vc_progress_options.x_pos;
+ y_pos = 0xFFFFFFFF - vc_progress_options.y_pos;
+ break;
+ case 1:
+ x_pos = 0xFFFFFFFF - vc_progress_options.y_pos;
+ y_pos = vc_progress_options.x_pos;
+ break;
+ case 3:
+ x_pos = vc_progress_options.y_pos;
+ y_pos = 0xFFFFFFFF - vc_progress_options.x_pos;
+ break;
+ }
+ x = (uint32_t)((x_pos * (uint64_t) vinfo.v_width) / 0xFFFFFFFFULL);
+ y = (uint32_t)((y_pos * (uint64_t) vinfo.v_height) / 0xFFFFFFFFULL);
+ x -= (width / 2);
+ y -= (height / 2);
+ } else {
+ x = (vc_progress->dx * vc_uiscale);
+ y = (vc_progress->dy * vc_uiscale);
if( 1 & vc_progress->flags) {
x += ((vinfo.v_width - width) / 2);
y += ((vinfo.v_height - height) / 2);
}
-
- assert(((x + width) < (int)vinfo.v_width) &&
- ((y + height) < (int)vinfo.v_height));
-
- vc_blit_rect( x, y, 0,
- width, height, width, width,
- data, vc_saveunder,
- kDataAlpha
- | (vc_progress_angle & kDataRotate)
- | (vc_needsave ? kSave : 0) );
- vc_needsave = FALSE;
-
- clock_deadline_for_periodic_event(vc_progress_interval, mach_absolute_time(), &vc_progress_deadline);
- thread_call_enter_delayed(&vc_progress_call, vc_progress_deadline);
}
+
+ if ((x + width) > (int)vinfo.v_width) break;
+ if ((y + height) > (int)vinfo.v_height) break;
+
+ data += vc_progress_count * width * height;
+
+ vc_blit_rect( x, y, 0,
+ width, height, width, width,
+ data, vc_saveunder,
+ kDataAlpha
+ | (vc_progress_angle & kDataRotate)
+ | (vc_needsave ? kSave : 0) );
+ vc_needsave = FALSE;
+
+ clock_deadline_for_periodic_event(vc_progress_interval, mach_absolute_time(), &vc_progress_deadline);
+ thread_call_enter_delayed(&vc_progress_call, vc_progress_deadline);
}
+ while (FALSE);
simple_unlock(&vc_progress_lock);
splx(s);
}
static boolean_t gc_desire_text = FALSE;
static boolean_t gc_paused_progress;
-static uint64_t lastVideoPhys = 0;
-static vm_offset_t lastVideoVirt = 0;
-static vm_size_t lastVideoSize = 0;
-static boolean_t lastVideoMapped = FALSE;
+static vm_offset_t lastVideoVirt = 0;
+static vm_size_t lastVideoMapSize = 0;
+static boolean_t lastVideoMapKmap = FALSE;
+
static void
gc_pause( boolean_t pause, boolean_t graphics_now )
{
void
initialize_screen(PE_Video * boot_vinfo, unsigned int op)
{
- unsigned int fbsize = 0;
+ unsigned int newMapSize = 0;
vm_offset_t newVideoVirt = 0;
boolean_t graphics_now;
- ppnum_t fbppage;
+ uint32_t delay;
if ( boot_vinfo )
{
struct vc_info new_vinfo = vinfo;
+ boolean_t makeMapping = FALSE;
+
/*
- * First, check if we are changing the size and/or location of the framebuffer
+ * Copy parameters
*/
- new_vinfo.v_name[0] = 0;
- new_vinfo.v_physaddr = boot_vinfo->v_baseAddr & ~3; /* Get the physical address */
-#ifndef __LP64__
- new_vinfo.v_physaddr |= (((uint64_t) boot_vinfo->v_baseAddrHigh) << 32);
-#endif
if (kPEBaseAddressChange != op)
{
new_vinfo.v_width = (unsigned int)boot_vinfo->v_width;
new_vinfo.v_height = (unsigned int)boot_vinfo->v_height;
new_vinfo.v_depth = (unsigned int)boot_vinfo->v_depth;
new_vinfo.v_rowbytes = (unsigned int)boot_vinfo->v_rowBytes;
+ if (kernel_map == VM_MAP_NULL) {
+ // only booter supplies HW rotation
+ new_vinfo.v_rotate = (unsigned int)boot_vinfo->v_rotate;
+ }
#if defined(__i386__) || defined(__x86_64__)
new_vinfo.v_type = (unsigned int)boot_vinfo->v_display;
#else
new_vinfo.v_scale = kPEScaleFactor2x;
else /* Scale factor not set, default to 1x */
new_vinfo.v_scale = kPEScaleFactor1x;
+ }
+ new_vinfo.v_name[0] = 0;
+ new_vinfo.v_physaddr = 0;
+ /*
+ * Check if we are have to map the framebuffer
+ * If VM is up, we are given a virtual address, unless b0 is set to indicate physical.
+ */
+ newVideoVirt = boot_vinfo->v_baseAddr;
+ makeMapping = (kernel_map == VM_MAP_NULL) || (0 != (1 & newVideoVirt));
+ if (makeMapping)
+ {
+ newVideoVirt = 0;
+ new_vinfo.v_physaddr = boot_vinfo->v_baseAddr & ~3UL; /* Get the physical address */
+#ifndef __LP64__
+ new_vinfo.v_physaddr |= (((uint64_t) boot_vinfo->v_baseAddrHigh) << 32);
+#endif
+ kprintf("initialize_screen: b=%08llX, w=%08X, h=%08X, r=%08X, d=%08X\n", /* (BRINGUP) */
+ new_vinfo.v_physaddr, new_vinfo.v_width, new_vinfo.v_height, new_vinfo.v_rowbytes, new_vinfo.v_type); /* (BRINGUP) */
}
- if (!lastVideoMapped)
- kprintf("initialize_screen: b=%08llX, w=%08X, h=%08X, r=%08X, d=%08X\n", /* (BRINGUP) */
- new_vinfo.v_physaddr, new_vinfo.v_width, new_vinfo.v_height, new_vinfo.v_rowbytes, new_vinfo.v_type); /* (BRINGUP) */
-
- if (!new_vinfo.v_physaddr) /* Check to see if we have a framebuffer */
+ if (!newVideoVirt && !new_vinfo.v_physaddr) /* Check to see if we have a framebuffer */
{
kprintf("initialize_screen: No video - forcing serial mode\n"); /* (BRINGUP) */
new_vinfo.v_depth = 0; /* vc routines are nop */
}
else
{
- /*
- * If VM is up, we are given a virtual address, unless b0 is set to indicate physical.
- */
- if ((kernel_map != VM_MAP_NULL) && (0 == (1 & boot_vinfo->v_baseAddr)))
- {
- fbppage = pmap_find_phys(kernel_pmap, (addr64_t)boot_vinfo->v_baseAddr); /* Get the physical address of frame buffer */
- if(!fbppage) /* Did we find it? */
- {
- panic("initialize_screen: Strange framebuffer - addr = %08X\n", (uint32_t)boot_vinfo->v_baseAddr);
- }
- new_vinfo.v_physaddr = (((uint64_t)fbppage) << PAGE_SHIFT) | (boot_vinfo->v_baseAddr & PAGE_MASK); /* Get the physical address */
- }
-
- if (boot_vinfo->v_length != 0)
- fbsize = (unsigned int) round_page(boot_vinfo->v_length);
- else
- fbsize = (unsigned int) round_page(new_vinfo.v_height * new_vinfo.v_rowbytes); /* Remember size */
-
-
- if ((lastVideoPhys != new_vinfo.v_physaddr) || (fbsize > lastVideoSize)) /* Did framebuffer change location or get bigger? */
+ if (makeMapping)
{
unsigned int flags = VM_WIMG_IO;
- newVideoVirt = io_map_spec((vm_map_offset_t)new_vinfo.v_physaddr, fbsize, flags); /* Allocate address space for framebuffer */
- }
- }
-
- if (newVideoVirt != 0)
+ if (boot_vinfo->v_length != 0)
+ newMapSize = (unsigned int) round_page(boot_vinfo->v_length);
+ else
+ newMapSize = (unsigned int) round_page(new_vinfo.v_height * new_vinfo.v_rowbytes); /* Remember size */
+ newVideoVirt = io_map_spec((vm_map_offset_t)new_vinfo.v_physaddr, newMapSize, flags); /* Allocate address space for framebuffer */
+ }
new_vinfo.v_baseaddr = newVideoVirt + boot_vinfo->v_offset; /* Set the new framebuffer address */
- else
- new_vinfo.v_baseaddr = lastVideoVirt + boot_vinfo->v_offset; /* Set the new framebuffer address */
+ }
#if defined(__x86_64__)
// Adjust the video buffer pointer to point to where it is in high virtual (above the hole)
// If we changed the virtual address, remove the old mapping
if (newVideoVirt != 0)
{
- if (lastVideoVirt) /* Was the framebuffer mapped before? */
+ if (lastVideoVirt && lastVideoMapSize) /* Was the framebuffer mapped before? */
{
- /* XXX why did this ever succeed? */
- /* TODO: Consider this. */
- if (!TEST_PAGE_SIZE_4K && lastVideoMapped) /* Was this not a special pre-VM mapping? */
+ /* XXX why only !4K? */
+ if (!TEST_PAGE_SIZE_4K && lastVideoMapSize)
{
pmap_remove(kernel_pmap, trunc_page_64(lastVideoVirt),
- round_page_64(lastVideoVirt + lastVideoSize)); /* Toss mappings */
+ round_page_64(lastVideoVirt + lastVideoMapSize)); /* Toss mappings */
}
- if(lastVideoMapped) /* Was this not a special pre-VM mapping? */
+ /* Was this not a special pre-VM mapping? */
+ if (lastVideoMapKmap)
{
- kmem_free(kernel_map, lastVideoVirt, lastVideoSize); /* Toss kernel addresses */
+ kmem_free(kernel_map, lastVideoVirt, lastVideoMapSize); /* Toss kernel addresses */
}
}
- lastVideoPhys = new_vinfo.v_physaddr; /* Remember the framebuffer address */
- lastVideoSize = fbsize; /* Remember the size */
- lastVideoVirt = newVideoVirt; /* Remember the virtual framebuffer address */
- lastVideoMapped = (NULL != kernel_map);
+ lastVideoMapKmap = (NULL != kernel_map); /* Remember how mapped */
+ lastVideoMapSize = newMapSize; /* Remember the size */
+ lastVideoVirt = newVideoVirt; /* Remember the virtual framebuffer address */
}
if (kPEBaseAddressChange != op)
case kPEAcquireScreen:
if ( gc_acquired ) break;
- vc_progress_set( graphics_now, (kVCDarkReboot & vc_user_options) ? 120 : vc_acquire_delay );
+
+ vc_progress_options = vc_user_options;
+ bzero(&vc_user_options, sizeof(vc_user_options));
+
+ if (kVCAcquireImmediate & vc_progress_options.options) delay = 0;
+ else if (kVCDarkReboot & vc_progress_options.options) delay = 120;
+ else delay = vc_acquire_delay;
+
+ if (kVCDarkBackground & vc_progress_options.options) vc_progress_white = TRUE;
+ else if (kVCLightBackground & vc_progress_options.options) vc_progress_white = FALSE;
+
+ vc_progress_set( graphics_now, delay );
gc_enable( !graphics_now );
gc_acquired = TRUE;
gc_desire_text = FALSE;
vc_progress_set( FALSE, 0 );
vc_acquire_delay = kProgressReacquireDelay;
- vc_enable_progressmeter(FALSE);
vc_progress_white = TRUE;
+ vc_enable_progressmeter(FALSE);
vc_progress_withmeter &= ~1;
vc_clut8 = NULL;
break;
}
-void
-vc_set_options(int new_value)
-{
- vc_user_options = new_value;
-}
extern "C" {
#endif
+#define kVCSysctlProgressOptions "kern.progressoptions"
+#define kVCSysctlConsoleOptions "kern.consoleoptions"
+#define kVCSysctlProgressMeterEnable "kern.progressmeterenable"
+#define kVCSysctlProgressMeter "kern.progressmeter"
+
+enum
+{
+ kVCDarkReboot = 0x00000001,
+ kVCAcquireImmediate = 0x00000002,
+ kVCUsePosition = 0x00000004,
+ kVCDarkBackground = 0x00000008,
+ kVCLightBackground = 0x00000010,
+};
+
+struct vc_progress_user_options {
+ uint32_t options;
+ // fractional position of middle of spinner 0 (0.0) - 0xFFFFFFFF (1.0)
+ uint32_t x_pos;
+ uint32_t y_pos;
+ uint32_t resv[8];
+};
+typedef struct vc_progress_user_options vc_progress_user_options;
+
+
+#if XNU_KERNEL_PRIVATE
+
void vcputc(int, int, int);
int vcgetc( int l,
unsigned int v_columns; /* characters */
unsigned int v_rowscanbytes; /* Actualy number of bytes used for display per row*/
unsigned int v_scale;
- unsigned int v_reserved[4];
+ unsigned int v_rotate;
+ unsigned int v_reserved[3];
};
struct vc_progress_element {
};
typedef struct vc_progress_element vc_progress_element;
+extern struct vc_progress_user_options vc_user_options;
+
void vc_progress_initialize( vc_progress_element * desc,
const unsigned char * data1x,
const unsigned char * data2x,
extern void vc_progress_setdiskspeed(uint32_t speed);
-
-extern int vc_user_options;
-
-enum
-{
- kVCDarkReboot = 0x00000001,
-};
-extern void vc_set_options(int new_value);
+#endif /* XNU_KERNEL_PRIVATE */
#ifdef __cplusplus
}
#if KERNEL_SERVER
intran: io_connect_t iokit_lookup_connect_port(mach_port_t)
outtran: mach_port_t iokit_make_connect_port(io_connect_t)
- destructor: iokit_remove_reference(io_connect_t)
+ destructor: iokit_remove_connect_reference(io_connect_t)
#endif /* KERNEL_SERVER */
;
typedef io_object_t io_connect_t;
extern void iokit_remove_reference( io_object_t obj );
+extern void iokit_remove_connect_reference( io_object_t obj );
extern io_object_t iokit_lookup_object_port( ipc_port_t port );
extern io_connect_t iokit_lookup_connect_port( ipc_port_t port );
*/
extern void iokit_add_reference( io_object_t obj );
+extern void iokit_add_connect_reference( io_object_t obj );
extern ipc_port_t iokit_port_for_object( io_object_t obj,
ipc_kobject_type_t type );
iokit_lock_port(port);
if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_CONNECT)) {
obj = (io_object_t) port->ip_kobject;
- iokit_add_reference( obj );
+ iokit_add_connect_reference( obj );
}
else
obj = NULL;
iokit_lock_port(port);
if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_CONNECT)) {
obj = (io_object_t) port->ip_kobject;
- iokit_add_reference(obj);
+ iokit_add_connect_reference(obj);
}
iokit_unlock_port(port);
}
}
+
+#if CONFIG_VMX
+ vmx_suspend();
+#endif
kdebug_enable = 0;
IOCPURunPlatformQuiesceActions();
hv_suspend();
#endif
-#if CONFIG_VMX
- /*
- * Turn off VT, otherwise switching to legacy mode will fail
- */
- vmx_suspend();
-#endif
-
/*
* Enable FPU/SIMD unit for potential hibernate acceleration
*/
#if HIBERNATION
acpi_sleep_cpu(acpi_hibernate, &data);
#else
+#if CONFIG_VMX
+ vmx_suspend();
+#endif
acpi_sleep_cpu(func, refcon);
#endif
/* update CPU microcode */
ucode_update_wake();
+#if CONFIG_MTRR
+ /* set up PAT following boot processor power up */
+ pat_init();
+#endif
+
#if CONFIG_VMX
/*
* Restore VT mode
*/
- vmx_resume();
-#endif
-
-#if CONFIG_MTRR
- /* set up PAT following boot processor power up */
- pat_init();
+ vmx_resume(did_hibernate);
#endif
/*
#if CONFIG_VMX
/* resume VT operation */
- vmx_resume();
+ vmx_resume(FALSE);
#endif
#if CONFIG_MTRR
/* spin on entry rendezvous */
atomic_incl(&mp_rv_entry, 1);
tsc_spin_start = rdtsc64();
+
while (mp_rv_entry < mp_rv_ncpus) {
/* poll for pesky tlb flushes if interrupts disabled */
if (!intrs_enabled)
handle_pending_TLB_flushes();
- if (mp_spin_timeout(tsc_spin_start))
- panic("mp_rendezvous_action() entry");
+ if (mp_spin_timeout(tsc_spin_start)) {
+ panic("mp_rv_action() entry: %ld of %d responses, start: 0x%llx, cur: 0x%llx", mp_rv_entry, mp_rv_ncpus, tsc_spin_start, rdtsc64());
+ }
}
/* action function */
if (!intrs_enabled)
handle_pending_TLB_flushes();
if (mp_spin_timeout(tsc_spin_start))
- panic("mp_rendezvous_action() exit");
+ panic("mp_rv_action() exit: %ld of %d responses, start: 0x%llx, cur: 0x%llx", mp_rv_exit, mp_rv_ncpus, tsc_spin_start, rdtsc64());
}
/* teardown function */
tsc_spin_start = rdtsc64();
while (mp_rv_complete < mp_rv_ncpus) {
if (mp_spin_timeout(tsc_spin_start))
- panic("mp_rendezvous() timeout");
+ panic("mp_rendezvous() timeout: %ld of %d responses, start: 0x%llx, cur: 0x%llx", mp_rv_complete, mp_rv_ncpus, tsc_spin_start, rdtsc64());
}
/* Tidy up */
#endif /* MACH_KDP */
boolean_t
-mp_recent_debugger_activity() {
+mp_recent_debugger_activity(void) {
uint64_t abstime = mach_absolute_time();
return (((abstime - debugger_entry_time) < LastDebuggerEntryAllowance) ||
((abstime - debugger_exit_time) < LastDebuggerEntryAllowance));
vmx_enable();
+ VMX_KPRINTF("[%d]vmx_cpu_init() initialized: %d\n",
+ cpu_number(), specs->initialized);
+
/* if we have read the data on boot, we won't read it again on wakeup */
if (specs->initialized)
return;
/* See if VMX is present, return if it is not */
specs->vmx_present = vmx_is_available() && vmxon_is_enabled();
+ VMX_KPRINTF("[%d]vmx_cpu_init() vmx_present: %d\n",
+ cpu_number(), specs->vmx_present);
if (!specs->vmx_present)
return;
addr64_t vmxon_region_paddr;
int result;
+ VMX_KPRINTF("[%d]vmx_on() entry state: %d\n",
+ cpu_number(), cpu->specs.vmx_on);
+
assert(cpu->specs.vmx_present);
if (NULL == cpu->vmxon_region)
cpu->specs.vmx_on = TRUE;
}
+ VMX_KPRINTF("[%d]vmx_on() return state: %d\n",
+ cpu_number(), cpu->specs.vmx_on);
}
/* -----------------------------------------------------------------------------
vmx_cpu_t *cpu = ¤t_cpu_datap()->cpu_vmx;
int result;
+ VMX_KPRINTF("[%d]vmx_off() entry state: %d\n",
+ cpu_number(), cpu->specs.vmx_on);
+
if (TRUE == cpu->specs.vmx_on) {
/* Tell the CPU to release the VMXON region */
result = __vmxoff();
cpu->specs.vmx_on = FALSE;
}
+
+ VMX_KPRINTF("[%d]vmx_off() return state: %d\n",
+ cpu_number(), cpu->specs.vmx_on);
}
/* -----------------------------------------------------------------------------
Restore the previous VT state. Called when CPU comes back online.
-------------------------------------------------------------------------- */
void
-vmx_resume()
+vmx_resume(boolean_t is_wake_from_hibernate)
{
VMX_KPRINTF("vmx_resume\n");
vmx_enable();
- if (vmx_use_count)
- vmx_on(NULL);
+ if (vmx_use_count == 0)
+ return;
+
+ /*
+ * When resuming from hiberate on the boot cpu,
+ * we must mark VMX as off since that's the state at wake-up
+ * because the restored state in memory records otherwise.
+ * This results in vmx_on() doing the right thing.
+ */
+ if (is_wake_from_hibernate) {
+ vmx_cpu_t *cpu = ¤t_cpu_datap()->cpu_vmx;
+ cpu->specs.vmx_on = FALSE;
+ }
+
+ vmx_on(NULL);
}
/* -----------------------------------------------------------------------------
void vmx_init(void);
void vmx_cpu_init(void);
-void vmx_resume(void);
+void vmx_resume(boolean_t is_wake_from_hibernate);
void vmx_suspend(void);
#define VMX_BASIC_TRUE_CTLS (1ull << 55)
mach_voucher_attr_content_t content,
mach_voucher_attr_content_size_t content_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher);
static kern_return_t
.ivam_extract_content = ipc_importance_extract_content,
.ivam_command = ipc_importance_command,
.ivam_release = ipc_importance_manager_release,
+ .ivam_flags = IVAM_FLAGS_NONE,
};
#define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
mach_voucher_attr_content_t __unused content,
mach_voucher_attr_content_size_t content_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher)
{
ipc_importance_elem_t elem;
if (0 != content_size)
return KERN_INVALID_ARGUMENT;
+ *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
/* never an out voucher */
switch (command) {
return MACH_MSG_SUCCESS;
}
+ ipc_voucher_send_preprocessing(kmsg);
+
port = (ipc_port_t) kmsg->ikm_header->msgh_remote_port;
assert(IP_VALID(port));
ip_lock(port);
ipc_voucher_attr_manager_t *,
ipc_voucher_attr_control_t *);
+static kern_return_t
+ipc_voucher_prepare_processing_recipe(
+ ipc_voucher_t voucher,
+ ipc_voucher_attr_raw_recipe_array_t recipes,
+ ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
+ mach_voucher_attr_recipe_command_t command,
+ ipc_voucher_attr_manager_flags flags,
+ int *need_processing);
#if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
void user_data_attr_manager_init(void);
#define ivace_reset_data(ivace_elem, next_index) { \
(ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
(ivace_elem)->ivace_refs = 0; \
+ (ivace_elem)->ivace_persist = 0; \
(ivace_elem)->ivace_made = 0; \
(ivace_elem)->ivace_free = TRUE; \
(ivace_elem)->ivace_releasing = FALSE; \
#define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
(ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
(ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
+ (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
(ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
(ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
(ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
assert(0xdeadc0dedeadc0de != ivace->ivace_value);
assert(0 < ivace->ivace_refs);
assert(!ivace->ivace_free);
- ivace->ivace_refs++;
+
+ /* Take ref only on non-persistent values */
+ if (!ivace->ivace_persist) {
+ ivace->ivace_refs++;
+ }
ivac_unlock(ivac);
}
static iv_index_t
ivace_reference_by_value(
ipc_voucher_attr_control_t ivac,
- mach_voucher_attr_value_handle_t value)
+ mach_voucher_attr_value_handle_t value,
+ mach_voucher_attr_value_flags_t flag)
{
ivac_entry_t ivace = IVACE_NULL;
iv_index_t hash_index;
/* found it? */
if (index != IV_HASH_END) {
- /* only add reference on non-default value */
- if (IV_UNUSED_VALINDEX != index) {
+ /* only add reference on non-persistent value */
+ if (!ivace->ivace_persist) {
ivace->ivace_refs++;
ivace->ivace_made++;
}
ivace->ivace_refs = 1;
ivace->ivace_made = 1;
ivace->ivace_free = FALSE;
+ ivace->ivace_persist = (flag & MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST) ? TRUE : FALSE;
/* insert the new entry in the proper hash chain */
ivace->ivace_next = ivac->ivac_table[hash_index].ivace_index;
assert(0 < ivace->ivace_refs);
+ /* cant release persistent values */
+ if (ivace->ivace_persist) {
+ ivac_unlock(ivac);
+ return;
+ }
+
if (0 < --ivace->ivace_refs) {
ivac_unlock(ivac);
return;
mach_voucher_attr_value_handle_t previous_vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
mach_voucher_attr_value_handle_array_size_t previous_vals_count;
mach_voucher_attr_value_handle_t new_value;
+ mach_voucher_attr_value_flags_t new_flag;
ipc_voucher_t new_value_voucher;
ipc_voucher_attr_manager_t ivam;
ipc_voucher_attr_control_t ivac;
ivam, key, command,
previous_vals, previous_vals_count,
content, content_size,
- &new_value, &new_value_voucher);
+ &new_value, &new_flag, &new_value_voucher);
if (KERN_SUCCESS != kr) {
ivac_release(ivac);
return kr;
* is transferred to a new value, or consumed if
* we find a matching existing value.
*/
- val_index = ivace_reference_by_value(ivac, new_value);
+ val_index = ivace_reference_by_value(ivac, new_value, new_flag);
iv_set(voucher, key_index, val_index);
/*
* is transferred to a new value, or consumed if
* we find a matching existing value.
*/
- val_index = ivace_reference_by_value(ivac, new_value);
+ val_index = ivace_reference_by_value(ivac, new_value,
+ MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE);
iv_set(voucher, key_index, val_index);
/*
new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_value = default_value;
new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_refs = IVACE_REFS_MAX;
new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_made = IVACE_REFS_MAX;
+ new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_persist = TRUE;
assert(IV_HASH_END == new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_next);
ivgt_lock();
return KERN_SUCCESS;
}
-
/*
* Routine: mach_voucher_attr_control_create_mach_voucher
* Purpose:
return KERN_NOT_SUPPORTED;
}
+/*
+ * Routine: ipc_voucher_send_preprocessing
+ * Purpose:
+ * Processing of the voucher in the kmsg before sending it.
+ * Currently use to switch PERSONA_TOKEN in case of process with
+ * no com.apple.private.personas.propagate entitlement.
+ */
+void
+ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg)
+{
+ uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
+ sizeof(ipc_voucher_attr_recipe_data_t);
+ ipc_voucher_t pre_processed_voucher;
+ ipc_voucher_t voucher_to_send;
+ kern_return_t kr;
+ int need_preprocessing = FALSE;
+
+ if (!IP_VALID(kmsg->ikm_voucher) || current_task() == kernel_task) {
+ return;
+ }
+
+ /* setup recipe for preprocessing of all the attributes. */
+ pre_processed_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+
+ kr = ipc_voucher_prepare_processing_recipe(pre_processed_voucher,
+ (mach_voucher_attr_raw_recipe_array_t)recipes,
+ &recipe_size, MACH_VOUCHER_ATTR_SEND_PREPROCESS,
+ IVAM_FLAGS_SUPPORT_SEND_PREPROCESS, &need_preprocessing);
+
+ assert(KERN_SUCCESS == kr);
+ /*
+ * Only do send preprocessing if the voucher needs any pre processing.
+ */
+ if (need_preprocessing) {
+ kr = ipc_create_mach_voucher(recipes,
+ recipe_size,
+ &voucher_to_send);
+ assert(KERN_SUCCESS == kr);
+ ipc_port_release_send(kmsg->ikm_voucher);
+ kmsg->ikm_voucher = convert_voucher_to_port(voucher_to_send);
+ }
+}
+
+/*
+ * Routine: ipc_voucher_receive_postprocessing
+ * Purpose:
+ * Redeems the voucher attached to the kmsg.
+ * Note:
+ * Although it is possible to call ipc_importance_receive
+ * here, it is called in mach_msg_receive_results and not here
+ * in order to maintain symmetry with ipc_voucher_send_preprocessing.
+ */
+void
+ipc_voucher_receive_postprocessing(
+ ipc_kmsg_t kmsg,
+ mach_msg_option_t option)
+{
+ uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
+ sizeof(ipc_voucher_attr_recipe_data_t);
+ ipc_voucher_t recv_voucher;
+ ipc_voucher_t sent_voucher;
+ kern_return_t kr;
+ int need_postprocessing = FALSE;
+
+ if ((option & MACH_RCV_VOUCHER) == 0 || (!IP_VALID(kmsg->ikm_voucher)) ||
+ current_task() == kernel_task) {
+ return;
+ }
+
+ /* setup recipe for auto redeem of all the attributes. */
+ sent_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+
+ kr = ipc_voucher_prepare_processing_recipe(sent_voucher,
+ (mach_voucher_attr_raw_recipe_array_t)recipes,
+ &recipe_size, MACH_VOUCHER_ATTR_AUTO_REDEEM,
+ IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS, &need_postprocessing);
+
+ assert(KERN_SUCCESS == kr);
+
+ /*
+ * Only do receive postprocessing if the voucher needs any post processing.
+ */
+ if (need_postprocessing) {
+ kr = ipc_create_mach_voucher(recipes,
+ recipe_size,
+ &recv_voucher);
+ assert(KERN_SUCCESS == kr);
+ /* swap the voucher port (and set voucher bits in case it didn't already exist) */
+ kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
+ ipc_port_release_send(kmsg->ikm_voucher);
+ kmsg->ikm_voucher = convert_voucher_to_port(recv_voucher);
+ }
+}
+
+/*
+ * Routine: ipc_voucher_prepare_processing_recipe
+ * Purpose:
+ * Check if the given voucher has an attribute which supports
+ * the given flag and prepare a recipe to apply that supported
+ * command.
+ */
+static kern_return_t
+ipc_voucher_prepare_processing_recipe(
+ ipc_voucher_t voucher,
+ ipc_voucher_attr_raw_recipe_array_t recipes,
+ ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
+ mach_voucher_attr_recipe_command_t command,
+ ipc_voucher_attr_manager_flags flags,
+ int *need_processing)
+{
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_size = *in_out_size;
+ ipc_voucher_attr_raw_recipe_array_size_t recipe_used = 0;
+ iv_index_t key_index;
+ ipc_voucher_attr_recipe_t recipe;
+
+ if (IV_NULL == voucher)
+ return KERN_INVALID_ARGUMENT;
+
+ /* Setup a recipe to copy all attributes. */
+ if (recipe_size < sizeof(*recipe))
+ return KERN_NO_SPACE;
+
+ *need_processing = FALSE;
+ recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
+ recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
+ recipe->command = MACH_VOUCHER_ATTR_COPY;
+ recipe->previous_voucher = voucher;
+ recipe->content_size = 0;
+ recipe_used += sizeof(*recipe) + recipe->content_size;
+
+ for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
+ ipc_voucher_attr_manager_t manager;
+ mach_voucher_attr_key_t key;
+ iv_index_t value_index;
+
+ /* don't output anything for a default value */
+ value_index = iv_lookup(voucher, key_index);
+ if (IV_UNUSED_VALINDEX == value_index)
+ continue;
+
+ if (recipe_size - recipe_used < sizeof(*recipe))
+ return KERN_NO_SPACE;
+
+ recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
+
+ /*
+ * Get the manager for this key_index. The
+ * existence of a non-default value for this
+ * slot within our voucher will keep the
+ * manager referenced during the callout.
+ */
+ ivgt_lookup(key_index, FALSE, &manager, NULL);
+ assert(IVAM_NULL != manager);
+ if (IVAM_NULL == manager) {
+ continue;
+ }
+
+ /* Check if the supported flag is set in the manager */
+ if ((manager->ivam_flags & flags) == 0)
+ continue;
+
+ key = iv_index_to_key(key_index);
+
+ recipe->key = key;
+ recipe->command = command;
+ recipe->content_size = 0;
+ recipe->previous_voucher = voucher;
+
+ recipe_used += sizeof(*recipe) + recipe->content_size;
+ *need_processing = TRUE;
+ }
+
+ *in_out_size = recipe_used;
+ return KERN_SUCCESS;
+}
#if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
mach_voucher_attr_content_t content,
mach_voucher_attr_content_size_t content_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher);
static kern_return_t
.ivam_extract_content = user_data_extract_content,
.ivam_command = user_data_command,
.ivam_release = user_data_release,
+ .ivam_flags = IVAM_FLAGS_NONE,
};
ipc_voucher_attr_control_t user_data_control;
mach_voucher_attr_content_t content,
mach_voucher_attr_content_size_t content_size,
mach_voucher_attr_value_handle_t *out_value,
+ mach_voucher_attr_value_flags_t *out_flags,
ipc_voucher_t *out_value_voucher)
{
user_data_element_t elem;
/* never an out voucher */
*out_value_voucher = IPC_VOUCHER_NULL;
+ *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
switch (command) {
iv_value_refs_t ivace_layered:1, /* layered effective entry */
ivace_releasing:1, /* release in progress */
ivace_free:1, /* on freelist */
- ivace_refs:29; /* reference count */
+ ivace_persist:1, /* Persist the entry, don't count made refs */
+ ivace_refs:28; /* reference count */
union {
iv_value_refs_t ivaceu_made; /* made count (non-layered) */
iv_index_t ivaceu_layer; /* next effective layer (layered) */
#define IVACE_NULL ((ivac_entry_t) 0);
-#define IVACE_REFS_MAX ((1 << 29) - 1)
+#define IVACE_REFS_MAX ((1 << 28) - 1)
#define IVAC_ENTRIES_MIN 512
#define IVAC_ENTRIES_MAX 524288
#define IVAC_NULL IPC_VOUCHER_ATTR_CONTROL_NULL
extern ipc_voucher_attr_control_t ivac_alloc(iv_index_t);
+extern void ipc_voucher_receive_postprocessing(ipc_kmsg_t kmsg, mach_msg_option_t option);
+extern void ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg);
#define ivac_lock_init(ivac) \
lck_spin_init(&(ivac)->ivac_lock_data, &ipc_lck_grp, &ipc_lck_attr)
mach_voucher_attr_content_t,
mach_voucher_attr_content_size_t,
mach_voucher_attr_value_handle_t *,
+ mach_voucher_attr_value_flags_t *,
ipc_voucher_t *);
typedef kern_return_t (*ipc_voucher_attr_manager_extract_content_t)(ipc_voucher_attr_manager_t,
typedef void (*ipc_voucher_attr_manager_release_t)(ipc_voucher_attr_manager_t);
+typedef uint32_t ipc_voucher_attr_manager_flags;
+
struct ipc_voucher_attr_manager {
ipc_voucher_attr_manager_release_value_t ivam_release_value;
ipc_voucher_attr_manager_get_value_t ivam_get_value;
ipc_voucher_attr_manager_extract_content_t ivam_extract_content;
ipc_voucher_attr_manager_command_t ivam_command;
ipc_voucher_attr_manager_release_t ivam_release;
+ ipc_voucher_attr_manager_flags ivam_flags;
};
+#define IVAM_FLAGS_NONE 0
+#define IVAM_FLAGS_SUPPORT_SEND_PREPROCESS 0x1
+#define IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS 0x2
+
__BEGIN_DECLS
/* DEBUG/TRACE Convert from a port to a voucher */
#include <ipc/ipc_space.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_importance.h>
+#include <ipc/ipc_voucher.h>
#include <machine/machine_routines.h>
#include <security/mac_mach_internal.h>
#endif /* IMPORTANCE_INHERITANCE */
+ /* auto redeem the voucher in the message */
+ ipc_voucher_receive_postprocessing(kmsg, option);
+
trailer_size = ipc_kmsg_add_trailer(kmsg, space, option, self, seqno, FALSE,
kmsg->ikm_header->msgh_remote_port->ip_context);
mr = ipc_kmsg_copyout(kmsg, space, map, MACH_MSG_BODY_NULL, option);
{
kern_return_t kr;
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
is_write_lock(space);
if (!is_active(space)) {
#if CONFIG_NO_PANIC_STRINGS
#define Assert(file, line, ex) (Assert)("", line, "")
-#endif
+#define __Panic(fmt, args...) panic("", ##args)
+#else /* CONFIG_NO_PANIC_STRINGS */
+#define __Panic(fmt, args...) panic(fmt, ##args)
+#endif /* CONFIG_NO_PANIC_STRINGS */
__END_DECLS
#define assert(ex) \
(__builtin_expect(!!((long)(ex)), 1L) ? (void)0 : Assert(__FILE__, __LINE__, # ex))
#define assert_static(ex) _Static_assert((ex), #ex)
+#define assertf(ex, fmt, args...) \
+ (__builtin_expect(!!((long)(ex)), 1L) ? (void)0 : __Panic("%s:%d Assertion failed: %s : " fmt, __FILE__, __LINE__, # ex, ##args))
#define __assert_only
#define assert(ex) ((void)0)
#define assert_static(ex) _Static_assert((ex), #ex)
+#define assertf(ex, fmt, args...) ((void)0)
#define __assert_only __unused
logical_deferred_writes += task->task_deferred_writes;
logical_invalidated_writes += task->task_invalidated_writes;
logical_metadata_writes += task->task_metadata_writes;
- cpu_time_billed_to_me += (int64_t)bank_billed_time(task->bank_context);
- cpu_time_billed_to_others += (int64_t)bank_serviced_time(task->bank_context);
+ cpu_time_billed_to_me += (int64_t)bank_billed_time_safe(task);
+ cpu_time_billed_to_others += (int64_t)bank_serviced_time_safe(task);
}
/* collect information from the coalition itself */
#include <mach/vm_map.h>
#include <mach/task_info.h>
+#include <machine/commpage.h>
+#include <machine/cpu_capabilities.h>
+
#include <kern/kern_types.h>
#include <kern/assert.h>
#include <kern/kalloc.h>
return (KERN_NOT_SUPPORTED);
#endif
}
+
+kern_return_t
+host_set_multiuser_config_flags(host_priv_t host_priv, uint32_t multiuser_config)
+{
+ (void)host_priv;
+ (void)multiuser_config;
+ return (KERN_NOT_SUPPORTED);
+}
return mr;
}
- mr = ipc_kmsg_send(kmsg,
- MACH_SEND_KERNEL_DEFAULT,
- MACH_MSG_TIMEOUT_NONE);
+ /*
+ * respect the thread's SEND_IMPORTANCE option to allow importance
+ * donation from the kernel-side of user threads
+ * (11938665 & 23925818)
+ */
+ mach_msg_option_t option = MACH_SEND_KERNEL_DEFAULT;
+ if (current_thread()->options & TH_OPT_SEND_IMPORTANCE)
+ option &= ~MACH_SEND_NOIMPORTANCE;
+
+ mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_destroy(kmsg);
}
return mr;
}
- mr = ipc_kmsg_send(kmsg,
- MACH_SEND_KERNEL_DEFAULT,
- MACH_MSG_TIMEOUT_NONE);
+ /*
+ * respect the thread's SEND_IMPORTANCE option to force importance
+ * donation from the kernel-side of user threads
+ * (11938665 & 23925818)
+ */
+ mach_msg_option_t option = MACH_SEND_KERNEL_DEFAULT;
+ if (current_thread()->options & TH_OPT_SEND_IMPORTANCE)
+ option &= ~MACH_SEND_NOIMPORTANCE;
+
+ mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_destroy(kmsg);
}
return mr;
}
-#if 11938665
/*
* Until we are sure of its effects, we are disabling
* importance donation from the kernel-side of user
* threads in importance-donating tasks - unless the
- * option to force importance donation is passed in.
+ * option to force importance donation is passed in,
+ * or the thread's SEND_IMPORTANCE option has been set.
+ * (11938665 & 23925818)
*/
- if ((option & MACH_SEND_IMPORTANCE) == 0)
+ if (current_thread()->options & TH_OPT_SEND_IMPORTANCE)
+ option &= ~MACH_SEND_NOIMPORTANCE;
+ else if ((option & MACH_SEND_IMPORTANCE) == 0)
option |= MACH_SEND_NOIMPORTANCE;
-#endif
+
mr = ipc_kmsg_send(kmsg, option, timeout_val);
if (mr != MACH_MSG_SUCCESS) {
return mr;
}
-#if 11938665
/*
* Until we are sure of its effects, we are disabling
* importance donation from the kernel-side of user
* threads in importance-donating tasks.
+ * (11938665 & 23925818)
*/
- option |= MACH_SEND_NOIMPORTANCE;
-#endif
+ if (current_thread()->options & TH_OPT_SEND_IMPORTANCE)
+ option &= ~MACH_SEND_NOIMPORTANCE;
+ else
+ option |= MACH_SEND_NOIMPORTANCE;
+
mr = ipc_kmsg_send(kmsg, option, timeout_val);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_free(kmsg);
return mr;
}
- mr = ipc_kmsg_send(kmsg,
- MACH_SEND_KERNEL_DEFAULT,
- MACH_MSG_TIMEOUT_NONE);
+
+ /*
+ * respect the thread's SEND_IMPORTANCE option to force importance
+ * donation from the kernel-side of user threads
+ * (11938665 & 23925818)
+ */
+ mach_msg_option_t option = MACH_SEND_KERNEL_DEFAULT;
+ if (current_thread()->options & TH_OPT_SEND_IMPORTANCE)
+ option &= ~MACH_SEND_NOIMPORTANCE;
+
+ mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_destroy(kmsg);
return mr;
assert(IP_VALID(kern_port));
*semaphorep = convert_port_to_semaphore(kern_port);
+ if (*semaphorep == SEMAPHORE_NULL) {
+ /* the port is valid, but doesn't denote a semaphore */
+ kr = KERN_INVALID_CAPABILITY;
+ } else {
+ kr = KERN_SUCCESS;
+ }
ip_unlock(kern_port);
- return KERN_SUCCESS;
+ return kr;
}
/*
/* Thread still has a mutex promotion */
} else if (thread->sched_flags & TH_SFLAG_DEPRESSED_MASK) {
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_RW_DEMOTE) | DBG_FUNC_NONE,
- thread->sched_pri, DEPRESSPRI, 0, 0, 0);
-
+ (uintptr_t)thread_tid(thread), thread->sched_pri, DEPRESSPRI, 0, 0);
+
set_sched_pri(thread, DEPRESSPRI);
} else {
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_RW_DEMOTE) | DBG_FUNC_NONE,
- thread->sched_pri, thread->base_pri, 0, 0, 0);
-
+ (uintptr_t)thread_tid(thread), thread->sched_pri, thread->base_pri, 0, 0);
+
thread_recompute_sched_pri(thread, FALSE);
}
}
/* pset lock dropped */
/*
- * Continue processor shutdown in shutdown context.
+ * Continue processor shutdown in shutdown context.
+ *
+ * We save the current context in machine_processor_shutdown in such a way
+ * that when this thread is next invoked it will return from here instead of
+ * from the machine_switch_context() in thread_invoke like a normal context switch.
+ *
+ * As such, 'old_thread' is neither the idle thread nor the current thread - it's whatever
+ * thread invoked back to this one. (Usually, it's another processor's idle thread.)
+ *
+ * TODO: Make this a real thread_run of the idle_thread, so we don't have to keep this in sync
+ * with thread_invoke.
*/
thread_bind(prev);
old_thread = machine_processor_shutdown(self, processor_offline, processor);
}
/*
- *Complete the shutdown and place the processor offline.
+ * Complete the shutdown and place the processor offline.
*
- * Called at splsched in the shutdown context.
+ * Called at splsched in the shutdown context.
+ * This performs a minimal thread_invoke() to the idle thread,
+ * so it needs to be kept in sync with what thread_invoke() does.
+ *
+ * The onlining half of this is done in load_context().
*/
void
processor_offline(
processor_t processor)
{
- thread_t new_thread, old_thread = processor->active_thread;
+ assert(processor == current_processor());
+ assert(processor->active_thread == current_thread());
+
+ thread_t old_thread = processor->active_thread;
+ thread_t new_thread = processor->idle_thread;
- new_thread = processor->idle_thread;
processor->active_thread = new_thread;
processor->current_pri = IDLEPRI;
processor->current_thmode = TH_MODE_NONE;
processor->deadline = UINT64_MAX;
new_thread->last_processor = processor;
- processor->last_dispatch = mach_absolute_time();
- timer_stop(PROCESSOR_DATA(processor, thread_timer), processor->last_dispatch);
+ uint64_t ctime = mach_absolute_time();
+
+ processor->last_dispatch = ctime;
+ old_thread->last_run_time = ctime;
+
+ /* Update processor->thread_timer and ->kernel_timer to point to the new thread */
+ thread_timer_event(ctime, &new_thread->system_timer);
+ PROCESSOR_DATA(processor, kernel_timer) = &new_thread->system_timer;
+
+ timer_stop(PROCESSOR_DATA(processor, current_state), ctime);
+
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+ MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED) | DBG_FUNC_NONE,
+ old_thread->reason, (uintptr_t)thread_tid(new_thread),
+ old_thread->sched_pri, new_thread->sched_pri, 0);
machine_set_current_thread(new_thread);
assert(processor == current_processor());
assert(thread == current_thread());
+ KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_QUANTUM_EXPIRED) | DBG_FUNC_START, 0, 0, 0, 0, 0);
+
SCHED_STATS_QUANTUM_TIMER_EXPIRATION(processor);
/*
sched_timeshare_consider_maintenance(ctime);
#endif /* CONFIG_SCHED_TIMESHARE_CORE */
+
+ KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_QUANTUM_EXPIRED) | DBG_FUNC_END, preempt, 0, 0, 0, 0);
}
/*
void
sched_set_thread_base_priority(thread_t thread, int priority)
{
+ int old_priority = thread->base_pri;
thread->base_pri = priority;
+ /* A thread is 'throttled' when its base priority is at or below MAXPRI_THROTTLE */
+ if ((priority > MAXPRI_THROTTLE) && (old_priority <= MAXPRI_THROTTLE)) {
+ sched_set_thread_throttled(thread, FALSE);
+ } else if ((priority <= MAXPRI_THROTTLE) && (old_priority > MAXPRI_THROTTLE)) {
+ sched_set_thread_throttled(thread, TRUE);
+ }
+
thread_recompute_sched_pri(thread, FALSE);
}
assert_thread_sched_count(thread);
- /*
- * When backgrounding a thread, iOS has the semantic that
- * realtime and fixed priority threads should be demoted
- * to timeshare background threads.
- *
- * On OSX, realtime and fixed priority threads don't lose their mode.
- */
-
if (wants_throttle) {
thread->sched_flags |= TH_SFLAG_THROTTLED;
if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN && thread->sched_mode == TH_MODE_TIMESHARE) {
sched_background_incr(thread);
}
-
- assert_thread_sched_count(thread);
-
} else {
thread->sched_flags &= ~TH_SFLAG_THROTTLED;
if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN && thread->sched_mode == TH_MODE_TIMESHARE) {
sched_background_decr(thread);
}
-
- assert_thread_sched_count(thread);
-
}
assert_thread_sched_count(thread);
timer_data_t system_state;
timer_data_t user_state;
- timer_t current_state;
+ timer_t current_state; /* points to processor's idle, system, or user state timer */
/* Thread execution timers */
- timer_t thread_timer;
- timer_t kernel_timer;
+ timer_t thread_timer; /* points to current thread's user or system timer */
+ timer_t kernel_timer; /* points to current thread's system_timer */
/* Kernel stack cache */
struct stack_cache {
extern int8_t sched_load_shifts[NRQS];
extern uint32_t sched_decay_usage_age_factor;
extern uint32_t sched_use_combined_fgbg_decay;
-void sched_timeshare_consider_maintenance(uint64_t);
+void sched_timeshare_consider_maintenance(uint64_t ctime);
#endif /* CONFIG_SCHED_TIMESHARE_CORE */
+void sched_consider_recommended_cores(uint64_t ctime, thread_t thread);
+
extern int32_t sched_poll_yield_shift;
extern uint64_t sched_safe_duration;
#if CONFIG_TELEMETRY
#include <kern/telemetry.h>
#endif
-
+
+#include <sys/kdebug.h>
+
uint32_t avenrun[3] = {0, 0, 0};
uint32_t mach_factor[3] = {0, 0, 0};
combined_fgbg_load_now = NRQS - 1;
}
+ KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+ MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_LOAD) | DBG_FUNC_NONE,
+ (nthreads - nshared), (nshared - nbackground), nbackground, 0, 0);
+
/*
* Sample total running threads.
*/
uint64_t timer_deadline_tracking_bin_1;
uint64_t timer_deadline_tracking_bin_2;
+#endif /* CONFIG_SCHED_TIMESHARE_CORE */
+
thread_t sched_maintenance_thread;
-#endif /* CONFIG_SCHED_TIMESHARE_CORE */
uint64_t sched_one_second_interval;
assert((abstime >> 32) == 0 && (uint32_t)abstime != 0);
sched_telemetry_interval = (uint32_t)abstime;
#endif
+
}
#endif /* CONFIG_SCHED_TIMESHARE_CORE */
/* We're handling all scheduling AST's */
ast_off(AST_SCHEDULING);
+#if PROC_REF_DEBUG
+ if ((continuation != NULL) && (self->task != kernel_task)) {
+ if (uthread_get_proc_refcount(self->uthread) != 0) {
+ panic("thread_block_reason with continuation uthread %p with uu_proc_refcount != 0", self->uthread);
+ }
+ }
+#endif
+
self->continuation = continuation;
self->parameter = parameter;
removed_from_runq = thread_run_queue_remove(thread);
}
+ thread->sched_pri = priority;
+
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_CHANGE_PRIORITY),
(uintptr_t)thread_tid(thread),
thread->base_pri,
0, /* eventually, 'reason' */
0);
- thread->sched_pri = priority;
-
if (is_current_thread) {
nurgency = thread_get_urgency(thread, &urgency_param1, &urgency_param2);
/*
simple_lock_init(&sched_vm_group_list_lock, 0);
+
result = kernel_thread_start_priority((thread_continue_t)sched_init_thread,
(void *)SCHED(maintenance_continuation), MAXPRI_KERNEL, &thread);
if (result != KERN_SUCCESS)
*/
sched_vm_group_maintenance();
+
KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_MAINTENANCE)|DBG_FUNC_END,
sched_pri_shift,
sched_background_pri_shift,
{
thread_block(THREAD_CONTINUE_NULL);
- sched_maintenance_thread = current_thread();
+ thread_t thread = current_thread();
+
+ sched_maintenance_thread = thread;
+
continuation();
/*NOTREACHED*/
extern void bsd_scale_setup(int);
extern unsigned int semaphore_max;
extern void stackshot_lock_init(void);
+extern void console_init(void);
/*
* Running in virtual memory, on the interrupt stack.
doprnt_hide_pointers = FALSE;
}
+ kernel_bootstrap_log("console_init");
+ console_init();
+
kernel_bootstrap_log("stackshot_lock_init");
stackshot_lock_init();
processor_t myprocessor = self->last_processor;
self->sched_pri = DEPRESSPRI;
+
+ KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_CHANGE_PRIORITY),
+ (uintptr_t)thread_tid(self),
+ self->base_pri,
+ self->sched_pri,
+ 0, /* eventually, 'reason' */
+ 0);
+
myprocessor->current_pri = self->sched_pri;
self->sched_flags |= TH_SFLAG_DEPRESS;
thread_lock(self);
if (!(self->sched_flags & TH_SFLAG_DEPRESSED_MASK)) {
self->sched_pri = DEPRESSPRI;
+
+ KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_CHANGE_PRIORITY),
+ (uintptr_t)thread_tid(self),
+ self->base_pri,
+ self->sched_pri,
+ 0, /* eventually, 'reason' */
+ 0);
+
myprocessor->current_pri = self->sched_pri;
}
self->computation_epoch = abstime;
}
+void
+task_bank_reset(__unused task_t task) {
+
+#if CONFIG_BANK
+ if (task->bank_context != NULL) {
+ bank_task_destroy(task);
+ }
+#endif
+
+}
+
+/*
+ * NOTE: This should only be called when the P_LINTRANSIT
+ * flag is set (the proc_trans lock is held) on the
+ * proc associated with the task.
+ */
+void
+task_bank_init(__unused task_t task) {
+
+#if CONFIG_BANK
+ if (task->bank_context != NULL) {
+ panic("Task bank init called with non null bank context for task: %p and bank_context: %p", task, task->bank_context);
+ }
+ bank_task_initialize(task);
+#endif
+
+}
+
#if TASK_REFERENCE_LEAK_DEBUG
#include <kern/btlog.h>
/*
* remove the reference on atm descriptor
*/
- task_atm_reset(task);
+ task_atm_reset(task);
-#if CONFIG_BANK
/*
* remove the reference on bank context
*/
- if (task->bank_context != NULL) {
- bank_task_destroy(task->bank_context);
- task->bank_context = NULL;
- }
-#endif
+ task_bank_reset(task);
if (task->task_io_stats)
kfree(task->task_io_stats, sizeof(struct io_stat_info));
* remove the limit & callback on the wakeups ledger entry.
*/
#if CONFIG_TELEMETRY
- telemetry_task_ctl_locked(current_task(), TF_WAKEMON_WARNING, 0);
+ telemetry_task_ctl_locked(task, TF_WAKEMON_WARNING, 0);
#endif
ledger_disable_refill(ledger, task_ledgers.interrupt_wakeups);
ledger_disable_callback(ledger, task_ledgers.interrupt_wakeups);
extern void task_importance_mark_denap_receiver(task_t task, boolean_t denap);
extern void task_importance_reset(task_t task);
extern void task_atm_reset(task_t task);
+extern void task_bank_reset(task_t task);
+extern void task_bank_init(task_t task);
#if IMPORTANCE_INHERITANCE
thread_template.static_param = 0;
thread_template.policy_reset = 0;
- thread_template.base_pri = 0;
+ thread_template.base_pri = BASEPRI_DEFAULT;
thread_template.sched_pri = 0;
thread_template.max_priority = 0;
thread_template.task_priority = 0;
new_thread->importance = new_priority - new_thread->task_priority;
new_thread->saved_importance = new_thread->importance;
- if (parent_task->max_priority <= MAXPRI_THROTTLE) {
- sched_set_thread_throttled(new_thread, TRUE);
- }
-
sched_set_thread_base_priority(new_thread, new_priority);
+
thread_policy_create(new_thread);
/* Chain the thread onto the task's list */
return kr;
}
+/*
+ * thread_enable_send_importance - set/clear the SEND_IMPORTANCE thread option bit.
+ */
+void thread_enable_send_importance(thread_t thread, boolean_t enable)
+{
+ if (enable == TRUE)
+ thread->options |= TH_OPT_SEND_IMPORTANCE;
+ else
+ thread->options &= ~TH_OPT_SEND_IMPORTANCE;
+}
+
#if CONFIG_DTRACE
uint32_t dtrace_get_thread_predcache(thread_t thread)
{
#define TH_OPT_GLOBAL_FORCED_IDLE 0x0100 /* Thread performs forced idle for thermal control */
#define TH_OPT_SCHED_VM_GROUP 0x0200 /* Thread belongs to special scheduler VM group */
#define TH_OPT_HONOR_QLIMIT 0x0400 /* Thread will honor qlimit while sending mach_msg, regardless of MACH_SEND_ALWAYS */
+#define TH_OPT_SEND_IMPORTANCE 0x0800 /* Thread will allow importance donation from kernel rpc */
boolean_t wake_active; /* wake event on stop */
int at_safe_point; /* thread_abort_safely allowed */
extern kern_return_t thread_set_voucher_name(mach_port_name_t name);
extern kern_return_t thread_get_current_voucher_origin_pid(int32_t *pid);
+extern void thread_enable_send_importance(thread_t thread, boolean_t enable);
+
#endif /* XNU_KERNEL_PRIVATE */
processor_t myprocessor = thread->last_processor;
thread->sched_pri = DEPRESSPRI;
+
+ KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_CHANGE_PRIORITY),
+ (uintptr_t)thread_tid(thread),
+ thread->base_pri,
+ thread->sched_pri,
+ 0, /* eventually, 'reason' */
+ 0);
+
myprocessor->current_pri = thread->sched_pri;
}
thread_unlock(thread);
s = splsched();
thread_lock(thread);
+ __unused
integer_t old_max_priority = thread->max_priority;
thread->task_priority = priority;
thread->max_priority = max_priority;
- /* A thread is 'throttled' when its max priority is below MAXPRI_THROTTLE */
- if ((max_priority > MAXPRI_THROTTLE) && (old_max_priority <= MAXPRI_THROTTLE)) {
- sched_set_thread_throttled(thread, FALSE);
- } else if ((max_priority <= MAXPRI_THROTTLE) && (old_max_priority > MAXPRI_THROTTLE)) {
- sched_set_thread_throttled(thread, TRUE);
- }
thread_recompute_priority(thread);
assert_thread_sched_count(thread);
+ if (thread->sched_flags & TH_SFLAG_THROTTLE_DEMOTED)
+ sched_thread_mode_undemote(thread, TH_SFLAG_THROTTLE_DEMOTED);
+
+ assert_thread_sched_count(thread);
+
if (thread->sched_flags & TH_SFLAG_THROTTLED)
sched_set_thread_throttled(thread, FALSE);
thread->importance = 0;
- sched_set_thread_base_priority(thread, thread->task_priority);
-
/* Prevent further changes to thread base priority or mode */
thread->policy_reset = 1;
+ sched_set_thread_base_priority(thread, thread->task_priority);
+
assert(thread->BG_COUNT == 0);
assert_thread_sched_count(thread);
out memory_info : mach_memory_info_array_t,
Dealloc);
+/*
+ * Update the global multiuser flags, readable from the commpage
+ */
+routine host_set_multiuser_config_flags(
+ host_priv : host_priv_t;
+ in multiuser_flags : uint32_t);
+
+#if !KERNEL && LIBSYSCALL_INTERFACE
+routine host_get_multiuser_config_flags(
+ host : host_t;
+ out multiuser_flags : uint32_t);
+#else
+skip;
+#endif // !KERNEL && LIBSYSCALL_INTERFACE
+
+#if !KERNEL && LIBSYSCALL_INTERFACE
+routine host_check_multiuser_mode(
+ host : host_t;
+ out multiuser_mode : uint32_t);
+#else
+skip;
+#endif // !KERNEL && LIBSYSCALL_INTERFACE
+
/* vim: set ft=c : */
#define MACH_VOUCHER_ATTR_COPY ((mach_voucher_attr_recipe_command_t)1)
#define MACH_VOUCHER_ATTR_REMOVE ((mach_voucher_attr_recipe_command_t)2)
#define MACH_VOUCHER_ATTR_SET_VALUE_HANDLE ((mach_voucher_attr_recipe_command_t)3)
+#define MACH_VOUCHER_ATTR_AUTO_REDEEM ((mach_voucher_attr_recipe_command_t)4)
+#define MACH_VOUCHER_ATTR_SEND_PREPROCESS ((mach_voucher_attr_recipe_command_t)5)
/* redeem is on its way out? */
#define MACH_VOUCHER_ATTR_REDEEM ((mach_voucher_attr_recipe_command_t)10)
#define MACH_VOUCHER_ATTR_VALUE_MAX_NESTED ((mach_voucher_attr_value_handle_array_size_t)4)
typedef uint32_t mach_voucher_attr_value_reference_t;
+typedef uint32_t mach_voucher_attr_value_flags_t;
+#define MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE ((mach_voucher_attr_value_flags_t)0)
+#define MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST ((mach_voucher_attr_value_flags_t)1)
/* USE - TBD */
typedef uint32_t mach_voucher_attr_control_flags_t;
#define CPUFAMILY_ARM_SWIFT 0x1e2d6381
#define CPUFAMILY_ARM_CYCLONE 0x37a09642
#define CPUFAMILY_ARM_TYPHOON 0x2c91a47e
+#define CPUFAMILY_ARM_TWISTER 0x92fb37c8
/* The following synonyms are deprecated: */
#define CPUFAMILY_INTEL_6_14 CPUFAMILY_INTEL_YONAH
/* The options implemented by the library interface to mach_msg et. al. */
#define MACH_MSG_OPTION_LIB (MACH_SEND_INTERRUPT | MACH_RCV_INTERRUPT)
-/* Default options to use when sending from the kernel */
-#if 11938665
- /*
- * Until we are sure of its effects, we are disabling
- * importance donation from the kernel-side of user
- * threads in importance-donating tasks.
- */
+/*
+ * Default options to use when sending from the kernel.
+ *
+ * Until we are sure of its effects, we are disabling
+ * importance donation from the kernel-side of user
+ * threads in importance-donating tasks.
+ * (11938665 & 23925818)
+ */
#define MACH_SEND_KERNEL_DEFAULT (MACH_SEND_MSG | \
MACH_SEND_ALWAYS | MACH_SEND_NOIMPORTANCE)
-#else
-#define MACH_SEND_KERNEL_DEFAULT (MACH_SEND_MSG | MACH_SEND_ALWAYS)
-#endif
#endif /* MACH_KERNEL_PRIVATE */
/*
* Maximum number of unmapped pagers we're willing to keep around.
*/
-int apple_protect_pager_cache_limit = 10;
+int apple_protect_pager_cache_limit = 20;
/*
* Statistics & counters.
size = round_page_32(C_SEG_OFFSET_TO_BYTES(c_seg->c_populated_offset));
if (size == 0) {
- assert(c_seg->c_on_minorcompact_q);
assert(c_seg->c_bytes_used == 0);
c_seg_switch_state(c_seg, C_IS_EMPTY, FALSE);
} else {
/* proceed with the invalid page */
kr = KERN_SUCCESS;
- if (!m->cs_validated) {
+ if (!m->cs_validated &&
+ !m->object->code_signed) {
/*
- * This page has not been validated, so it
- * must not belong to a code-signed object
- * and should not be forcefully considered
+ * This page has not been (fully) validated but
+ * does not belong to a code-signed object
+ * so it should not be forcefully considered
* as tainted.
* We're just concerned about it here because
* we've been asked to "execute" it but that
* even though they're just reading it and not
* executing from it.
*/
- assert(!m->object->code_signed);
} else {
/*
* Page might have been tainted before or not;
return(result);
}
+
/*
* vm_map_protect:
*
if (override_nx(map, VME_ALIAS(current)) && prot)
prot |= VM_PROT_EXECUTE;
+
if (current->is_sub_map && current->use_pmap) {
pmap_protect(VME_SUBMAP(current)->pmap,
current->vme_start,
}
else
{
+#if 1
+ site = NULL;
+#else
+ /* this code would free a site with no allocations but can race a new
+ * allocation being made */
vm_tag_free_locked(site->tag);
site->tag = VM_KERN_MEMORY_NONE;
vm_allocation_sites[idx] = NULL;
if (!(VM_TAG_UNLOAD & site->flags)) site = NULL;
+#endif
}
}
lck_spin_unlock(&vm_allocation_sites_lock);
if (NULL == p->pm_obj)
panic("pmap_create pte obj");
- /* All pmaps share the kernel's pml4 */
- pml4 = pmap64_pml4(p, 0ULL);
- kpml4 = kernel_pmap->pm_pml4;
- pml4[KERNEL_PML4_INDEX] = kpml4[KERNEL_PML4_INDEX];
- pml4[KERNEL_KEXTS_INDEX] = kpml4[KERNEL_KEXTS_INDEX];
- pml4[KERNEL_PHYSMAP_PML4_INDEX] = kpml4[KERNEL_PHYSMAP_PML4_INDEX];
+ if (!(flags & PMAP_CREATE_EPT)) {
+ /* All host pmaps share the kernel's pml4 */
+ pml4 = pmap64_pml4(p, 0ULL);
+ kpml4 = kernel_pmap->pm_pml4;
+ pml4[KERNEL_PML4_INDEX] = kpml4[KERNEL_PML4_INDEX];
+ pml4[KERNEL_KEXTS_INDEX] = kpml4[KERNEL_KEXTS_INDEX];
+ pml4[KERNEL_PHYSMAP_PML4_INDEX] = kpml4[KERNEL_PHYSMAP_PML4_INDEX];
+ }
PMAP_TRACE(PMAP_CODE(PMAP__CREATE) | DBG_FUNC_START,
p, flags, 0, 0, 0);
@lldb_type_summary(['bank_element', 'bank_element_t'])
-@header("{0: <20s} {1: <16s} {2: <16s} {3: <16s} {4: <16s} {5: <20s} {6: <20s}".format("bank_element", "type", "ref_count", "sync", "pid", "task", "process_name"))
+@header("{0: <20s} {1: <16s} {2: <16s} {3: <16s} {4: <20s} {5: <20s}".format("bank_element", "type", "ref_count", "sync", "task", "process_name"))
def GetBankElementSummary(bank_element):
""" Summarizes the bank element
params: bank_element = value of the object of type bank_element_t
returns: String with summary of the type.
"""
- format_str = "{0: <#020x} {1: <16s} {2: <16d} {3: <16d} {4: <16d}"
+ format_str = "{0: <#020x} {1: <16s} {2: <16d} {3: <16d}"
if bank_element.be_type == 0:
- out_string = format_str.format(bank_element, "BANK_TASK", unsigned(bank_element.be_refs), unsigned(bank_element.be_made), bank_element.be_pid)
+ out_string = format_str.format(bank_element, "BANK_TASK", unsigned(bank_element.be_refs), unsigned(bank_element.be_made))
else:
- out_string = format_str.format(bank_element, "BANK_ACCOUNT", unsigned(bank_element.be_refs), unsigned(bank_element.be_made), bank_element.be_pid)
+ out_string = format_str.format(bank_element, "BANK_ACCOUNT", unsigned(bank_element.be_refs), unsigned(bank_element.be_made))
#if DEVELOPMENT
format_str = "{0: <#020x} {1: <20s}"
@lldb_type_summary(['bank_task', 'bank_task_t'])
-@header("{0: <20s} {1: <16s} {2: <20s} {3: <16s} {4: <16s} {5: <20s} {6: <20s}".format("bank_task", "pid", "ledger", "ref_count", "sync", "task", "process_name"))
+@header("{0: <20s} {1: <16s} {2: <20s} {3: <16s} {4: <16s} {5: <16s} {6: <16s} {7: <16s} {8: <20s} {9: <20s}".format("bank_task", "pid", "ledger", "ref_count", "sync", "persona id", "uid", "gid", "task", "process_name"))
def GetBankTaskSummary(bank_task):
""" Summarizes the bank task
params: bank_task = value of the object of type bank_task_t
returns: String with summary of the type.
"""
- format_str = "{0: <#020x} {1: <16d} {2: <#020x} {3: <16d} {4: <16d}"
- out_string = format_str.format(bank_task, bank_task.bt_elem.be_pid, bank_task.bt_creditcard, unsigned(bank_task.bt_elem.be_refs), unsigned(bank_task.bt_elem.be_made))
+ format_str = "{0: <#020x} {1: <16d} {2: <#020x} {3: <16d} {4: <16d} {5: <16d} {6: <16d} {7: <16d}"
+ out_string = format_str.format(bank_task, bank_task.bt_proc_persona.pid, bank_task.bt_creditcard, unsigned(bank_task.bt_elem.be_refs), unsigned(bank_task.bt_elem.be_made), bank_task.bt_proc_persona.persona_id, bank_task.bt_proc_persona.uid, bank_task.bt_proc_persona.gid)
#if DEVELOPMENT
format_str = "{0: <#020x} {1: <20s}"
@lldb_type_summary(['bank_account', 'bank_account_t'])
-@header("{0: <20s} {1: <16s} {2: <16s} {3: <20s} {4: <16s} {5: <16s} {6: <20s} {7: <20s} {8: <20s} {9: <20s}".format("bank_account", "holder_pid", "merchant_pid", "chit_ledger", "ref_count", "sync", "holder_task", "holder_process", "merchant_task", "merchant_process"))
+@header("{0: <20s} {1: <16s} {2: <16s} {3: <16s} {4: <16s} {5: <20s} {6: <16s} {7: <16s} {8: <20s} {9: <20s} {10: <20s} {11: <20s}".format("bank_account", "holder_pid", "merchant_pid", "secure_orig", "proximal_pid", "chit_ledger", "ref_count", "sync", "holder_task", "holder_process", "merchant_task", "merchant_process"))
def GetBankAccountSummary(bank_account):
""" Summarizes the bank account
params: bank_task = value of the object of type bank_account_t
returns: String with summary of the type.
"""
- format_str = "{0: <#020x} {1: <16d} {2: <16d} {3: <#020x} {4: <16d} {5: <16d}"
- out_string = format_str.format(bank_account, bank_account.ba_holder.bt_elem.be_pid, bank_account.ba_merchant.bt_elem.be_pid, bank_account.ba_bill, unsigned(bank_account.ba_elem.be_refs), unsigned(bank_account.ba_elem.be_made))
+ format_str = "{0: <#020x} {1: <16d} {2: <16d} {3: <16d} {4: <16d} {5: <#020x} {6: <16d} {7: <16d}"
+ out_string = format_str.format(bank_account, bank_account.ba_holder.bt_proc_persona.pid, bank_account.ba_merchant.bt_proc_persona.pid, bank_account.ba_secureoriginator.bt_proc_persona.pid, bank_account.ba_proximateprocess.bt_proc_persona.pid,bank_account.ba_bill, unsigned(bank_account.ba_elem.be_refs), unsigned(bank_account.ba_elem.be_made))
#if DEVELOPMENT
format_str = "{0: <#020x} {1: <20s}"
params: handle_ptr - uint64 number stored in handle of voucher.
returns: str - summary of bank element
"""
+ if handle_ptr == 1 :
+ return "Bank task of Current task"
elem = kern.GetValueFromAddress(handle_ptr, 'bank_element_t')
if elem.be_type & 1 :
ba = Cast(elem, 'struct bank_account *')
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
+#include <libgen.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
dsecs * 1.0E6 / (double) totalmsg);
if (save_perfdata == TRUE) {
- record_perf_data("kqmpmm_avg_msg_latency", "usec", avg_msg_latency, "Message latency measured in microseconds. Lower is better", stderr);
+ char name[256];
+ snprintf(name, sizeof(name), "%s_avg_msg_latency", basename(argv[0]));
+ record_perf_data(name, "usec", avg_msg_latency, "Message latency measured in microseconds. Lower is better", stderr);
}
return (0);
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
+#include <libgen.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
double avg_msg_latency = dsecs*1.0E6 / (double)totalmsg;
if (save_perfdata == TRUE) {
- record_perf_data("mpmm_avg_msg_latency", "usec", avg_msg_latency, "Message latency measured in microseconds. Lower is better", stderr);
+ char name[256];
+ snprintf(name, sizeof(name), "%s_avg_msg_latency", basename(argv[0]));
+ record_perf_data(name, "usec", avg_msg_latency, "Message latency measured in microseconds. Lower is better", stderr);
}
if (stress_prepost) {
--- /dev/null
+#!/bin/bash
+
+TESTDIR=$PWD/
+MPMMTEST="${TESTDIR}/MPMMtest"
+MPMMTEST_64="${TESTDIR}/MPMMtest_64"
+KQMPMMTEST="${TESTDIR}/KQMPMMtest"
+KQMPMMTEST_64="${TESTDIR}/KQMPMMtest_64"
+
+if [ -e $MPMMTEST ] && [ -x $MPMMTEST ]
+then
+ echo ""; echo " Running $MPMMTEST";
+ $MPMMTEST -perf || { x=$?; echo "$MPMMTEST failed $x "; exit $x; }
+fi
+
+if [ -e $MPMMTEST_64 ] && [ -x $MPMMTEST_64 ]
+then
+ echo ""; echo " Running $MPMMTEST_64"
+ $MPMMTEST_64 -perf || { x=$?; echo "$MPMMTEST_64 failed $x"; exit $x; }
+fi
+
+if [ -e $KQMPMMTEST ] && [ -x $KQMPMMTEST ]
+then
+ echo ""; echo " Running $KQMPMMTEST"
+ $KQMPMMTEST -perf || { x=$?; echo "$KQMPMMTEST failed $x"; exit $x; }
+fi
+
+if [ -e $KQMPMMTEST_64 ] && [ -x $KQMPMMTEST_64 ]
+then
+ echo ""; echo " Running $KQMPMMTEST_64"
+ $KQMPMMTEST_64 -perf || { x=$?; echo "$KQMPMMTEST_64 failed $x"; exit $?; }
+fi
+
+echo ""; echo " SUCCESS";
+exit 0;
+
+
ARCH_32_TARGETS := MPMMtest KQMPMMtest KQMPMMtestD
ARCH_64_TARGETS := MPMMtest_64 KQMPMMtest_64 KQMPMMtest_64D
-TARGETS := $(if $(ARCH_64), $(ARCH_64_TARGETS)) $(if $(ARCH_32), $(ARCH_32_TARGETS))
+TARGETS := MPMMtest_perf.sh $(if $(ARCH_64), $(ARCH_64_TARGETS)) $(if $(ARCH_32), $(ARCH_32_TARGETS))
all: $(addprefix $(DSTROOT)/, $(TARGETS))
${CC} ${CFLAGS} ${ARCH_64_FLAGS} -DDIRECT_MSG_RCV=1 -o $(SYMROOT)/$(notdir $@) $?
if [ ! -e $@ ]; then ditto $(SYMROOT)/$(notdir $@) $@; fi
+$(DSTROOT)/MPMMtest_perf.sh: MPMMtest_run.sh
+ cp $? $@
+ chmod +x $@
+
clean:
rm -rf $(addprefix $(DSTROOT)/,$(TARGETS)) $(addprefix $(SYMROOT)/,$(TARGETS)) $(SYMROOT)/*.dSYM
endif
DEPLOYMENT_TARGET_DEFINES = -DPLATFORM_$(PLATFORM)
+
+ifeq ($(RC_XBS),YES)
+_v =
+else ifeq ($(VERBOSE),YES)
+_v =
+else
+_v = @
+endif
rm -f $(addprefix $(DSTROOT)/,$(EXECUTABLES))
# DEPENDENCIES
-$(addprefix $(DSTROOT)/,$(EXECUTABLES)): DSTROOT SYMROOT
+$(addprefix $(DSTROOT)/,$(EXECUTABLES)): | DSTROOT SYMROOT
-$(addprefix $(OBJROOT)/,$(OBJECTS)): OBJROOT
+$(addprefix $(OBJROOT)/,$(OBJECTS)): | OBJROOT
DSTROOT SYMROOT OBJROOT:
- mkdir -p $($@)
+ $(_v)mkdir -p $($@)
# OBJECTS
-$(OBJROOT)/exit-asm.o: exit-asm.S OBJROOT
+$(OBJROOT)/exit-asm.o: exit-asm.S | OBJROOT
$(CC) -c -o $@ $< $(CFLAGS)
-$(OBJROOT)/exit.o: exit.c OBJROOT
+$(OBJROOT)/exit.o: exit.c | OBJROOT
$(CC) -c -o $@ $< $(CFLAGS)
-$(OBJROOT)/printexecinfo.o: printexecinfo.c OBJROOT
+$(OBJROOT)/printexecinfo.o: printexecinfo.c | OBJROOT
$(CC) -c -o $@ $< $(CFLAGS)
-$(OBJROOT)/run.o: run.c OBJROOT
+$(OBJROOT)/run.o: run.c | OBJROOT
$(CC) -c -o $@ $< $(CFLAGS)
# EXECUTABLES
fi
case "$PRODUCT" in
+ "Watch OS")
+ COUNT=500
+ ;;
"iPhone OS")
COUNT=1000
;;
+ "Mac OS X")
+ COUNT=6000
+ ;;
*)
- COUNT=10000
+ COUNT=1000
;;
esac
--- /dev/null
+include ../Makefile.common
+
+CC:=$(shell xcrun -sdk "$(SDKROOT)" -find cc)
+
+SYMROOT?=$(shell /bin/pwd)
+
+CFLAGS := -g -O2 -isysroot $(SDKROOT) -I$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders
+
+ifdef RC_ARCHS
+ ARCHS:=$(RC_ARCHS)
+ else
+ ifeq "$(Embedded)" "YES"
+ ARCHS:=armv7 armv7s arm64
+ else
+ ARCHS:=x86_64 i386
+ endif
+endif
+
+ARCH_32 := $(filter-out %64, $(ARCHS))
+ARCH_64 := $(filter %64, $(ARCHS))
+
+ARCH_32_FLAGS := $(patsubst %, -arch %, $(ARCH_32))
+ARCH_64_FLAGS := $(patsubst %, -arch %, $(ARCH_64))
+ARCH_FLAGS := $(if $(ARCH_64), $(ARCH_64_FLAGS)) $(if $(ARCH_32), $(ARCH_32_FLAGS))
+
+DSTROOT?=$(shell /bin/pwd)
+
+TARGETS := persona_mgr persona_spawn
+
+all: $(addprefix $(DSTROOT)/, $(TARGETS))
+
+$(DSTROOT)/persona_%: persona_%.c persona_test.h Makefile
+ ${CC} ${CFLAGS} ${ARCH_FLAGS} -o $(SYMROOT)/$(notdir $@) $<
+ if [ ! -e $@ ]; then ditto $(SYMROOT)/$(notdir $@) $@; fi
+
+clean:
+ rm -rf $(addprefix $(DSTROOT)/,$(TARGETS)) $(addprefix $(SYMROOT)/,$(TARGETS)) $(SYMROOT)/*.dSYM
--- /dev/null
+/*
+ * persona_mgr.c
+ * Tool to manage personas
+ *
+ * Jeremy C. Andrus <jeremy_andrus@apple.com>
+ *
+ */
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/task.h>
+#include <mach/vm_param.h>
+#include <sys/kauth.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "persona_test.h"
+
+/* internal */
+#include <libproc.h>
+#include <spawn_private.h>
+#include <sys/persona.h>
+#include <sys/proc_info.h>
+#include <sys/spawn_internal.h>
+
+#define PROG_NAME "Persona Manager"
+#define PROG_VMAJOR 0
+#define PROG_VMINOR 1
+
+enum {
+ PERSONA_OP_CREATE = 1,
+ PERSONA_OP_DESTROY = 2,
+ PERSONA_OP_LOOKUP = 3,
+ PERSONA_OP_MAX = 3,
+};
+
+static struct mgr_config {
+ int verbose;
+} g;
+
+
+static int persona_op_create(struct kpersona_info *ki)
+{
+ int ret;
+ uid_t persona_id = 0;
+
+ info("Creating persona...");
+ ki->persona_info_version = PERSONA_INFO_V1;
+ ret = kpersona_alloc(ki, &persona_id);
+ if (ret == 0) {
+ info("Created persona %d:", persona_id);
+ dump_kpersona(NULL, ki);
+ } else {
+ err("kpersona_alloc return %d (errno:%d)", ret, errno);
+ }
+
+ return ret;
+}
+
+static int persona_op_destroy(struct kpersona_info *ki)
+{
+ int ret;
+
+ info("Destroying Persona %d...", ki->persona_id);
+ ki->persona_info_version = PERSONA_INFO_V1;
+ ret = kpersona_dealloc(ki->persona_id);
+ if (ret < 0)
+ err_print("destroy failed!");
+
+ return ret;
+}
+
+static int persona_op_lookup(struct kpersona_info *ki, pid_t pid, uid_t uid)
+{
+ int ret;
+
+ info("Looking up persona (pid:%d, uid:%d)", pid, uid);
+ if (pid > 0) {
+ ki->persona_info_version = PERSONA_INFO_V1;
+ ret = kpersona_pidinfo(pid, ki);
+ if (ret < 0)
+ err_print("pidinfo failed!");
+ else
+ dump_kpersona("Persona-for-pid:", ki);
+ } else {
+ int np = 0;
+ uid_t personas[128];
+ size_t npersonas = ARRAY_SZ(personas);
+ const char *name = NULL;
+ if (ki->persona_name[0] != 0)
+ name = ki->persona_name;
+
+ np = kpersona_find(name, uid, personas, &npersonas);
+ if (np < 0)
+ err("kpersona_find returned %d (errno:%d)", np, errno);
+ info("Found %zu persona%c", npersonas, npersonas != 1 ? 's' : ' ');
+ np = npersonas;
+ while (np--) {
+ info("\tpersona[%d]=%d...", np, personas[np]);
+ ki->persona_info_version = PERSONA_INFO_V1;
+ ret = kpersona_info(personas[np], ki);
+ if (ret < 0)
+ err("kpersona_info failed (errno:%d) for persona[%d]", errno, personas[np]);
+ dump_kpersona(NULL, ki);
+ }
+ }
+
+ return ret;
+}
+
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ *
+ * Main Entry Point
+ *
+ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ */
+static void usage_main(const char *progname, const char *msg, int verbose)
+{
+ const char *nm = basename((char *)progname);
+
+ if (msg)
+ printf("%s\n\n", msg);
+
+ printf("%s v%d.%d\n", PROG_NAME, PROG_VMAJOR, PROG_VMINOR);
+ printf("usage: %s [op] [-v] [-i id] [-t type] [-p pid] [-u uid] [-g gid] [-l login] [-G {groupspec}] [-m gmuid]\n", nm);
+ if (!verbose)
+ exit(1);
+
+ printf("\t%-15s\tOne of: create | destroy | lookup\n", "[op]");
+ printf("\t%-15s\tBe verbose\n", "-v");
+
+ printf("\t%-15s\tID of the persona\n", "-i id");
+ printf("\t%-15s\tType of the persona\n", "-t type");
+ printf("\t%-15s\tPID of the process whose persona info to lookup\n", "-p pid");
+ printf("\t%-15s\tUID to use in lookup\n", "-u uid");
+ printf("\t%-15s\tGID of the persona\n", "-g gid");
+ printf("\t%-15s\tLogin name of the persona\n", "-l login");
+ printf("\t%-15s\tGroups to which the persona will belong\n", "-G {groupspec}");
+ printf("\t%-15s\tgroupspec: G1{,G2,G3...}\n", " ");
+ printf("\t%-15s\tUID used for memberd lookup (opt-in to memberd)\n", "-m gmuid");
+
+ printf("\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ char ch;
+ int ret;
+
+ const char *op_str = NULL;
+ int persona_op = 0;
+ struct kpersona_info kinfo;
+ uid_t uid = (uid_t)-1;
+ pid_t pid = (pid_t)-1;
+
+ /*
+ * Defaults
+ */
+ g.verbose = 0;
+
+ if (geteuid() != 0)
+ err("%s must be run as root", argv[0] ? basename(argv[0]) : PROG_NAME);
+
+ if (argc < 2)
+ usage_main(argv[0], "Not enough arguments", 0);
+
+ op_str = argv[1];
+
+ if (strcmp(op_str, "create") == 0)
+ persona_op = PERSONA_OP_CREATE;
+ else if (strcmp(op_str, "destroy") == 0)
+ persona_op = PERSONA_OP_DESTROY;
+ else if (strcmp(op_str, "lookup") == 0)
+ persona_op = PERSONA_OP_LOOKUP;
+ else if (strcmp(op_str, "help") == 0 || strcmp(op_str, "-h") == 0)
+ usage_main(argv[0], NULL, 1);
+
+ if (persona_op <= 0 || persona_op > PERSONA_OP_MAX)
+ usage_main(argv[0], "Invalid [op]", 0);
+
+ memset(&kinfo, 0, sizeof(kinfo));
+ kinfo.persona_gmuid = KAUTH_UID_NONE;
+
+ /*
+ * Argument parse
+ */
+ optind = 2;
+ while ((ch = getopt(argc, argv, "vi:t:p:u:g:l:G:m:h")) != -1) {
+ switch (ch) {
+ case 'i':
+ ret = atoi(optarg);
+ if (ret <= 0)
+ err("Invalid Persona ID: %s", optarg);
+ kinfo.persona_id = (uid_t)ret;
+ break;
+ case 't':
+ ret = atoi(optarg);
+ if (ret <= PERSONA_INVALID || ret > PERSONA_TYPE_MAX)
+ err("Invalid type specification: %s", optarg);
+ kinfo.persona_type = ret;
+ break;
+ case 'p':
+ ret = atoi(optarg);
+ if (ret <= 0)
+ err("Invalid PID: %s", optarg);
+ pid = (pid_t)ret;
+ break;
+ case 'u':
+ ret = atoi(optarg);
+ if (ret <= 0)
+ err("Invalid UID: %s", optarg);
+ uid = (uid_t)ret;
+ break;
+ case 'g':
+ kinfo.persona_gid = (gid_t)atoi(optarg);
+ if (kinfo.persona_gid <= 500)
+ err("Invalid GID: %d", kinfo.persona_gid);
+ break;
+ case 'l':
+ strncpy(kinfo.persona_name, optarg, MAXLOGNAME);
+ break;
+ case 'G':
+ ret = parse_groupspec(&kinfo, optarg);
+ if (ret < 0)
+ err("Invalid groupspec: \"%s\"", optarg);
+ break;
+ case 'm':
+ ret = atoi(optarg);
+ if (ret < 0)
+ err("Invalid group membership ID: %s", optarg);
+ kinfo.persona_gmuid = (uid_t)ret;
+ break;
+ case 'v':
+ g.verbose = 1;
+ break;
+ case 'h':
+ case '?':
+ usage_main(argv[0], NULL, 1);
+ case ':':
+ default:
+ printf("Invalid option: '%c'\n", ch);
+ usage_main(argv[0], NULL, 0);
+ }
+ }
+
+ if (uid == (uid_t)-1)
+ uid = kinfo.persona_id;
+
+ if (kinfo.persona_gmuid && kinfo.persona_ngroups == 0) {
+ /*
+ * In order to set the group membership UID, we need to set at
+ * least one group: make it equal to either the GID or UID
+ */
+ kinfo.persona_ngroups = 1;
+ if (kinfo.persona_gid)
+ kinfo.persona_groups[0] = kinfo.persona_gid;
+ else
+ kinfo.persona_groups[0] = kinfo.persona_id;
+ }
+
+ if (g.verbose)
+ dump_kpersona("Input persona:", &kinfo);
+
+ switch (persona_op) {
+ case PERSONA_OP_CREATE:
+ ret = persona_op_create(&kinfo);
+ break;
+ case PERSONA_OP_DESTROY:
+ ret = persona_op_destroy(&kinfo);
+ break;
+ case PERSONA_OP_LOOKUP:
+ ret = persona_op_lookup(&kinfo, pid, uid);
+ break;
+ default:
+ err("Invalid persona op: %d", persona_op);
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * spawn_persona.c
+ * Use new POSIX spawn attributes to create a new process in a persona
+ *
+ * Jeremy C. Andrus <jeremy_andrus@apple.com>
+ *
+ */
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/task.h>
+#include <mach/vm_param.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "persona_test.h"
+
+/* internal */
+#include <libproc.h>
+#include <spawn_private.h>
+#include <sys/persona.h>
+#include <sys/proc_info.h>
+#include <sys/spawn_internal.h>
+
+#define PERSONA_TEST_NAME "Persona Spawn"
+#define PERSONA_TEST_VMAJOR 0
+#define PERSONA_TEST_VMINOR 1
+
+static struct test_config {
+ int verbose;
+ int wait_for_children;
+} g;
+
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ *
+ * Child Management
+ *
+ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ */
+struct child {
+ TAILQ_ENTRY(child) sibling;
+ int pid;
+};
+
+static pthread_mutex_t g_child_mtx;
+static TAILQ_HEAD(, child) g_children = TAILQ_HEAD_INITIALIZER(g_children);
+static int g_nchildren = 0;
+
+static pid_t spawn_child(int argc, char **argv, struct persona_args *pa)
+{
+ int ret;
+ uint32_t persona_flags = 0;
+ posix_spawnattr_t attr;
+ struct child *child = NULL;
+ extern char **environ;
+
+ (void)argc;
+
+ if (!pa) {
+ err_print("Invalid persona args!");
+ return -ERR_SYSTEM;
+ }
+
+ if (!pa->flags & PA_HAS_ID) {
+ err_print("No persona ID specified!");
+ return -ERR_SYSTEM;
+ }
+
+ if (g.verbose) {
+ dump_persona_args("Spawning new child with args: ", pa);
+ infov("\t prog: \"%s\"", argv[0]);
+ for (int i = 1; i < argc; i++) {
+ infov("\t arg[%d]: %s", i, argv[i]);
+ }
+ }
+
+ child = (struct child *)calloc(1, sizeof(*child));
+ if (!child) {
+ err_print("No memory left :-(");
+ return -ERR_SYSTEM;
+ }
+
+ ret = posix_spawnattr_init(&attr);
+ if (ret != 0) {
+ err_print("posix_spawnattr_init");
+ ret = -ERR_SPAWN_ATTR;
+ goto out_err;
+ }
+
+ if (pa->flags & PA_SHOULD_VERIFY)
+ persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
+
+ if (pa->flags & PA_OVERRIDE)
+ persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE;
+
+ ret = posix_spawnattr_set_persona_np(&attr, pa->kinfo.persona_id, persona_flags);
+ if (ret != 0) {
+ err_print("posix_spawnattr_set_persona_np failed!");
+ ret = -ERR_SPAWN_ATTR;
+ goto out_err;
+ }
+
+ if (pa->flags & PA_HAS_UID) {
+ ret = posix_spawnattr_set_persona_uid_np(&attr, pa->override_uid);
+ if (ret != 0) {
+ err_print("posix_spawnattr_set_persona_uid_np failed!");
+ ret = -ERR_SPAWN_ATTR;
+ goto out_err;
+ }
+ }
+
+ if (pa->flags & PA_HAS_GID) {
+ ret = posix_spawnattr_set_persona_gid_np(&attr, pa->kinfo.persona_gid);
+ if (ret != 0) {
+ err_print("posix_spawnattr_set_persona_gid_np failed!");
+ ret = -ERR_SPAWN_ATTR;
+ goto out_err;
+ }
+ }
+
+ ret = posix_spawn(&child->pid, argv[0], NULL, &attr, argv, environ);
+ if (ret != 0) {
+ err_print("posix_spawn (ret=%d)", ret);
+ ret = -ERR_SPAWN;
+ goto out_err;
+ }
+
+ infov("\tspawned child PID: %d", child->pid);
+
+ /* link the processes onto the global children list */
+ pthread_mutex_lock(&g_child_mtx);
+ TAILQ_INSERT_TAIL(&g_children, child, sibling);
+ ++g_nchildren;
+ pthread_mutex_unlock(&g_child_mtx);
+
+ posix_spawnattr_destroy(&attr);
+ return child->pid;
+
+out_err:
+ posix_spawnattr_destroy(&attr);
+ free(child);
+ return (pid_t)ret;
+}
+
+
+static int child_should_exit = 0;
+
+static void child_sighandler(int sig)
+{
+ (void)sig;
+ dbg("PID: %d received sig %d", getpid(), sig);
+ child_should_exit = 1;
+}
+
+static int child_main_loop(int argc, char **argv)
+{
+ char ch;
+ sigset_t sigset;
+ int err = 0;
+ uid_t persona_id = 0;
+ struct kpersona_info kinfo;
+ int rval = 0;
+
+ while ((ch = getopt(argc, argv, "vhER")) != -1) {
+ switch (ch) {
+ case 'v':
+ g.verbose = 1;
+ break;
+ case 'E':
+ child_should_exit = 1;
+ break;
+ case 'R':
+ rval = 1;
+ break;
+ case 'h':
+ case '?':
+ case ':':
+ default:
+ err("Invalid child process invocation.");
+ }
+ }
+
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGINT);
+ sigaddset(&sigset, SIGHUP);
+ sigaddset(&sigset, SIGTERM);
+ sigaddset(&sigset, SIGABRT);
+ sigaddset(&sigset, SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+
+ signal(SIGINT, child_sighandler);
+ signal(SIGHUP, child_sighandler);
+ signal(SIGTERM, child_sighandler);
+ signal(SIGABRT, child_sighandler);
+ signal(SIGCHLD, child_sighandler);
+
+ err = kpersona_get(&persona_id);
+
+ info("Child: PID:%d", getpid());
+ info("Child: UID:%d, GID:%d", getuid(), getgid());
+ info("Child: login:%s", getlogin());
+ info("Child: Persona: %d (err:%d)", persona_id, err);
+
+ kinfo.persona_info_version = PERSONA_INFO_V1;
+ err = kpersona_info(persona_id, &kinfo);
+ if (err == 0)
+ dump_kpersona("Child: kpersona_info", &kinfo);
+ else
+ info("Child: ERROR grabbing kpersona_info: %d", errno);
+
+ if (child_should_exit)
+ return rval;
+
+ infov("Child Sleeping!");
+ while (!child_should_exit)
+ sleep(1);
+
+ infov("Child exiting!");
+ return rval;
+}
+
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ *
+ * Main Entry Point
+ *
+ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ */
+static void main_sighandler(int sig)
+{
+ dbg("PID: %d received sig %d", getpid(), sig);
+ if (sig == SIGCHLD) {
+ --g_nchildren;
+ }
+}
+
+static void usage_main(const char *progname, int verbose)
+{
+ const char *nm = basename((char *)progname);
+
+ printf("%s v%d.%d\n", PERSONA_TEST_NAME, PERSONA_TEST_VMAJOR, PERSONA_TEST_VMINOR);
+ printf("usage: %s [-I id] [-V] [-u uid] [-g gid] [-vw] progname [args...]\n", nm);
+ printf(" Spawn a new process into a new or existing persona.\n");
+ if (!verbose)
+ exit(1);
+
+ printf("\t%-10s\tID of the persona\n", "-I id");
+ printf("\t%-10s\tVerify persona parameters against existing persona (given by -I)\n", "-V");
+ printf("\t%-10s\tOverride/verify the user ID of the new process\n", "-u uid");
+ printf("\t%-10s\tOverride/verify the group ID of the new process\n", "-g gid");
+ printf("\t%-10s\tBe verbose\n", "-v");
+ printf("\t%-10s\tDo not wait for the child process\n", "-w");
+ printf("\n");
+
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ char ch;
+ int ret;
+
+ pthread_mutex_init(&g_child_mtx, NULL);
+
+ /*
+ * Defaults
+ */
+ g.verbose = 0;
+ g.wait_for_children = 1;
+
+ if (argc > 1 && strcmp(argv[1], "child") == 0) {
+ optind = 2;
+ ret = child_main_loop(argc, argv);
+ if (ret != 1)
+ exit(0);
+
+ /*
+ * If we get here, then the child wants us to continue running
+ * to potentially spawn yet another child process. This is
+ * helpful when testing inherited personas and verifying
+ * persona restrictions.
+ */
+ }
+
+ if (geteuid() != 0)
+ err("%s must be run as root", argv[0] ? basename(argv[0]) : PERSONA_TEST_NAME);
+
+ struct persona_args pa;
+ memset(&pa, 0, sizeof(pa));
+
+ pa.flags = PA_NONE;
+ pa.kinfo.persona_id = getuid();
+
+ /*
+ * Argument parse for default overrides:
+ */
+ while ((ch = getopt(argc, argv, "Vg:I:u:vwh")) != -1) {
+ switch (ch) {
+ case 'V':
+ pa.flags |= PA_SHOULD_VERIFY;
+ break;
+ case 'g':
+ pa.kinfo.persona_gid = atoi(optarg);
+ if (pa.kinfo.persona_gid <= 500)
+ err("Invalid GID: %d", pa.kinfo.persona_gid);
+ pa.flags |= PA_HAS_GID;
+ pa.flags |= PA_OVERRIDE;
+ break;
+ case 'I':
+ pa.kinfo.persona_id = atoi(optarg);
+ if (pa.kinfo.persona_id == 0)
+ err("Invalid Persona ID: %s", optarg);
+ pa.flags |= PA_HAS_ID;
+ break;
+ case 'u':
+ pa.override_uid = atoi(optarg);
+ if (pa.override_uid <= 500)
+ err("Invalid UID: %d", pa.override_uid);
+ pa.flags |= PA_HAS_UID;
+ pa.flags |= PA_OVERRIDE;
+ break;
+ case 'v':
+ g.verbose = 1;
+ break;
+ case 'w':
+ g.wait_for_children = 0;
+ break;
+ case 'h':
+ case '?':
+ usage_main(argv[0], 1);
+ case ':':
+ default:
+ printf("Invalid option: '%c'\n", ch);
+ usage_main(argv[0], 0);
+ }
+ }
+
+ if (pa.flags & PA_SHOULD_VERIFY)
+ pa.flags = ~PA_OVERRIDE;
+
+ if (optind >= argc) {
+ printf("No program given!\n");
+ usage_main(argv[0], 0);
+ }
+
+ argc -= optind;
+ for (int i = 0; i < argc; i++) {
+ argv[i] = argv[i + optind];
+ }
+
+ argv[argc] = NULL;
+
+ ret = spawn_child(argc, argv, &pa);
+ if (ret < 0)
+ return ret;
+
+ pid_t child_pid = (pid_t)ret;
+ int status = 0;
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+ signal(SIGCHLD, main_sighandler);
+
+ if (g.wait_for_children) {
+ infov("Waiting for child...");
+ waitpid(child_pid, &status, 0);
+ if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ if (status != 0)
+ errc(ERR_CHILD_FAIL,
+ "Child exited with status: %d", status);
+ }
+ }
+
+ info("Done.");
+ return 0;
+}
--- /dev/null
+/*
+ * persona_test.h
+ *
+ * Jeremy C. Andrus <jeremy_andrus@apple.com>
+ *
+ */
+#ifndef _PERSONA_TEST_H_
+#define _PERSONA_TEST_H_
+
+/* internal */
+#include <spawn_private.h>
+#include <sys/persona.h>
+#include <sys/spawn_internal.h>
+
+//#define DEBUG
+
+enum {
+ PA_NONE = 0x0000,
+ PA_CREATE = 0x0001,
+ PA_SHOULD_VERIFY = 0x0002,
+ PA_OVERRIDE = 0x0004,
+ PA_HAS_ID = 0x0100,
+ PA_HAS_TYPE = 0x0200,
+ PA_HAS_UID = 0x0400,
+ PA_HAS_GID = 0x0800,
+ PA_HAS_GROUPS = 0x1000,
+ PA_HAS_LOGIN = 0x2000,
+};
+
+struct persona_args {
+ uint32_t flags;
+ uid_t override_uid;
+ struct kpersona_info kinfo;
+};
+
+
+/*
+ * Error codes emitted on failure
+ */
+#define ERR_SYSTEM -1
+#define ERR_SPAWN 30
+#define ERR_SPAWN_ATTR 31
+#define ERR_CHILD_FAIL 40
+#define ERR_ARGS 98
+#define ERR_SETUP 99
+
+#define err(fmt, ...) \
+ do { \
+ fflush(NULL); \
+ fprintf(stderr, "[%4d] [ERROR(%d:%s)] %s:%d: " fmt "\n", \
+ getuid(), errno, strerror(errno), \
+ __func__, __LINE__, ## __VA_ARGS__ ); \
+ fflush(stderr); \
+ exit(ERR_SYSTEM); \
+ } while (0)
+
+#define errc(code, fmt, ...) \
+ do { \
+ fflush(NULL); \
+ fprintf(stderr, "[%4d] [ERROR(%d)] %s:%d: " fmt "\n", \
+ getuid(), code, \
+ __func__, __LINE__, ## __VA_ARGS__ ); \
+ fflush(stderr); \
+ exit(code ? code : ERR_SYSTEM); \
+ } while (0)
+
+#define err_print(fmt, ...) \
+ do { \
+ fflush(NULL); \
+ fprintf(stderr, "[%4d] [ERROR(%d:%s)] %s:%d: " fmt "\n", \
+ getuid(), errno, strerror(errno), \
+ __func__, __LINE__, ## __VA_ARGS__ ); \
+ fflush(stderr); \
+ } while (0)
+
+
+#define err__start(fmt, ...) \
+ do { \
+ fprintf(stderr, "[%4d] [ERROR] " fmt, getuid(), ## __VA_ARGS__); \
+ fflush(stderr); \
+ } while (0)
+
+#define err__cont(fmt, ...) \
+ do { \
+ fprintf(stderr, fmt, ## __VA_ARGS__); \
+ fflush(stderr); \
+ } while (0)
+
+#define err__finish(fmt, ...) \
+ do { \
+ fprintf(stderr, fmt "\n", ## __VA_ARGS__); \
+ fflush(stderr); \
+ } while (0)
+
+
+#ifdef DEBUG
+#define dbg(fmt, ...) \
+ do { \
+ fprintf(stdout, "[%4d] [DEBUG] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+ fflush(NULL); \
+ } while (0)
+#define warn(fmt, ...) \
+ do { \
+ fprintf(stdout, "[%4d] [WARN ] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+ fflush(NULL); \
+ } while (0)
+#else
+#define dbg(...)
+#define warn(...)
+#endif
+
+#define info(fmt, ...) \
+ do { \
+ fprintf(stdout, "[%4d] [INFO ] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+ fflush(NULL); \
+ } while (0)
+
+#define info_start(fmt, ...) \
+ do { \
+ fprintf(stdout, "[%4d] [INFO ] " fmt, getuid(), ## __VA_ARGS__ ); \
+ } while (0)
+
+#define info_cont(fmt, ...) \
+ do { \
+ fprintf(stdout, fmt, ## __VA_ARGS__ ); \
+ } while (0)
+
+#define info_end() \
+ do { \
+ fprintf(stdout, "\n"); \
+ fflush(NULL); \
+ } while (0)
+
+#define infov(fmt, ...) \
+ if (g.verbose) { \
+ fprintf(stdout, "[%4d] [vINFO] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+ fflush(NULL); \
+ }
+
+#define ARRAY_SZ(a) \
+ (sizeof(a) / sizeof((a)[0]))
+
+
+static inline void _dump_kpersona(const char *msg, uint32_t flags, const struct kpersona_info *ki)
+{
+ if (msg)
+ info("%s", msg);
+ info("\t kpersona_info (v%d) {", ki->persona_info_version);
+ info("\t\t %cid: %d", flags & PA_HAS_ID ? '+' : '-', ki->persona_id);
+ info("\t\t %ctype: %d", flags & PA_HAS_TYPE ? '+' : '-', ki->persona_type);
+ info("\t\t %cgid: %d", flags & PA_HAS_GID ? '+' : '-', ki->persona_gid);
+
+ info_start("\t\t ngroups: %d", ki->persona_ngroups);
+ for (int i = 0; i < ki->persona_ngroups; i++) {
+ if (i == 0) info_cont(" {");
+ info_cont(" %d", ki->persona_groups[i]);
+ }
+ if (ki->persona_ngroups > 0)
+ info_cont(" }");
+ info_end();
+
+ info("\t\t %cgmuid: %d (0x%x)", flags & PA_HAS_GROUPS ? '+' : '-',
+ (int)ki->persona_gmuid, ki->persona_gmuid);
+ info("\t\t %clogin: \"%s\"", flags & PA_HAS_LOGIN ? '+' : '-', ki->persona_name);
+ info("\t }");
+}
+
+#define dump_kpersona(msg, ki) \
+ _dump_kpersona(msg, 0xffffffff, ki)
+
+static inline void dump_persona_args(const char *msg, const struct persona_args *pa)
+{
+ const struct kpersona_info *ki = &pa->kinfo;
+
+ if (msg)
+ info("%s", msg);
+ info("\t flags: 0x%x", pa->flags);
+ info("\t %cuid: %d", pa->flags & PA_HAS_UID ? '+' : '-', pa->override_uid);
+ _dump_kpersona(NULL, pa->flags, ki);
+}
+
+static int parse_groupspec(struct kpersona_info *kinfo, char *spec)
+{
+ int idx = 0;
+ int grp;
+ char *s, *e;
+
+ if (!spec)
+ return -1;
+ s = e = spec;
+ while (*s) {
+ int comma = 0;
+ e = s;
+ while (*e && *e != ',')
+ e++;
+ if (*e)
+ comma = 1;
+ *e = 0;
+ grp = atoi(s);
+ if (comma) {
+ *e = ',';
+ s = e + 1;
+ } else {
+ s = e;
+ }
+ if (grp < 0)
+ return -1;
+ kinfo->persona_groups[idx] = grp;
+ idx++;
+ }
+ kinfo->persona_ngroups = idx;
+
+ return 0;
+}
+
+#endif /* _PERSONA_TEST_H_ */