From 490019cf9519204c5fb36b2fba54ceb983bb6b72 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 13 Jul 2016 22:52:56 +0000 Subject: [PATCH] xnu-3248.40.184.tar.gz --- bsd/bsm/audit_kevents.h | 2 + bsd/conf/files | 5 + bsd/hfs/hfs_quota.c | 18 +- bsd/kern/bsd_init.c | 4 + bsd/kern/kdebug.c | 10 +- bsd/kern/kern_credential.c | 75 +- bsd/kern/kern_cs.c | 4 + bsd/kern/kern_csr.c | 14 +- bsd/kern/kern_descrip.c | 5 +- bsd/kern/kern_event.c | 3 +- bsd/kern/kern_exec.c | 337 ++++- bsd/kern/kern_exit.c | 13 +- bsd/kern/kern_fork.c | 23 +- bsd/kern/kern_memorystatus.c | 60 +- bsd/kern/kern_persona.c | 1138 +++++++++++++++++ bsd/kern/kern_proc.c | 34 +- bsd/kern/kern_prot.c | 15 +- bsd/kern/kern_shutdown.c | 11 +- bsd/kern/kern_xxx.c | 19 + bsd/kern/kpi_socket.c | 8 +- bsd/kern/kpi_socketfilter.c | 64 +- bsd/kern/mach_loader.c | 120 +- bsd/kern/mach_loader.h | 6 +- bsd/kern/posix_sem.c | 166 ++- bsd/kern/posix_shm.c | 188 +-- bsd/kern/sys_persona.c | 369 ++++++ bsd/kern/syscalls.master | 6 +- bsd/kern/sysv_msg.c | 8 +- bsd/kern/trace.codes | 7 +- bsd/kern/ubc_subr.c | 169 ++- bsd/kern/uipc_socket.c | 28 +- bsd/kern/uipc_socket2.c | 2 +- bsd/kern/uipc_syscalls.c | 17 +- bsd/kern/uipc_usrreq.c | 18 +- bsd/miscfs/Makefile | 2 + bsd/miscfs/routefs/Makefile | 30 + bsd/miscfs/routefs/routefs.h | 67 + bsd/miscfs/routefs/routefs_ops.c | 562 ++++++++ bsd/net/dlil.c | 6 +- bsd/net/if.c | 4 +- bsd/net/if_var.h | 12 +- bsd/net/kpi_interface.c | 9 + bsd/net/necp.c | 46 +- bsd/net/ntstat.c | 154 ++- bsd/net/ntstat.h | 14 + bsd/net/pf.c | 149 ++- bsd/net/pf_norm.c | 4 +- bsd/netinet/flow_divert.c | 560 +++++--- bsd/netinet/flow_divert.h | 7 +- bsd/netinet/flow_divert_proto.h | 3 + bsd/netinet/in.c | 7 +- bsd/netinet/in_pcblist.c | 4 +- bsd/netinet/ip_output.c | 5 + bsd/netinet/mp_pcb.c | 14 +- bsd/netinet/mp_proto.c | 3 +- bsd/netinet/mptcp.c | 24 +- bsd/netinet/mptcp_opt.c | 37 +- bsd/netinet/mptcp_subr.c | 369 ++++-- bsd/netinet/mptcp_usrreq.c | 114 +- bsd/netinet/mptcp_var.h | 6 +- bsd/netinet/tcp_input.c | 7 +- bsd/netinet/tcp_output.c | 34 +- bsd/netinet/tcp_subr.c | 77 +- bsd/netinet/tcp_timer.c | 7 +- bsd/netinet/tcp_usrreq.c | 59 +- bsd/netinet/tcp_var.h | 9 +- bsd/netinet6/esp_input.c | 15 +- bsd/netinet6/ip6_forward.c | 6 + bsd/netinet6/ipsec.c | 41 +- bsd/netinet6/nd6_rtr.c | 83 -- bsd/netinet6/udp6_usrreq.c | 10 +- bsd/netkey/key.c | 8 +- bsd/sys/Makefile | 2 + bsd/sys/codedir_internal.h | 6 - bsd/sys/codesign.h | 10 +- bsd/sys/csr.h | 12 +- bsd/sys/fsctl.h | 5 +- bsd/sys/imgact.h | 2 +- bsd/sys/kdebug.h | 5 + bsd/sys/kern_memorystatus.h | 2 + bsd/sys/mount.h | 2 +- bsd/sys/mount_internal.h | 2 +- bsd/sys/persona.h | 386 ++++++ bsd/sys/proc.h | 7 +- bsd/sys/proc_internal.h | 5 + bsd/sys/reboot.h | 9 + bsd/sys/socket.h | 1 + bsd/sys/socketvar.h | 11 +- bsd/sys/spawn_internal.h | 41 +- bsd/sys/ubc_internal.h | 3 +- bsd/sys/vnode.h | 6 +- bsd/vfs/vfs_cache.c | 8 +- bsd/vfs/vfs_conf.c | 6 + bsd/vfs/vfs_subr.c | 4 + bsd/vfs/vfs_syscalls.c | 34 +- bsd/vm/vm_unix.c | 3 +- config/MASTER | 7 + config/MASTER.x86_64 | 11 +- config/MasterVersion | 2 +- config/Private.exports | 9 + config/Private.x86_64.exports | 1 - iokit/IOKit/IOKitKeysPrivate.h | 2 + iokit/IOKit/IOPolledInterface.h | 9 +- iokit/IOKit/IOService.h | 2 +- iokit/IOKit/IOUserClient.h | 9 +- iokit/Kernel/IOCPU.cpp | 36 +- iokit/Kernel/IODeviceTreeSupport.cpp | 8 +- iokit/Kernel/IOMemoryDescriptor.cpp | 6 +- iokit/Kernel/IOPMrootDomain.cpp | 21 +- iokit/Kernel/IOPolledInterface.cpp | 33 +- iokit/Kernel/IOService.cpp | 89 +- iokit/Kernel/IOUserClient.cpp | 68 +- iokit/bsddev/IOKitBSDInit.cpp | 2 +- libkern/Makefile | 20 +- libkern/os/Makefile | 42 + libkern/os/overflow.h | 111 ++ .../Libsyscall.xcodeproj/project.pbxproj | 4 + libsyscall/mach/host.c | 16 + libsyscall/wrappers/persona.c | 91 ++ libsyscall/wrappers/spawn/posix_spawn.c | 150 ++- libsyscall/wrappers/spawn/spawn_private.h | 5 + osfmk/Makefile | 2 + osfmk/atm/atm.c | 4 + osfmk/bank/bank.c | 428 ++++++- osfmk/bank/bank_internal.h | 37 +- osfmk/bank/bank_types.h | 23 +- osfmk/console/Makefile | 9 +- osfmk/console/i386/serial_console.c | 7 +- osfmk/console/video_console.c | 203 +-- osfmk/console/video_console.h | 40 +- osfmk/device/device.defs | 2 +- osfmk/device/device_types.h | 1 + osfmk/device/iokit_rpc.c | 5 +- osfmk/i386/acpi.c | 26 +- osfmk/i386/i386_init.c | 2 +- osfmk/i386/mp.c | 12 +- osfmk/i386/vmx/vmx_cpu.c | 35 +- osfmk/i386/vmx/vmx_cpu.h | 2 +- osfmk/ipc/ipc_importance.c | 4 + osfmk/ipc/ipc_kmsg.c | 2 + osfmk/ipc/ipc_voucher.c | 221 +++- osfmk/ipc/ipc_voucher.h | 15 +- osfmk/ipc/mach_msg.c | 4 + osfmk/ipc/mach_port.c | 3 + osfmk/kern/assert.h | 8 +- osfmk/kern/coalition.c | 4 +- osfmk/kern/host.c | 11 + osfmk/kern/ipc_mig.c | 60 +- osfmk/kern/ipc_sync.c | 8 +- osfmk/kern/locks.c | 8 +- osfmk/kern/machine.c | 44 +- osfmk/kern/priority.c | 26 +- osfmk/kern/processor_data.h | 6 +- osfmk/kern/sched.h | 4 +- osfmk/kern/sched_average.c | 8 +- osfmk/kern/sched_prim.c | 23 +- osfmk/kern/startup.c | 4 + osfmk/kern/syscall_subr.c | 16 + osfmk/kern/task.c | 39 +- osfmk/kern/task.h | 2 + osfmk/kern/thread.c | 18 +- osfmk/kern/thread.h | 3 + osfmk/kern/thread_act.c | 8 + osfmk/kern/thread_policy.c | 16 +- osfmk/mach/mach_host.defs | 23 + osfmk/mach/mach_voucher_types.h | 5 + osfmk/mach/machine.h | 1 + osfmk/mach/message.h | 18 +- osfmk/vm/vm_apple_protect.c | 2 +- osfmk/vm/vm_compressor_backing_store.c | 1 - osfmk/vm/vm_fault.c | 10 +- osfmk/vm/vm_map.c | 2 + osfmk/vm/vm_resident.c | 6 + osfmk/x86_64/pmap.c | 14 +- tools/lldbmacros/bank.py | 20 +- tools/lldbmacros/ipc.py | 2 + tools/tests/MPMMTest/KQMPMMtest.c | 5 +- tools/tests/MPMMTest/MPMMtest.c | 5 +- tools/tests/MPMMTest/MPMMtest_run.sh | 36 + tools/tests/MPMMTest/Makefile | 6 +- tools/tests/Makefile.common | 8 + tools/tests/execperf/Makefile | 14 +- tools/tests/execperf/test.sh | 8 +- tools/tests/personas/Makefile | 37 + tools/tests/personas/persona_mgr.c | 291 +++++ tools/tests/personas/persona_spawn.c | 389 ++++++ tools/tests/personas/persona_test.h | 216 ++++ 187 files changed, 7857 insertions(+), 1525 deletions(-) create mode 100644 bsd/kern/kern_persona.c create mode 100644 bsd/kern/sys_persona.c create mode 100644 bsd/miscfs/routefs/Makefile create mode 100644 bsd/miscfs/routefs/routefs.h create mode 100644 bsd/miscfs/routefs/routefs_ops.c create mode 100644 bsd/sys/persona.h create mode 100644 libkern/os/Makefile create mode 100644 libkern/os/overflow.h create mode 100644 libsyscall/wrappers/persona.c create mode 100755 tools/tests/MPMMTest/MPMMtest_run.sh create mode 100644 tools/tests/personas/Makefile create mode 100644 tools/tests/personas/persona_mgr.c create mode 100644 tools/tests/personas/persona_spawn.c create mode 100644 tools/tests/personas/persona_test.h diff --git a/bsd/bsm/audit_kevents.h b/bsd/bsm/audit_kevents.h index e6ea2a05b..eb75536e2 100644 --- a/bsd/bsm/audit_kevents.h +++ b/bsd/bsm/audit_kevents.h @@ -809,5 +809,7 @@ #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_ */ diff --git a/bsd/conf/files b/bsd/conf/files index 2e3baa92b..190d2964b 100644 --- a/bsd/conf/files +++ b/bsd/conf/files @@ -91,6 +91,7 @@ OPTIONS/hfs optional hfs 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 @@ -187,6 +188,8 @@ bsd/miscfs/mockfs/mockfs_fsnode.c optional mockfs 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 @@ -453,6 +456,7 @@ bsd/kern/kern_fork.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 @@ -483,6 +487,7 @@ bsd/kern/sys_pipe.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 diff --git a/bsd/hfs/hfs_quota.c b/bsd/hfs/hfs_quota.c index 989bd67bf..10a9e4e8b 100644 --- a/bsd/hfs/hfs_quota.c +++ b/bsd/hfs/hfs_quota.c @@ -215,7 +215,7 @@ hfs_chkdq(cp, change, cred, flags) /* * 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) @@ -227,7 +227,11 @@ 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; @@ -242,6 +246,7 @@ hfs_chkdqchg(cp, change, cred, type) quotatypes[type]); #endif dq->dq_flags |= DQ_BLKS; + vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL); } dqunlock(dq); @@ -263,6 +268,7 @@ hfs_chkdqchg(cp, change, cred, type) 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); @@ -276,6 +282,7 @@ hfs_chkdqchg(cp, change, cred, type) "disk quota exceeded for too long"); #endif dq->dq_flags |= DQ_BLKS; + vfs_event_signal(&fsid, VQ_QUOTA, (intptr_t)NULL); } dqunlock(dq); @@ -374,6 +381,10 @@ int hfs_isiqchg_allowed(dq, hfsmp, change, cred, type, uid) { 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; @@ -384,6 +395,7 @@ int hfs_isiqchg_allowed(dq, hfsmp, change, cred, type, uid) 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); @@ -399,6 +411,7 @@ int hfs_isiqchg_allowed(dq, hfsmp, change, cred, type, uid) 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); } @@ -406,6 +419,7 @@ int hfs_isiqchg_allowed(dq, hfsmp, change, cred, type, uid) 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); diff --git a/bsd/kern/bsd_init.c b/bsd/kern/bsd_init.c index 4f871df32..8cf33e20a 100644 --- a/bsd/kern/bsd_init.c +++ b/bsd/kern/bsd_init.c @@ -546,6 +546,10 @@ bsd_init(void) 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; diff --git a/bsd/kern/kdebug.c b/bsd/kern/kdebug.c index 505fbf81d..556e0d0bb 100644 --- a/bsd/kern/kdebug.c +++ b/bsd/kern/kdebug.c @@ -991,12 +991,12 @@ out1: 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); } } diff --git a/bsd/kern/kern_credential.c b/bsd/kern/kern_credential.c index 0d2a07e02..0558ce449 100644 --- a/bsd/kern/kern_credential.c +++ b/bsd/kern/kern_credential.c @@ -2073,14 +2073,66 @@ static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst); #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 @@ -3159,11 +3211,11 @@ kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp *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 @@ -3190,7 +3242,7 @@ kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp 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 @@ -3207,13 +3259,12 @@ kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp 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); diff --git a/bsd/kern/kern_cs.c b/bsd/kern/kern_cs.c index fb3a2012c..c15dd4f11 100644 --- a/bsd/kern/kern_cs.c +++ b/bsd/kern/kern_cs.c @@ -175,6 +175,10 @@ cs_allow_invalid(struct proc *p) 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 diff --git a/bsd/kern/kern_csr.c b/bsd/kern/kern_csr.c index 4b5c26815..1805b76e7 100644 --- a/bsd/kern/kern_csr.c +++ b/bsd/kern/kern_csr.c @@ -2,7 +2,7 @@ * 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 @@ -11,10 +11,10 @@ * 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, @@ -22,7 +22,7 @@ * 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@ */ @@ -85,12 +85,6 @@ csr_check(csr_config_t mask) return error; } -void -csr_set_allow_all(int value) -{ - csr_allow_all = !!value; // force value to 0 or 1 -} - /* * Syscall stubs */ diff --git a/bsd/kern/kern_descrip.c b/bsd/kern/kern_descrip.c index 62a9d94f7..e089c5363 100644 --- a/bsd/kern/kern_descrip.c +++ b/bsd/kern/kern_descrip.c @@ -2377,10 +2377,7 @@ fcntl_nocancel(proc_t p, struct fcntl_nocancel_args *uap, int32_t *retval) } } - 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; diff --git a/bsd/kern/kern_event.c b/bsd/kern/kern_event.c index d80579a35..6bc84137c 100644 --- a/bsd/kern/kern_event.c +++ b/bsd/kern/kern_event.c @@ -906,6 +906,7 @@ filt_timerattach(struct knote *kn) error = filt_timervalidate(kn); if (error != 0) { filt_timerunlock(); + thread_call_free(callout); return (error); } @@ -1651,7 +1652,7 @@ kevent_internal(struct proc *p, struct kqueue *kq; struct fileproc *fp = NULL; struct kevent_internal_s kev; - int error, noutputs; + int error = 0, noutputs; struct timeval atv; #if 1 diff --git a/bsd/kern/kern_exec.c b/bsd/kern/kern_exec.c index e2e7d1526..20b1f0317 100644 --- a/bsd/kern/kern_exec.c +++ b/bsd/kern/kern_exec.c @@ -102,6 +102,7 @@ #include #include #include +#include #if SYSV_SHM #include /* shmexec() */ #endif @@ -132,6 +133,7 @@ #include #include #include +#include #if CONFIG_MACF #include @@ -195,6 +197,7 @@ void task_importance_update_owner_info(task_t); #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); @@ -694,6 +697,40 @@ bad: 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 * @@ -876,7 +913,7 @@ grade: /* * 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); @@ -888,7 +925,7 @@ grade: 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 @@ -912,15 +949,11 @@ grade: 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. */ @@ -931,8 +964,28 @@ grade: */ 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. @@ -973,7 +1026,7 @@ grade: /* 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; @@ -997,8 +1050,6 @@ grade: if (vfexec || spawn) { vm_map_switch(old_map); } - /* Set the entry point */ - thread_setentrypoint(thread, load_result.entry_point); /* Stop profiling */ stopprofclock(p); @@ -1177,6 +1228,11 @@ done: thread_deallocate(thread); } + if (load_result.threadstate) { + kfree(load_result.threadstate, load_result.threadstate_sz); + load_result.threadstate = NULL; + } + bad: return(error); } @@ -1226,6 +1282,7 @@ struct execsw { * namei:??? * vn_rdwr:??? [anything vn_rdwr can return] * :??? [anything an imgact can return] + * EDEADLK Process is being terminated */ static int exec_activate_image(struct image_params *imgp) @@ -1276,6 +1333,7 @@ again: */ proc_lock(p); if (p->p_lflag & P_LEXIT) { + error = EDEADLK; proc_unlock(p); goto bad_notrans; } @@ -1375,6 +1433,17 @@ encapsulated_binary: (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); @@ -1883,6 +1952,126 @@ static inline void spawn_coalitions_release_all(coalition_t coal[COALITION_NUM_T } #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) { @@ -1980,6 +2169,9 @@ posix_spawn(proc_t ap, struct posix_spawn_args *uap, int32_t *retval) 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 @@ -2004,7 +2196,7 @@ posix_spawn(proc_t ap, struct posix_spawn_args *uap, int32_t *retval) 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) { @@ -2028,8 +2220,8 @@ posix_spawn(proc_t ap, struct posix_spawn_args *uap, int32_t *retval) 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; @@ -2099,7 +2291,29 @@ posix_spawn(proc_t ap, struct posix_spawn_args *uap, int32_t *retval) 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) @@ -2215,6 +2429,10 @@ posix_spawn(proc_t ap, struct posix_spawn_args *uap, int32_t *retval) 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 @@ -2234,6 +2452,30 @@ do_fork1: 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) { @@ -2350,6 +2592,21 @@ do_fork1: } } +#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. @@ -2571,11 +2828,24 @@ bad: } 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. @@ -2645,6 +2915,10 @@ bad: 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); @@ -2894,20 +3168,33 @@ __mac_execve(proc_t p, struct __mac_execve_args *uap, int32_t *retval) 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 @@ -3845,6 +4132,13 @@ handle_mac_transition: * 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, @@ -3856,6 +4150,7 @@ handle_mac_transition: imgp->ip_px_smpx, &disjoint_cred, /* will be non zero if disjoint */ &label_update_return); + thread_enable_send_importance(thread, FALSE); if (disjoint_cred) { /* diff --git a/bsd/kern/kern_exit.c b/bsd/kern/kern_exit.c index fca7ab329..d99430bd0 100644 --- a/bsd/kern/kern_exit.c +++ b/bsd/kern/kern_exit.c @@ -106,6 +106,9 @@ #include /* shmexit */ #endif #include /* acct_process */ +#if CONFIG_PERSONAS +#include +#endif #include #include @@ -471,7 +474,7 @@ exit1_internal(proc_t p, int rv, int *retval, boolean_t thread_can_terminate, bo 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 @@ -1316,7 +1319,13 @@ reap_child_locked(proc_t parent, proc_t child, int deadparent, int reparentedtoi * 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); /* diff --git a/bsd/kern/kern_fork.c b/bsd/kern/kern_fork.c index 7b3e2440e..e23d52ead 100644 --- a/bsd/kern/kern_fork.c +++ b/bsd/kern/kern_fork.c @@ -93,7 +93,9 @@ #include #include #include - +#if CONFIG_PERSONAS +#include +#endif #if CONFIG_DTRACE /* Do not include dtrace.h, it redefines kmem_[alloc/free] */ extern void dtrace_fasttrap_fork(proc_t, proc_t); @@ -394,7 +396,6 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind, coalition_t *coalit * 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) { @@ -671,6 +672,13 @@ fork1(proc_t parent_proc, thread_t *child_threadp, int kind, coalition_t *coalit } #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; @@ -1373,6 +1381,17 @@ retry: 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; diff --git a/bsd/kern/kern_memorystatus.c b/bsd/kern/kern_memorystatus.c index 97296a8a8..22f7edbd3 100644 --- a/bsd/kern/kern_memorystatus.c +++ b/bsd/kern/kern_memorystatus.c @@ -303,6 +303,23 @@ static uint32_t memorystatus_jld_eval_period_msecs = 0; /* Init pass sets this 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. @@ -3100,16 +3117,6 @@ memorystatus_kill_specific_process(pid_t victim_pid, uint32_t cause) { 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); } @@ -3338,12 +3345,15 @@ memorystatus_kill_top_process_aggressive(boolean_t any, uint32_t cause, int aggr 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); @@ -3456,6 +3466,8 @@ memorystatus_kill_top_process_aggressive(boolean_t any, uint32_t cause, int aggr 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? */ @@ -3472,6 +3484,17 @@ memorystatus_kill_top_process_aggressive(boolean_t any, uint32_t cause, int aggr 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; } @@ -5908,6 +5931,23 @@ memorystatus_control(struct proc *p __unused, struct memorystatus_control_args * 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: diff --git a/bsd/kern/kern_persona.c b/bsd/kern/kern_persona.c new file mode 100644 index 000000000..5638b792c --- /dev/null +++ b/bsd/kern/kern_persona.c @@ -0,0 +1,1138 @@ +/* + * 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 +#include +#include + +#if CONFIG_PERSONAS +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 -> ", 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 diff --git a/bsd/kern/kern_proc.c b/bsd/kern/kern_proc.c index 9213b82f3..bd8215326 100644 --- a/bsd/kern/kern_proc.c +++ b/bsd/kern/kern_proc.c @@ -110,6 +110,7 @@ #include #include #include +#include #if CONFIG_MEMORYSTATUS #include @@ -193,6 +194,9 @@ procinit(void) 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 } /* @@ -671,6 +675,12 @@ proc_selfppid(void) 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) @@ -940,6 +950,24 @@ proc_pidversion(proc_t p) 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) { @@ -1872,7 +1900,7 @@ csops_internal(pid_t pid, int ops, user_addr_t uaddr, user_size_t usersize, user 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; @@ -1927,7 +1955,7 @@ csops_internal(pid_t pid, int ops, user_addr_t uaddr, user_size_t usersize, user 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; @@ -1959,7 +1987,7 @@ csops_internal(pid_t pid, int ops, user_addr_t uaddr, user_size_t usersize, user 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; diff --git a/bsd/kern/kern_prot.c b/bsd/kern/kern_prot.c index e90c68c55..75980efdc 100644 --- a/bsd/kern/kern_prot.c +++ b/bsd/kern/kern_prot.c @@ -95,8 +95,7 @@ #include #include #include - -#define chgproccnt_ok(p) 1 +#include #include @@ -778,7 +777,7 @@ setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) * 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); } @@ -797,7 +796,7 @@ setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) * 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); @@ -816,7 +815,7 @@ setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval) * 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); } } @@ -1026,7 +1025,7 @@ setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) * 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); } @@ -1041,7 +1040,7 @@ setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) */ 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. @@ -1061,7 +1060,7 @@ setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval) 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 diff --git a/bsd/kern/kern_shutdown.c b/bsd/kern/kern_shutdown.c index 3858ce83e..59a44295f 100644 --- a/bsd/kern/kern_shutdown.c +++ b/bsd/kern/kern_shutdown.c @@ -84,6 +84,9 @@ static void sd_log(vfs_context_t, const char *, ...); 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; @@ -185,7 +188,13 @@ reboot_kernel(int howto, char *message) /* * 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++) { diff --git a/bsd/kern/kern_xxx.c b/bsd/kern/kern_xxx.c index dde94f5a2..0fe1cfa30 100644 --- a/bsd/kern/kern_xxx.c +++ b/bsd/kern/kern_xxx.c @@ -85,6 +85,9 @@ #include #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) { @@ -127,3 +130,19 @@ reboot(struct proc *p, register struct reboot_args *uap, __unused int32_t *retva } 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; +} diff --git a/bsd/kern/kpi_socket.c b/bsd/kern/kpi_socket.c index 09818e3b0..82a647586 100644 --- a/bsd/kern/kpi_socket.c +++ b/bsd/kern/kpi_socket.c @@ -950,7 +950,13 @@ sock_release(socket_t sock) __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 { diff --git a/bsd/kern/kpi_socketfilter.c b/bsd/kern/kpi_socketfilter.c index bdebcc182..1f35405f4 100644 --- a/bsd/kern/kpi_socketfilter.c +++ b/bsd/kern/kpi_socketfilter.c @@ -891,12 +891,9 @@ sflt_connectin(struct socket *so, const struct sockaddr *remote) 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; @@ -941,9 +938,40 @@ sflt_connectout(struct socket *so, const struct sockaddr *nam) } __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; @@ -964,20 +992,30 @@ sflt_connectxout(struct socket *so, struct sockaddr_list **dst_sl0) * 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) { diff --git a/bsd/kern/mach_loader.c b/bsd/kern/mach_loader.c index b5666e881..0d7ba242d 100644 --- a/bsd/kern/mach_loader.c +++ b/bsd/kern/mach_loader.c @@ -82,6 +82,8 @@ #include #include /* for kIOReturnNotPrivileged */ +#include + /* * XXX vm/pmap.h should not treat these prototypes as MACH_KERNEL_PRIVATE * when KERNEL is defined. @@ -89,8 +91,6 @@ 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); @@ -118,7 +118,9 @@ static load_result_t load_result_null = { .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 }; /* @@ -201,7 +203,8 @@ static load_return_t load_threadstate( thread_t thread, uint32_t *ts, - uint32_t total_size + uint32_t total_size, + load_result_t * ); static load_return_t @@ -296,7 +299,7 @@ load_machfile( struct image_params *imgp, struct mach_header *header, thread_t thread, - vm_map_t new_map, + vm_map_t *mapp, load_result_t *result ) { @@ -304,11 +307,9 @@ load_machfile( 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; @@ -326,7 +327,6 @@ load_machfile( if (new_map == VM_MAP_NULL) { create_map = TRUE; - old_task = current_task(); } /* @@ -337,7 +337,6 @@ load_machfile( */ if (spawn) { create_map = TRUE; - old_task = get_threadtask(thread); } if (create_map) { @@ -431,22 +430,13 @@ load_machfile( } } - /* - * 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 @@ -478,8 +468,7 @@ load_machfile( 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); } @@ -1527,13 +1516,6 @@ load_main( /* 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++; @@ -1604,13 +1586,12 @@ load_unixthread( 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++; @@ -1622,72 +1603,56 @@ load_return_t 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; } @@ -1916,7 +1881,14 @@ load_dylinker( } } - 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; diff --git a/bsd/kern/mach_loader.h b/bsd/kern/mach_loader.h index 5600cb42f..bcef8baa3 100644 --- a/bsd/kern/mach_loader.h +++ b/bsd/kern/mach_loader.h @@ -68,11 +68,13 @@ typedef struct _load_result { 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; @@ -80,7 +82,7 @@ load_return_t load_machfile( 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 diff --git a/bsd/kern/posix_sem.c b/bsd/kern/posix_sem.c index 42a12cb7e..1b166106c 100644 --- a/bsd/kern/posix_sem.c +++ b/bsd/kern/posix_sem.c @@ -124,6 +124,10 @@ struct psemcache { }; #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 */ @@ -175,6 +179,7 @@ static int psem_ioctl (struct fileproc *fp, u_long com, 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); @@ -196,9 +201,14 @@ static lck_mtx_t psx_sem_subsys_mutex; #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 ) @@ -231,7 +241,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp, if (pnp->psem_namelen > PSEMNAMLEN) { psemstats.longnames++; - return (0); + return PSEMCACHE_NOTFOUND; } pcpp = PSEMHASH(pnp); @@ -244,7 +254,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp, if (pcp == 0) { psemstats.miss++; - return (0); + return PSEMCACHE_NOTFOUND; } /* We found a "positive" match, return the vnode */ @@ -253,7 +263,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp, /* TOUCH(ncp); */ *psemp = pcp->pseminfo; *pcache = pcp; - return (-1); + return PSEMCACHE_FOUND; } /* @@ -261,7 +271,7 @@ psem_cache_search(struct pseminfo **psemp, struct psemname *pnp, * The nc_vpid field records whether this is a whiteout. */ psemstats.neghits++; - return (ENOENT); + return PSEMCACHE_NEGATIVE; } /* @@ -281,11 +291,11 @@ psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *p /* 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. @@ -307,7 +317,7 @@ psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *p } #endif LIST_INSERT_HEAD(pcpp, pcp, psem_hash); - return(0); + return 0; } /* @@ -333,27 +343,43 @@ psem_cache_delete(struct psemcache *pcp) 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) @@ -498,15 +524,15 @@ 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; @@ -661,6 +687,39 @@ psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred) 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) { @@ -668,11 +727,10 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret 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; @@ -692,12 +750,12 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret goto bad; } + nameptr = pnbuf; #ifdef PSXSEM_NAME_RESTRICT - nameptr = pnbuf; if (*nameptr == '/') { while (*(nameptr++) == '/') { - plen--; + pathlen--; error = EINVAL; goto bad; } @@ -707,31 +765,24 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret } #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) { @@ -744,37 +795,12 @@ sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *ret 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 diff --git a/bsd/kern/posix_shm.c b/bsd/kern/posix_shm.c index e14baf815..38faf2939 100644 --- a/bsd/kern/posix_shm.c +++ b/bsd/kern/posix_shm.c @@ -135,6 +135,10 @@ struct pshmcache { }; #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 */ @@ -184,13 +188,13 @@ static int pshm_closefile (struct fileglob *fg, vfs_context_t ctx); 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, @@ -210,6 +214,7 @@ static lck_mtx_t psx_shm_subsys_mutex; #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 */ @@ -244,7 +249,7 @@ pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp, if (pnp->pshm_namelen > PSHMNAMLEN) { pshmstats.longnames++; - return (0); + return PSHMCACHE_NOTFOUND; } pcpp = PSHMHASH(pnp); @@ -257,7 +262,7 @@ pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp, if (pcp == 0) { pshmstats.miss++; - return (0); + return PSHMCACHE_NOTFOUND; } /* We found a "positive" match, return the vnode */ @@ -268,14 +273,14 @@ pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp, *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; } /* @@ -296,8 +301,8 @@ pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *p /* 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++; @@ -318,7 +323,7 @@ pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *p } #endif LIST_INSERT_HEAD(pcpp, pcp, pshm_hash); - return(0); + return 0; } /* @@ -330,27 +335,44 @@ pshm_cache_init(void) 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) @@ -488,12 +510,12 @@ shm_open(proc_t p, struct shm_open_args *uap, int32_t *retval) 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) */ @@ -1006,24 +1028,72 @@ out: } +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 */ @@ -1039,12 +1109,12 @@ shm_unlink(__unused proc_t p, struct shm_unlink_args *uap, goto bad; } + nameptr = pnbuf; #ifdef PSXSHM_NAME_RESTRICT - nameptr = pnbuf; if (*nameptr == '/') { while (*(nameptr++) == '/') { - plen--; + pathlen--; error = EINVAL; goto bad; } @@ -1054,31 +1124,24 @@ shm_unlink(__unused proc_t p, struct shm_unlink_args *uap, } #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(); @@ -1098,6 +1161,7 @@ shm_unlink(__unused proc_t p, struct shm_unlink_args *uap, error = 0; goto bad; } + #if CONFIG_MACF error = mac_posixshm_check_unlink(kauth_cred_get(), pinfo, nameptr); if (error) { @@ -1110,46 +1174,20 @@ shm_unlink(__unused proc_t p, struct shm_unlink_args *uap, 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 */ diff --git a/bsd/kern/sys_persona.c b/bsd/kern/sys_persona.c new file mode 100644 index 000000000..3272922f7 --- /dev/null +++ b/bsd/kern/sys_persona.c @@ -0,0 +1,369 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +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; +} diff --git a/bsd/kern/syscalls.master b/bsd/kern/syscalls.master index 066065c6a..faf5af9d4 100644 --- a/bsd/kern/syscalls.master +++ b/bsd/kern/syscalls.master @@ -683,7 +683,7 @@ 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); } @@ -781,7 +781,11 @@ 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); } diff --git a/bsd/kern/sysv_msg.c b/bsd/kern/sysv_msg.c index daca44630..cc35787f8 100644 --- a/bsd/kern/sysv_msg.c +++ b/bsd/kern/sysv_msg.c @@ -558,11 +558,11 @@ msgctl(struct proc *p, struct msgctl_args *uap, int32_t *retval) 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)); } @@ -1468,8 +1468,8 @@ IPCS_msg_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, 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; diff --git a/bsd/kern/trace.codes b/bsd/kern/trace.codes index b37035dfe..c03b66bc5 100644 --- a/bsd/kern/trace.codes +++ b/bsd/kern/trace.codes @@ -264,6 +264,9 @@ 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 @@ -1305,7 +1308,7 @@ 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 @@ -2091,6 +2094,8 @@ 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 diff --git a/bsd/kern/ubc_subr.c b/bsd/kern/ubc_subr.c index 7d7fbe7c6..3e4353a97 100644 --- a/bsd/kern/ubc_subr.c +++ b/bsd/kern/ubc_subr.c @@ -150,10 +150,9 @@ typedef void (*cs_md_update)(void *ctx, const void *data, size_t size); 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; @@ -161,7 +160,6 @@ struct cs_hash { 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, @@ -171,7 +169,6 @@ static struct cs_hash cs_hash_sha1 = { #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, @@ -180,13 +177,20 @@ static struct cs_hash cs_hash_sha256 = { }; 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 * @@ -199,6 +203,8 @@ cs_find_md(uint8_t type) 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; @@ -207,65 +213,31 @@ cs_find_md(uint8_t type) 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 */ } @@ -394,10 +366,9 @@ cs_validate_codedirectory(const CS_CodeDirectory *cd, size_t length) 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; @@ -512,12 +483,17 @@ cs_validate_csblob(const uint8_t *addr, size_t length, 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; @@ -525,25 +501,40 @@ cs_validate_csblob(const uint8_t *addr, size_t length, /* 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) { @@ -643,8 +634,7 @@ csblob_get_entitlements(struct cs_blob *csblob, void **out_start, size_t *out_le 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); @@ -653,8 +643,12 @@ csblob_get_entitlements(struct cs_blob *csblob, void **out_start, size_t *out_le 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); @@ -2718,9 +2712,7 @@ csblob_parse_teamid(struct cs_blob *csblob) { 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; @@ -2858,15 +2850,13 @@ ubc_cs_blob_add( 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)); @@ -3373,11 +3363,10 @@ cs_validate_page( 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 */ @@ -3430,12 +3419,10 @@ cs_validate_page( } 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 */ @@ -3458,7 +3445,7 @@ cs_validate_page( 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; } @@ -3503,7 +3490,7 @@ cs_validate_page( 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: " diff --git a/bsd/kern/uipc_socket.c b/bsd/kern/uipc_socket.c index ede272ba6..29568b48a 100644 --- a/bsd/kern/uipc_socket.c +++ b/bsd/kern/uipc_socket.c @@ -1579,6 +1579,8 @@ soconnectxlocked(struct socket *so, struct sockaddr_list **src_sl, */ 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 { @@ -5087,6 +5089,20 @@ sosetoptlock(struct socket *so, struct sockopt *sopt, int dolock) 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; @@ -5499,7 +5515,10 @@ integer: 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; @@ -6804,14 +6823,9 @@ sockaddrentry_dup(const struct sockaddr_entry *src_se, int how) 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 { diff --git a/bsd/kern/uipc_socket2.c b/bsd/kern/uipc_socket2.c index f8b94b904..40e7f1919 100644 --- a/bsd/kern/uipc_socket2.c +++ b/bsd/kern/uipc_socket2.c @@ -1839,7 +1839,7 @@ sbdrop(struct sockbuf *sb, int len) ((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); diff --git a/bsd/kern/uipc_syscalls.c b/bsd/kern/uipc_syscalls.c index 03330fbcb..beee6c37f 100644 --- a/bsd/kern/uipc_syscalls.c +++ b/bsd/kern/uipc_syscalls.c @@ -2756,7 +2756,6 @@ getsockaddr(struct socket *so, struct sockaddr **namp, user_addr_t uaddr, { struct sockaddr *sa; int error; - size_t alloclen; if (len > SOCK_MAXADDRLEN) return (ENAMETOOLONG); @@ -2764,12 +2763,7 @@ getsockaddr(struct socket *so, struct sockaddr **namp, user_addr_t uaddr, 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); } @@ -2845,7 +2839,8 @@ getsockaddrlist(struct socket *so, struct sockaddr_list **slp, *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); @@ -2876,7 +2871,7 @@ getsockaddrlist(struct socket *so, struct sockaddr_list **slp, } 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; @@ -2884,8 +2879,10 @@ getsockaddrlist(struct socket *so, struct sockaddr_list **slp, } se = sockaddrentry_alloc(M_WAITOK); - if (se == NULL) + if (se == NULL) { + error = ENOBUFS; break; + } sockaddrlist_insert(sl, se); diff --git a/bsd/kern/uipc_usrreq.c b/bsd/kern/uipc_usrreq.c index cfe63ef28..f94c740fd 100644 --- a/bsd/kern/uipc_usrreq.c +++ b/bsd/kern/uipc_usrreq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Apple Inc. All rights reserved. + * Copyright (c) 2000-2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -992,10 +992,15 @@ unp_bind( 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 */ @@ -1101,8 +1106,13 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) 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, diff --git a/bsd/miscfs/Makefile b/bsd/miscfs/Makefile index 1d54c5b95..d052d38c7 100644 --- a/bsd/miscfs/Makefile +++ b/bsd/miscfs/Makefile @@ -10,12 +10,14 @@ include $(MakeInc_def) INSTINC_SUBDIRS = \ devfs \ fifofs \ + routefs \ specfs \ union EXPINC_SUBDIRS = \ devfs \ fifofs \ + routefs \ specfs \ union diff --git a/bsd/miscfs/routefs/Makefile b/bsd/miscfs/routefs/Makefile new file mode 100644 index 000000000..1076c57a3 --- /dev/null +++ b/bsd/miscfs/routefs/Makefile @@ -0,0 +1,30 @@ +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) + + diff --git a/bsd/miscfs/routefs/routefs.h b/bsd/miscfs/routefs/routefs.h new file mode 100644 index 000000000..b6ba01974 --- /dev/null +++ b/bsd/miscfs/routefs/routefs.h @@ -0,0 +1,67 @@ +/* + * 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 + + +__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_ */ diff --git a/bsd/miscfs/routefs/routefs_ops.c b/bsd/miscfs/routefs/routefs_ops.c new file mode 100644 index 000000000..194325406 --- /dev/null +++ b/bsd/miscfs/routefs/routefs_ops.c @@ -0,0 +1,562 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if CONFIG_MACF +#include +#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 +#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 }; + + + diff --git a/bsd/net/dlil.c b/bsd/net/dlil.c index 6d89331b1..b7fa46e15 100644 --- a/bsd/net/dlil.c +++ b/bsd/net/dlil.c @@ -5701,6 +5701,10 @@ ifnet_detach(ifnet_t ifp) 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. @@ -7286,7 +7290,7 @@ ifnet_set_log(struct ifnet *ifp, int32_t level, uint32_t flags, * 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; } diff --git a/bsd/net/if.c b/bsd/net/if.c index 57d48d21c..a58ce458c 100644 --- a/bsd/net/if.c +++ b/bsd/net/if.c @@ -682,7 +682,9 @@ if_clone_list(int count, int *ret_total, user_addr_t dst) 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; diff --git a/bsd/net/if_var.h b/bsd/net/if_var.h index fc7ce88fb..21066e652 100644 --- a/bsd/net/if_var.h +++ b/bsd/net/if_var.h @@ -328,13 +328,19 @@ struct if_rxpoll_stats { }; 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 { @@ -356,6 +362,8 @@ 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; }; diff --git a/bsd/net/kpi_interface.c b/bsd/net/kpi_interface.c index a2289eca0..a64dd0f03 100644 --- a/bsd/net/kpi_interface.c +++ b/bsd/net/kpi_interface.c @@ -2733,11 +2733,20 @@ ifnet_set_delegate(ifnet_t ifp, ifnet_t delegated_ifp) 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) { diff --git a/bsd/net/necp.c b/bsd/net/necp.c index e40268c30..6da23d1a3 100644 --- a/bsd/net/necp.c +++ b/bsd/net/necp.c @@ -2149,6 +2149,18 @@ necp_policy_unapply(struct necp_session_policy *policy) 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 @@ -2372,6 +2384,10 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli } 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; @@ -2385,6 +2401,10 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli } 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; @@ -2398,6 +2418,11 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli } 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; @@ -2411,6 +2436,11 @@ necp_policy_apply(struct necp_session *session, struct necp_session_policy *poli } 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; @@ -5169,7 +5199,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc 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); @@ -5178,7 +5210,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc } 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); @@ -5187,7 +5221,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc } } 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); @@ -5196,7 +5232,9 @@ necp_socket_fillout_info_locked(struct inpcb *inp, struct sockaddr *override_loc } 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); diff --git a/bsd/net/ntstat.c b/bsd/net/ntstat.c index 9d42c7c6d..f742a560a 100644 --- a/bsd/net/ntstat.c +++ b/bsd/net/ntstat.c @@ -271,6 +271,9 @@ nstat_inpcb_to_flags( 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; } @@ -2104,6 +2107,85 @@ done: 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) { @@ -2150,6 +2232,11 @@ 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); @@ -2161,6 +2248,12 @@ v6: 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); @@ -2262,13 +2355,14 @@ nstat_sysinfo_send_data_internal( 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) @@ -2284,8 +2378,7 @@ nstat_sysinfo_send_data_internal( 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; @@ -2299,10 +2392,6 @@ nstat_sysinfo_send_data_internal( 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) @@ -2452,7 +2541,6 @@ nstat_sysinfo_send_data_internal( nstat_set_keyval_scalar(&kv[i++], NSTAT_SYSINFO_TFO_BLACKHOLE, data->u.tcp_stats.tfo_blackhole); - VERIFY(i == nkeyvals); break; } @@ -2557,15 +2645,55 @@ nstat_sysinfo_send_data_internal( 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; @@ -2699,11 +2827,11 @@ nstat_flush_accumulated_msgs( 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) diff --git a/bsd/net/ntstat.h b/bsd/net/ntstat.h index 1d479d3ab..ae6cdf1a0 100644 --- a/bsd/net/ntstat.h +++ b/bsd/net/ntstat.h @@ -173,6 +173,19 @@ enum ,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 -- @@ -188,6 +201,7 @@ enum #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 diff --git a/bsd/net/pf.c b/bsd/net/pf.c index 58a20fe4f..92808d67e 100644 --- a/bsd/net/pf.c +++ b/bsd/net/pf.c @@ -9439,52 +9439,56 @@ done: *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; @@ -10051,46 +10055,49 @@ done: } /* 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; diff --git a/bsd/net/pf_norm.c b/bsd/net/pf_norm.c index d6c5801f5..3df62ef3c 100644 --- a/bsd/net/pf_norm.c +++ b/bsd/net/pf_norm.c @@ -1666,7 +1666,7 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, 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; } } @@ -1715,7 +1715,7 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason, 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; } } diff --git a/bsd/netinet/flow_divert.c b/bsd/netinet/flow_divert.c index 76e29f8d6..cc2e2c8fe 100644 --- a/bsd/netinet/flow_divert.c +++ b/bsd/netinet/flow_divert.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Apple Inc. All rights reserved. + * Copyright (c) 2012-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -70,6 +70,7 @@ #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) { \ @@ -353,6 +354,12 @@ flow_divert_pcb_destroy(struct flow_divert_pcb *fd_cb) 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); } @@ -838,17 +845,106 @@ flow_divert_trie_search(struct flow_divert_trie *trie, uint8_t *string_bytes) 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(); } @@ -902,10 +998,108 @@ flow_divert_send_packet(struct flow_divert_pcb *fd_cb, mbuf_t packet, Boolean en } 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, @@ -985,21 +1179,44 @@ flow_divert_send_connect(struct flow_divert_pcb *fd_cb, struct sockaddr *to, mbu } } + 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; } @@ -1294,7 +1511,7 @@ flow_divert_send_app_data(struct flow_divert_pcb *fd_cb, mbuf_t data, struct soc 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; @@ -1593,7 +1810,6 @@ flow_divert_handle_connect_result(struct flow_divert_pcb *fd_cb, mbuf_t packet, 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) { @@ -1612,6 +1828,7 @@ set_socket_state: } flow_divert_disconnect_socket(fd_cb->so); } else { + flow_divert_send_buffered_data(fd_cb, FALSE); soisconnected(fd_cb->so); } @@ -1854,6 +2071,7 @@ flow_divert_handle_properties_update(struct flow_divert_pcb *fd_cb, mbuf_t packe 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"); @@ -1862,32 +2080,36 @@ flow_divert_handle_properties_update(struct flow_divert_pcb *fd_cb, mbuf_t packe 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); } @@ -1895,18 +2117,49 @@ flow_divert_handle_properties_update(struct flow_divert_pcb *fd_cb, mbuf_t packe 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); } @@ -2529,8 +2782,7 @@ flow_divert_connect_out(struct socket *so, struct sockaddr *to, proc_t p) 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); @@ -2552,12 +2804,6 @@ flow_divert_connect_out(struct socket *so, struct sockaddr *to, proc_t p) 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; @@ -2572,124 +2818,48 @@ flow_divert_connect_out(struct socket *so, struct sockaddr *to, proc_t p) } } - 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: @@ -2704,7 +2874,7 @@ flow_divert_connectx_out_common(struct socket *so, int af, 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); @@ -2729,6 +2899,39 @@ flow_divert_connectx_out_common(struct socket *so, int af, 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 */ } @@ -2744,7 +2947,7 @@ flow_divert_connectx_out(struct socket *so, struct sockaddr_list **src_sl, { #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 @@ -2756,7 +2959,7 @@ flow_divert_connectx6_out(struct socket *so, struct sockaddr_list **src_sl, { #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 */ @@ -2909,7 +3112,7 @@ flow_divert_in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifne } 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; @@ -2942,10 +3145,15 @@ flow_divert_data_out(struct socket *so, int flags, mbuf_t data, struct sockaddr /* 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)); @@ -2972,6 +3180,30 @@ done: 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) { @@ -3164,6 +3396,7 @@ flow_divert_token_set(struct socket *so, struct sockopt *sopt) 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) { @@ -3232,11 +3465,12 @@ flow_divert_token_set(struct socket *so, struct sockopt *sopt) } 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; } @@ -3266,6 +3500,13 @@ flow_divert_token_set(struct socket *so, struct sockopt *sopt) 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); @@ -3314,6 +3555,13 @@ flow_divert_token_get(struct socket *so, struct sockopt *sopt) 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); @@ -3586,6 +3834,7 @@ flow_divert_init(void) 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; @@ -3619,6 +3868,7 @@ flow_divert_init(void) 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; @@ -3651,6 +3901,7 @@ flow_divert_init(void) 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; @@ -3684,6 +3935,7 @@ flow_divert_init(void) 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; diff --git a/bsd/netinet/flow_divert.h b/bsd/netinet/flow_divert.h index 0968d9ad0..1af72b8be 100644 --- a/bsd/netinet/flow_divert.h +++ b/bsd/netinet/flow_divert.h @@ -48,11 +48,14 @@ struct flow_divert_pcb { 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); diff --git a/bsd/netinet/flow_divert_proto.h b/bsd/netinet/flow_divert_proto.h index a3b025eb7..a2b89bb8b 100644 --- a/bsd/netinet/flow_divert_proto.h +++ b/bsd/netinet/flow_divert_proto.h @@ -69,6 +69,7 @@ #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 @@ -80,6 +81,8 @@ #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; diff --git a/bsd/netinet/in.c b/bsd/netinet/in.c index f25e77c05..9f65560f8 100644 --- a/bsd/netinet/in.c +++ b/bsd/netinet/in.c @@ -1916,7 +1916,8 @@ in_selectaddrs(int af, struct sockaddr_list **src_sl, 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); @@ -1939,7 +1940,7 @@ in_selectaddrs(int af, struct sockaddr_list **src_sl, } } /* 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); } @@ -1970,7 +1971,7 @@ in_selectaddrs(int af, struct sockaddr_list **src_sl, } } /* 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); } diff --git a/bsd/netinet/in_pcblist.c b/bsd/netinet/in_pcblist.c index 2a00f895f..e54d8b996 100644 --- a/bsd/netinet/in_pcblist.c +++ b/bsd/netinet/in_pcblist.c @@ -245,8 +245,8 @@ tcpcb_to_xtcpcb_n(struct tcpcb *tp, struct xtcpcb_n *xt) 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; diff --git a/bsd/netinet/ip_output.c b/bsd/netinet/ip_output.c index 383751d4d..2788b0e79 100644 --- a/bsd/netinet/ip_output.c +++ b/bsd/netinet/ip_output.c @@ -1343,6 +1343,11 @@ sendit: 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; diff --git a/bsd/netinet/mp_pcb.c b/bsd/netinet/mp_pcb.c index 304c4c05c..31ea83fd0 100644 --- a/bsd/netinet/mp_pcb.c +++ b/bsd/netinet/mp_pcb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Apple Inc. All rights reserved. + * Copyright (c) 2012-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -207,7 +207,17 @@ mp_pcballoc(struct socket *so, struct mppcbinfo *mppi) 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); diff --git a/bsd/netinet/mp_proto.c b/bsd/netinet/mp_proto.c index 08bb5102f..65eafb0be 100644 --- a/bsd/netinet/mp_proto.c +++ b/bsd/netinet/mp_proto.c @@ -52,7 +52,8 @@ static struct protosw mpsw[] = { .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, diff --git a/bsd/netinet/mptcp.c b/bsd/netinet/mptcp.c index d218931be..71ea9f4a8 100644 --- a/bsd/netinet/mptcp.c +++ b/bsd/netinet/mptcp.c @@ -398,13 +398,14 @@ try_again: 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; } @@ -491,11 +492,12 @@ mptcp_get_subflow(struct mptses *mpte, struct mptsub *ignore, struct mptsub **pr } /* - * 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; } @@ -884,6 +886,13 @@ mptcp_update_rcv_state_f(struct mptcp_dss_ack_opt *dss_info, struct tcpcb *tp, 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); @@ -904,6 +913,13 @@ mptcp_update_rcv_state_g(struct mptcp_dss64_ack32_opt *dss_info, 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, diff --git a/bsd/netinet/mptcp_opt.c b/bsd/netinet/mptcp_opt.c index 414e76c5f..834a26e20 100644 --- a/bsd/netinet/mptcp_opt.c +++ b/bsd/netinet/mptcp_opt.c @@ -343,9 +343,24 @@ mptcp_send_infinite_mapping(struct tcpcb *tp, u_char *opt, unsigned int optlen) 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) @@ -1128,7 +1143,7 @@ mptcp_do_mpcapable_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th, 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; @@ -1136,6 +1151,11 @@ mptcp_do_mpcapable_opt(struct tcpcb *tp, u_char *cp, struct tcphdr *th, 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; @@ -1395,13 +1415,13 @@ mptcp_do_dss_opt_ack_meat(u_int64_t full_dack, struct tcpcb *tp) 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: " @@ -1432,6 +1452,13 @@ mptcp_do_dss_opt_meat(u_char *cp, struct tcpcb *tp) } \ } + /* + * 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; diff --git a/bsd/netinet/mptcp_subr.c b/bsd/netinet/mptcp_subr.c index b4ecb3ff0..d9a35da30 100644 --- a/bsd/netinet/mptcp_subr.c +++ b/bsd/netinet/mptcp_subr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Apple Inc. All rights reserved. + * Copyright (c) 2012-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -132,7 +132,6 @@ static void mptcp_key_pool_init(void); 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 *, @@ -148,6 +147,7 @@ static void mptcp_subflow_eupcall(struct socket *, void *, uint32_t); 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 @@ -185,7 +185,7 @@ static const char *mptcp_evret2str(ev_ret_t); 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 */ @@ -256,7 +256,18 @@ typedef struct mptcp_subflow_event_entry { 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, @@ -308,10 +319,6 @@ static mptsub_ev_entry_t mpsub_ev_entry_tbl [] = { .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, @@ -720,6 +727,13 @@ mptcp_subflow_socreate(struct mptses *mpte, struct mptsub *mpts, int dom, (*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; @@ -1268,17 +1282,6 @@ mptcp_subflow_add(struct mptses *mpte, struct mptsub *mpts, 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). @@ -1291,7 +1294,9 @@ mptcp_subflow_add(struct mptses *mpte, struct mptsub *mpts, 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) @@ -1413,8 +1418,7 @@ mptcp_subflow_add(struct mptses *mpte, struct mptsub *mpts, 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); @@ -1432,6 +1436,27 @@ mptcp_subflow_add(struct mptses *mpte, struct mptsub *mpts, 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; @@ -1688,6 +1713,16 @@ mptcp_subflow_input(struct mptses *mpte, struct mptsub *mpts) 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) { @@ -1742,7 +1777,7 @@ mptcp_subflow_wupcall(struct socket *so, void *arg, int waitf) 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. @@ -1772,6 +1807,7 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) 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); @@ -1793,7 +1829,8 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *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), @@ -1810,6 +1847,10 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) 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 @@ -1834,6 +1875,16 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) 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)) { @@ -1884,12 +1935,6 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) */ 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; } /* @@ -1983,9 +2028,12 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) } 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; } @@ -1996,9 +2044,16 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) 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) { @@ -2021,9 +2076,12 @@ mptcp_subflow_output(struct mptses *mpte, struct mptsub *mpts) 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; } @@ -2112,8 +2170,13 @@ mptcp_subflow_events(struct mptses *mpte, struct mptsub *mpts, * 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; @@ -2643,7 +2706,7 @@ mptcp_subflow_connected_ev(struct mptses *mpte, struct mptsub *mpts, 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: " @@ -2682,8 +2745,8 @@ mptcp_subflow_connected_ev(struct mptses *mpte, struct mptsub *mpts, /* * 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); @@ -2693,9 +2756,16 @@ mptcp_subflow_connected_ev(struct mptses *mpte, struct mptsub *mpts, 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 && @@ -2845,37 +2915,28 @@ mptcp_subflow_connected_ev(struct mptses *mpte, struct mptsub *mpts, (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) { @@ -2896,13 +2957,11 @@ mptcp_subflow_connected_ev(struct mptses *mpte, struct mptsub *mpts, 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); @@ -3176,7 +3235,6 @@ mptcp_fastjoin_ev(struct mptses *mpte, struct mptsub *mpts, */ if (mpts->mpts_sndnxt == 0) { mpts->mpts_sndnxt = mp_tp->mpt_snduna; - mpts->mpts_rel_seq = 1; } MPT_UNLOCK(mp_tp); } @@ -3635,10 +3693,10 @@ mptcp_thread_dowork(struct mptses *mpte) 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); @@ -3715,6 +3773,10 @@ mptcp_thread_dowork(struct mptses *mpte) MPTS_UNLOCK(mpts); continue; } + + if (mpts->mpts_flags & MPTSF_TFO_REQD) + mptcp_drop_tfo_data(mpte, mpts); + so = mpts->mpts_socket; /* @@ -3732,6 +3794,7 @@ mptcp_thread_dowork(struct mptses *mpte) 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); @@ -4326,7 +4389,7 @@ mptcp_get_trunced_hmac(mptcp_addr_id aid, struct mptcb *mp_tp) /* * Authentication data generation */ -int +void mptcp_generate_token(char *sha_digest, int sha_digest_len, caddr_t token, int token_len) { @@ -4335,10 +4398,10 @@ mptcp_generate_token(char *sha_digest, int sha_digest_len, caddr_t token, /* 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) { @@ -4357,13 +4420,48 @@ mptcp_generate_idsn(char *sha_digest, int sha_digest_len, caddr_t idsn, 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); @@ -4372,11 +4470,6 @@ mptcp_init_authparms(struct mptcb *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)) { @@ -4385,39 +4478,12 @@ mptcp_init_authparms(struct mptcb *mp_tp) 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); } /* @@ -4485,13 +4551,6 @@ mptcp_insert_dsn(struct mppcb *mpp, struct mbuf *m) __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); @@ -4504,9 +4563,18 @@ mptcp_insert_dsn(struct mppcb *mpp, struct mbuf *m) } 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); @@ -4523,12 +4591,14 @@ mptcp_preproc_sbdrop(struct mbuf *m, unsigned int len) 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), @@ -4849,9 +4919,15 @@ mptcp_adj_sendlen(struct socket *so, int32_t off, int32_t 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); } @@ -5516,3 +5592,50 @@ mptcp_use_symptoms_hints(struct mptsub* best, struct mptsub *second_best) /* 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); + } +} + diff --git a/bsd/netinet/mptcp_usrreq.c b/bsd/netinet/mptcp_usrreq.c index d61ad1fc3..e0b8fbcbc 100644 --- a/bsd/netinet/mptcp_usrreq.c +++ b/bsd/netinet/mptcp_usrreq.c @@ -88,6 +88,7 @@ static int mptcp_setopt(struct mptses *, struct sockopt *); 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, @@ -103,8 +104,21 @@ struct pr_usrreqs mptcp_usrreqs = { .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. */ @@ -165,6 +179,11 @@ mptcp_attach(struct socket *mp_so, struct proc *p) 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 @@ -306,12 +325,12 @@ static int 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; @@ -332,6 +351,33 @@ mptcp_usr_connectx(struct socket *mp_so, struct sockaddr_list **src_sl, 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); } @@ -1108,7 +1154,8 @@ mptcp_usr_send(struct socket *mp_so, int prus_flags, struct mbuf *m, 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; } @@ -1118,15 +1165,22 @@ mptcp_usr_send(struct socket *mp_so, int prus_flags, struct mbuf *m, (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) @@ -1377,6 +1431,10 @@ out: 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); } @@ -2054,3 +2112,37 @@ mptcp_sopt2str(int level, int optname, char *dst, int size) (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); +} diff --git a/bsd/netinet/mptcp_var.h b/bsd/netinet/mptcp_var.h index 905ab934a..c4fdf2c7e 100644 --- a/bsd/netinet/mptcp_var.h +++ b/bsd/netinet/mptcp_var.h @@ -216,6 +216,7 @@ struct mptsub { #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" \ @@ -589,8 +590,9 @@ extern void mptcp_get_rands(mptcp_addr_id, struct mptcb *, u_int32_t *, 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, diff --git a/bsd/netinet/tcp_input.c b/bsd/netinet/tcp_input.c index 1d65c4355..38e988614 100644 --- a/bsd/netinet/tcp_input.c +++ b/bsd/netinet/tcp_input.c @@ -3253,7 +3253,7 @@ findpcb: * 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 @@ -3321,7 +3321,10 @@ findpcb: 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); } diff --git a/bsd/netinet/tcp_output.c b/bsd/netinet/tcp_output.c index 86d4a71f3..4dfd0bc8f 100644 --- a/bsd/netinet/tcp_output.c +++ b/bsd/netinet/tcp_output.c @@ -212,13 +212,13 @@ sysctl_change_ecn_setting SYSCTL_HANDLER_ARGS 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", @@ -439,6 +439,7 @@ tcp_send_ecn_flags_on_syn(struct tcpcb *tp, struct socket *so) (tp->t_flagsext & TF_FASTOPEN))); } +#define TCP_ECN_SETUP_PERCENTAGE_MAX 5 void tcp_set_ecn(struct tcpcb *tp, struct ifnet *ifp) { @@ -487,6 +488,21 @@ 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; + } } /* @@ -815,16 +831,6 @@ again: 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; } @@ -1161,8 +1167,7 @@ after_sack_rexmit: 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) || @@ -1859,6 +1864,7 @@ send: INP_ADD_STAT(inp, cell, wifi, wired, txbytes, len); tp->t_stat.txretransmitbytes += len; + tp->t_stat.rxmitpkts++; } } else { tcpstat.tcps_sndpack++; diff --git a/bsd/netinet/tcp_subr.c b/bsd/netinet/tcp_subr.c index 65a171fed..88d18875a 100644 --- a/bsd/netinet/tcp_subr.c +++ b/bsd/netinet/tcp_subr.c @@ -1140,6 +1140,12 @@ tcp_update_ecn_perf_stats(struct tcpcb *tp, { 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); @@ -1165,55 +1171,11 @@ tcp_update_ecn_perf_stats(struct tcpcb *tp, } } - /* 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; } @@ -1426,6 +1388,8 @@ no_valid_rt: 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) { @@ -1452,36 +1416,29 @@ no_valid_rt: 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); } @@ -1754,8 +1711,8 @@ tcpcb_to_otcpcb(struct tcpcb *tp, struct otcpcb *otp) 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; @@ -1937,8 +1894,8 @@ tcpcb_to_xtcpcb64(struct tcpcb *tp, struct xtcpcb64 *otp) 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; diff --git a/bsd/netinet/tcp_timer.c b/bsd/netinet/tcp_timer.c index 0ffb340d0..bda29ca82 100644 --- a/bsd/netinet/tcp_timer.c +++ b/bsd/netinet/tcp_timer.c @@ -894,8 +894,7 @@ tcp_timers(tp, timer) 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); @@ -2220,11 +2219,11 @@ tcp_probe_connectivity(struct ifnet *ifp, u_int32_t enable) 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); diff --git a/bsd/netinet/tcp_usrreq.c b/bsd/netinet/tcp_usrreq.c index bfc86e994..c80d20e8a 100644 --- a/bsd/netinet/tcp_usrreq.c +++ b/bsd/netinet/tcp_usrreq.c @@ -457,8 +457,6 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 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 */ @@ -672,8 +670,6 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 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 */ @@ -1224,7 +1220,20 @@ tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 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; @@ -1316,8 +1325,6 @@ tcp_connect(tp, nam, p) 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; @@ -1406,24 +1413,6 @@ skip_oinp: 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); @@ -1443,8 +1432,6 @@ tcp6_connect(tp, nam, p) 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; @@ -1524,24 +1511,6 @@ tcp6_connect(tp, nam, p) 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); diff --git a/bsd/netinet/tcp_var.h b/bsd/netinet/tcp_var.h index 26f5b49d0..510d1cd84 100644 --- a/bsd/netinet/tcp_var.h +++ b/bsd/netinet/tcp_var.h @@ -270,7 +270,7 @@ struct tcpcb { #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 */ @@ -362,9 +362,6 @@ struct tcpcb { 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 */ @@ -450,6 +447,7 @@ struct tcpcb { 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 */ @@ -542,6 +540,7 @@ struct tcpcb { #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 */ @@ -850,7 +849,7 @@ struct tcpcb { #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 */ diff --git a/bsd/netinet6/esp_input.c b/bsd/netinet6/esp_input.c index 1718e87bd..e51ea9b0f 100644 --- a/bsd/netinet6/esp_input.c +++ b/bsd/netinet6/esp_input.c @@ -168,7 +168,7 @@ esp6_input_strip_udp_encap (struct mbuf *m, int ip6hlen) 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; } @@ -1272,7 +1272,7 @@ noreplaycheck: } } - 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 { @@ -1371,6 +1371,17 @@ noreplaycheck: 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) { diff --git a/bsd/netinet6/ip6_forward.c b/bsd/netinet6/ip6_forward.c index 2f5ab8fbf..ee5a70453 100644 --- a/bsd/netinet6/ip6_forward.c +++ b/bsd/netinet6/ip6_forward.c @@ -472,6 +472,8 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt, */ 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++; @@ -479,6 +481,8 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt, 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); @@ -522,6 +526,8 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt, 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); diff --git a/bsd/netinet6/ipsec.c b/bsd/netinet6/ipsec.c index 43259eea9..d3ef5ed0c 100644 --- a/bsd/netinet6/ipsec.c +++ b/bsd/netinet6/ipsec.c @@ -4497,7 +4497,8 @@ ipsec6_tunnel_validate(m, off, nxt0, sav, ifamily) { 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; @@ -4540,26 +4541,40 @@ ipsec6_tunnel_validate(m, off, nxt0, sav, ifamily) /* 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 diff --git a/bsd/netinet6/nd6_rtr.c b/bsd/netinet6/nd6_rtr.c index 6227ff003..a363d3a92 100644 --- a/bsd/netinet6/nd6_rtr.c +++ b/bsd/netinet6/nd6_rtr.c @@ -1985,84 +1985,6 @@ nd6_prefix_lookup(struct nd_prefix *pr, int nd6_prefix_expiry) 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) @@ -2076,11 +1998,6 @@ nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr, 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); diff --git a/bsd/netinet6/udp6_usrreq.c b/bsd/netinet6/udp6_usrreq.c index 1e0f9eb37..f4b1f11cc 100644 --- a/bsd/netinet6/udp6_usrreq.c +++ b/bsd/netinet6/udp6_usrreq.c @@ -1012,8 +1012,16 @@ udp6_input_checksum(struct mbuf *m, struct udphdr *uh, int off, int ulen) 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; } diff --git a/bsd/netkey/key.c b/bsd/netkey/key.c index 0e2e7df2e..2d91aa70b 100644 --- a/bsd/netkey/key.c +++ b/bsd/netkey/key.c @@ -2346,15 +2346,14 @@ key_spdadd( #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); @@ -2368,8 +2367,7 @@ key_spdadd( 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); diff --git a/bsd/sys/Makefile b/bsd/sys/Makefile index 4b4072516..f07d5649f 100644 --- a/bsd/sys/Makefile +++ b/bsd/sys/Makefile @@ -67,6 +67,7 @@ PRIVATE_DATAFILES = \ kern_overrides.h \ mbuf.h \ mman.h \ + persona.h \ priv.h \ proc.h \ proc_info.h \ @@ -138,6 +139,7 @@ PRIVATE_KERNELFILES = \ mach_swapon.h \ msgbuf.h \ eventvar.h \ + persona.h \ proc_info.h \ pthread_shims.h \ quota.h \ diff --git a/bsd/sys/codedir_internal.h b/bsd/sys/codedir_internal.h index ffda24326..556a311d0 100644 --- a/bsd/sys/codedir_internal.h +++ b/bsd/sys/codedir_internal.h @@ -38,10 +38,4 @@ #include -const -CS_CodeDirectory *findCodeDirectory( - const CS_SuperBlob *embedded, - const char *lower_bound, - const char *upper_bound); - #endif diff --git a/bsd/sys/codesign.h b/bsd/sys/codesign.h index 58509c690..5f78699a4 100644 --- a/bsd/sys/codesign.h +++ b/bsd/sys/codesign.h @@ -54,6 +54,7 @@ #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) @@ -99,6 +100,10 @@ enum { 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 */ @@ -107,12 +112,13 @@ enum { 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 */ }; diff --git a/bsd/sys/csr.h b/bsd/sys/csr.h index b2f59f1c3..cbff7a08b 100644 --- a/bsd/sys/csr.h +++ b/bsd/sys/csr.h @@ -2,7 +2,7 @@ * 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 @@ -11,10 +11,10 @@ * 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, @@ -22,7 +22,7 @@ * 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@ */ @@ -83,10 +83,6 @@ __BEGIN_DECLS 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); diff --git a/bsd/sys/fsctl.h b/bsd/sys/fsctl.h index c99d47ba7..623d16f55 100644 --- a/bsd/sys/fsctl.h +++ b/bsd/sys/fsctl.h @@ -284,8 +284,11 @@ typedef struct package_ext_info { /* 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 // // diff --git a/bsd/sys/imgact.h b/bsd/sys/imgact.h index 03db89c61..57df28890 100644 --- a/bsd/sys/imgact.h +++ b/bsd/sys/imgact.h @@ -117,7 +117,7 @@ struct image_params { 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 */ }; /* diff --git a/bsd/sys/kdebug.h b/bsd/sys/kdebug.h index d2e340aa2..956d7234c 100644 --- a/bsd/sys/kdebug.h +++ b/bsd/sys/kdebug.h @@ -193,6 +193,7 @@ extern void kernel_debug_enter( #define DBG_ARIADNE 43 #define DBG_DAEMON 44 #define DBG_ENERGYTRACE 45 +#define DBG_IMG 49 #define DBG_MIG 255 @@ -369,6 +370,9 @@ extern void kernel_debug_string_simple(const char *message); #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 @@ -711,6 +715,7 @@ extern void kernel_debug_string_simple(const char *message); /* 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 diff --git a/bsd/sys/kern_memorystatus.h b/bsd/sys/kern_memorystatus.h index c74003291..0cb7e52b7 100644 --- a/bsd/sys/kern_memorystatus.h +++ b/bsd/sys/kern_memorystatus.h @@ -190,6 +190,8 @@ int memorystatus_control(uint32_t command, int32_t pid, uint32_t flags, void *bu #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 diff --git a/bsd/sys/mount.h b/bsd/sys/mount.h index 2f6437348..2db9e5779 100644 --- a/bsd/sys/mount.h +++ b/bsd/sys/mount.h @@ -480,7 +480,7 @@ struct netfs_status { #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 */ diff --git a/bsd/sys/mount_internal.h b/bsd/sys/mount_internal.h index e73e4b9af..2f966ca5d 100644 --- a/bsd/sys/mount_internal.h +++ b/bsd/sys/mount_internal.h @@ -461,7 +461,7 @@ void mount_set_noreaddirext (mount_t); /* 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. diff --git a/bsd/sys/persona.h b/bsd/sys/persona.h new file mode 100644 index 000000000..d0912055f --- /dev/null +++ b/bsd/sys/persona.h @@ -0,0 +1,386 @@ +/* + * 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 + +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 +#include +#include + +#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 ""; + + 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 ""; +} +#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 + +/* + * 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_ */ diff --git a/bsd/sys/proc.h b/bsd/sys/proc.h index 4d3b2cbd3..e8b0cc6c3 100644 --- a/bsd/sys/proc.h +++ b/bsd/sys/proc.h @@ -193,7 +193,7 @@ struct extern_proc { #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 */ @@ -241,6 +241,8 @@ extern void wakeup_one(caddr_t chan); 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. */ @@ -308,6 +310,9 @@ extern int msleep1(void *chan, lck_mtx_t *mtx, int pri, const char *wmesg, u_int 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 *); /*! diff --git a/bsd/sys/proc_internal.h b/bsd/sys/proc_internal.h index f5ad0fcf4..5e7a3750a 100644 --- a/bsd/sys/proc_internal.h +++ b/bsd/sys/proc_internal.h @@ -221,6 +221,11 @@ struct proc { 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 */ diff --git a/bsd/sys/reboot.h b/bsd/sys/reboot.h index 5f51c9952..cd4878812 100644 --- a/bsd/sys/reboot.h +++ b/bsd/sys/reboot.h @@ -65,6 +65,8 @@ #define _SYS_REBOOT_H_ #include +#include +#include /* * Arguments to reboot system call. @@ -86,6 +88,13 @@ #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 diff --git a/bsd/sys/socket.h b/bsd/sys/socket.h index 8afe6c4da..8c577ce7c 100644 --- a/bsd/sys/socket.h +++ b/bsd/sys/socket.h @@ -334,6 +334,7 @@ struct so_tcdbg { #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; diff --git a/bsd/sys/socketvar.h b/bsd/sys/socketvar.h index e0b810b0c..f83ffc529 100644 --- a/bsd/sys/socketvar.h +++ b/bsd/sys/socketvar.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Apple Inc. All rights reserved. + * Copyright (c) 2000-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -334,7 +334,8 @@ struct socket { #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; }; @@ -597,8 +598,8 @@ struct kextcb { "\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 \ @@ -947,7 +948,7 @@ extern int soopt_mcopyin(struct sockopt *sopt, struct mbuf *m); 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 *); diff --git a/bsd/sys/spawn_internal.h b/bsd/sys/spawn_internal.h index e794747db..e86e6c2fd 100644 --- a/bsd/sys/spawn_internal.h +++ b/bsd/sys/spawn_internal.h @@ -131,6 +131,33 @@ struct _posix_spawn_coalition_info { } 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 @@ -168,7 +195,7 @@ typedef struct _posix_spawnattr { _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; /* @@ -339,8 +366,8 @@ struct _posix_spawn_args_desc { __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 @@ -362,8 +389,8 @@ struct user32__posix_spawn_args_desc { 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 { @@ -377,8 +404,8 @@ 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; }; diff --git a/bsd/sys/ubc_internal.h b/bsd/sys/ubc_internal.h index f5b04763a..d3a87d049 100644 --- a/bsd/sys/ubc_internal.h +++ b/bsd/sys/ubc_internal.h @@ -108,7 +108,8 @@ struct cs_blob { 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; diff --git a/bsd/sys/vnode.h b/bsd/sys/vnode.h index e243a7a2f..bc8b67337 100644 --- a/bsd/sys/vnode.h +++ b/bsd/sys/vnode.h @@ -2029,6 +2029,11 @@ int vnode_lookup_continue_needed(vnode_t vp, struct componentname *cnp); @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 @@ -2041,7 +2046,6 @@ int vaccess(mode_t file_mode, uid_t uid, gid_t gid, 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); diff --git a/bsd/vfs/vfs_cache.c b/bsd/vfs/vfs_cache.c index 36b1d24e6..b91ca2e44 100644 --- a/bsd/vfs/vfs_cache.c +++ b/bsd/vfs/vfs_cache.c @@ -1173,9 +1173,15 @@ skiprsrcfork: /* * 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; /* diff --git a/bsd/vfs/vfs_conf.c b/bsd/vfs/vfs_conf.c index 69e1a23ab..71acea8d1 100644 --- a/bsd/vfs/vfs_conf.c +++ b/bsd/vfs/vfs_conf.c @@ -96,6 +96,7 @@ extern int nfs_mountroot(void); 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; @@ -113,6 +114,7 @@ enum fs_type_num { FT_HFS = 17, FT_DEVFS = 19, FT_SYNTHFS = 20, + FT_ROUTEFS = 21, FT_MOCKFS = 0x6D6F636B }; @@ -151,6 +153,10 @@ static struct vfstable vfstbllist[] = { { &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, "", 0, 0, 0, NULL, NULL, 0, 0, 0, NULL, 0, NULL}, {NULL, "", 0, 0, 0, NULL, NULL, 0, 0, 0, NULL, 0, NULL}, }; diff --git a/bsd/vfs/vfs_subr.c b/bsd/vfs/vfs_subr.c index ca47a42da..4f31d45e7 100644 --- a/bsd/vfs/vfs_subr.c +++ b/bsd/vfs/vfs_subr.c @@ -7450,6 +7450,10 @@ out: * 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)) == diff --git a/bsd/vfs/vfs_syscalls.c b/bsd/vfs/vfs_syscalls.c index adea23a19..0ed08b06f 100644 --- a/bsd/vfs/vfs_syscalls.c +++ b/bsd/vfs/vfs_syscalls.c @@ -100,6 +100,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,10 @@ #include #include +#if ROUTEFS +#include +#endif /* ROUTEFS */ + #if CONFIG_MACF #include #include @@ -231,7 +236,7 @@ enum { * Virtual File System System Calls */ -#if NFSCLIENT || DEVFS +#if NFSCLIENT || DEVFS || ROUTEFS /* * Private in-kernel mounting spi (NFS only, not exported) */ @@ -681,7 +686,7 @@ mount_common(char *fstypename, vnode_t pvp, vnode_t vp, /* 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) @@ -3691,6 +3696,10 @@ openbyid_np(__unused proc_t p, struct openbyid_np_args *uap, int *retval) 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); } @@ -9375,6 +9384,27 @@ fsctl_internal(proc_t p, vnode_t *arg_vp, u_long cmd, user_addr_t udata, u_long } 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; diff --git a/bsd/vm/vm_unix.c b/bsd/vm/vm_unix.c index 099a70fb6..73abe01da 100644 --- a/bsd/vm/vm_unix.c +++ b/bsd/vm/vm_unix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Apple Inc. All rights reserved. + * Copyright (c) 2000-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -104,6 +104,7 @@ 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, ""); diff --git a/config/MASTER b/config/MASTER index d612020b7..4e5023e9b 100644 --- a/config/MASTER +++ b/config/MASTER @@ -137,6 +137,7 @@ options MOCKFS # Boot from an executable # options FIFO # fifo support # options FDESC # fdesc_fs support # options DEVFS # devfs support # +options ROUTEFS # routefs support # options JOURNALING # journaling support # options HFS_COMPRESSION # hfs compression # options CONFIG_HFS_STD # hfs standard support # @@ -486,6 +487,12 @@ options CONFIG_STATIC_CPPINIT # Static library initializes kext cpp ru # options CONFIG_KEXT_BASEMENT # # +# +# Persona Management +# +options CONFIG_PERSONAS # Persona management # +options PERSONA_DEBUG # Persona debugging # + # # security configuration options # diff --git a/config/MASTER.x86_64 b/config/MASTER.x86_64 index 3baa99963..3baf698bc 100644 --- a/config/MASTER.x86_64 +++ b/config/MASTER.x86_64 @@ -21,7 +21,10 @@ # 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 ] @@ -48,9 +51,9 @@ # 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 ] # ###################################################################### # diff --git a/config/MasterVersion b/config/MasterVersion index 6e5b5944b..1d3e76814 100644 --- a/config/MasterVersion +++ b/config/MasterVersion @@ -1,4 +1,4 @@ -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. diff --git a/config/Private.exports b/config/Private.exports index e04f8d26b..ea91129b6 100644 --- a/config/Private.exports +++ b/config/Private.exports @@ -204,6 +204,14 @@ _net_add_proto:_net_add_proto_old _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 @@ -319,6 +327,7 @@ _utun_pkt_dtls_input _vfs_context_bind _vfs_context_get_special_port _vfs_context_set_special_port +_vfs_context_kernel _vfs_devvp _vfs_getattr _vfs_getbyid diff --git a/config/Private.x86_64.exports b/config/Private.x86_64.exports index 35ecccdf5..ec3e60406 100644 --- a/config/Private.x86_64.exports +++ b/config/Private.x86_64.exports @@ -18,7 +18,6 @@ _cpuid_leaf7_features _cpuid_info _csr_check _csr_get_active_config -_csr_set_allow_all _hv_ept_pmap_create _hv_get* _hv_release* diff --git a/iokit/IOKit/IOKitKeysPrivate.h b/iokit/IOKit/IOKitKeysPrivate.h index 061c64ae3..3c294ff22 100644 --- a/iokit/IOKit/IOKitKeysPrivate.h +++ b/iokit/IOKit/IOKitKeysPrivate.h @@ -110,4 +110,6 @@ enum { kIOClassNameOverrideNone = 0x00000001, }; +#define kIOServiceLegacyMatchingRegistryIDKey "IOServiceLegacyMatchingRegistryID" + #endif /* ! _IOKIT_IOKITKEYSPRIVATE_H */ diff --git a/iokit/IOKit/IOPolledInterface.h b/iokit/IOKit/IOPolledInterface.h index 584484eab..bec682028 100644 --- a/iokit/IOKit/IOPolledInterface.h +++ b/iokit/IOKit/IOPolledInterface.h @@ -31,12 +31,13 @@ enum { - kIOPolledPreflightState = 1, - kIOPolledBeforeSleepState = 2, - kIOPolledAfterSleepState = 3, - kIOPolledPostflightState = 4, + kIOPolledPreflightState = 1, + kIOPolledBeforeSleepState = 2, + kIOPolledAfterSleepState = 3, + kIOPolledPostflightState = 4, kIOPolledPreflightCoreDumpState = 5, + kIOPolledPostflightCoreDumpState = 6, }; #if defined(__cplusplus) diff --git a/iokit/IOKit/IOService.h b/iokit/IOKit/IOService.h index e369da9b4..d99ceba5b 100644 --- a/iokit/IOKit/IOService.h +++ b/iokit/IOKit/IOService.h @@ -1286,6 +1286,7 @@ public: uint64_t getAuthorizationID( void ); IOReturn setAuthorizationID( uint64_t authorizationID ); void cpusRunning(void); + void scheduleFinalize(bool now); private: static IOReturn waitMatchIdle( UInt32 ms ); @@ -1379,7 +1380,6 @@ private: 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, diff --git a/iokit/IOKit/IOUserClient.h b/iokit/IOKit/IOUserClient.h index 06a9931db..d6a149317 100644 --- a/iokit/IOKit/IOUserClient.h +++ b/iokit/IOKit/IOUserClient.h @@ -208,8 +208,14 @@ private: 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, @@ -246,6 +252,7 @@ private: 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, diff --git a/iokit/Kernel/IOCPU.cpp b/iokit/Kernel/IOCPU.cpp index 47a17b5a1..a6e41b648 100644 --- a/iokit/Kernel/IOCPU.cpp +++ b/iokit/Kernel/IOCPU.cpp @@ -35,6 +35,7 @@ extern "C" { #include #include +#include } #include @@ -431,17 +432,19 @@ void IOCPUSleepKernel(void) 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 ); @@ -544,31 +547,10 @@ OSObject *IOCPU::getProperty(const OSSymbol *aKey) const 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); } diff --git a/iokit/Kernel/IODeviceTreeSupport.cpp b/iokit/Kernel/IODeviceTreeSupport.cpp index 965670f37..ce4a65ef7 100644 --- a/iokit/Kernel/IODeviceTreeSupport.cpp +++ b/iokit/Kernel/IODeviceTreeSupport.cpp @@ -37,6 +37,8 @@ #include +typedef UInt32 dtptr_t; + #include extern "C" { @@ -253,7 +255,7 @@ int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infoSize ) { IORegistryEntry *chosen; OSData *propObj; - unsigned int *propPtr; + dtptr_t *propPtr; unsigned int propSize; chosen = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ); @@ -263,9 +265,9 @@ int IODTGetLoaderInfo( const char *key, void **infoAddr, int *infoSize ) 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]); diff --git a/iokit/Kernel/IOMemoryDescriptor.cpp b/iokit/Kernel/IOMemoryDescriptor.cpp index f61b0f9d6..0cff9ef1d 100644 --- a/iokit/Kernel/IOMemoryDescriptor.cpp +++ b/iokit/Kernel/IOMemoryDescriptor.cpp @@ -1603,9 +1603,11 @@ IOGeneralMemoryDescriptor::initWithOptions(void * buffers, _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; diff --git a/iokit/Kernel/IOPMrootDomain.cpp b/iokit/Kernel/IOPMrootDomain.cpp index 2d6c7b79f..61ec8bf5f 100644 --- a/iokit/Kernel/IOPMrootDomain.cpp +++ b/iokit/Kernel/IOPMrootDomain.cpp @@ -835,12 +835,12 @@ static int 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); } @@ -849,6 +849,18 @@ static SYSCTL_PROC(_kern, OID_AUTO, consoleoptions, 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 { @@ -1161,6 +1173,7 @@ bool IOPMrootDomain::start( IOService * nub ) 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); diff --git a/iokit/Kernel/IOPolledInterface.cpp b/iokit/Kernel/IOPolledInterface.cpp index 1917714fc..09a1dd2fb 100644 --- a/iokit/Kernel/IOPolledInterface.cpp +++ b/iokit/Kernel/IOPolledInterface.cpp @@ -83,7 +83,6 @@ public: bool io; IOReturn ioStatus; uint32_t openCount; - uint32_t openState; static IOPolledFilePollers * copyPollers(IOService * media); }; @@ -222,17 +221,13 @@ IOPolledFilePollersOpen(IOPolledFileIOVars * filevars, uint32_t state, bool abor { 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) @@ -258,15 +253,9 @@ IOPolledFilePollersClose(IOPolledFileIOVars * filevars, uint32_t state) (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; @@ -278,20 +267,26 @@ IOPolledFilePollersClose(IOPolledFileIOVars * filevars, uint32_t state) } 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); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -701,11 +696,13 @@ IOPolledFilePollersSetup(IOPolledFileIOVars * vars, { 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; diff --git a/iokit/Kernel/IOService.cpp b/iokit/Kernel/IOService.cpp index 6ff5f289d..189f008fa 100644 --- a/iokit/Kernel/IOService.cpp +++ b/iokit/Kernel/IOService.cpp @@ -643,7 +643,7 @@ void IOService::detach( IOService * provider ) if( adjParent) provider->_adjustBusy( -1 ); if( (provider->__state[1] & kIOServiceTermPhase3State) && (0 == provider->getClient())) { - provider->scheduleFinalize(); + provider->scheduleFinalize(false); } provider->unlockForArbitration(); } @@ -2188,7 +2188,7 @@ void IOService::scheduleStop( IOService * provider ) IOLockUnlock( gJobsLock ); } -void IOService::scheduleFinalize( void ) +void IOService::scheduleFinalize(bool now) { uint64_t regID1 = getRegistryEntryID(); @@ -2199,17 +2199,19 @@ void IOService::scheduleFinalize( void ) (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 ) @@ -2503,10 +2505,10 @@ void IOService::terminateWorker( 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 ); @@ -4040,7 +4042,6 @@ OSObject * IOService::copyExistingServices( OSDictionary * matching, const OSSymbol * sym = OSSymbol::withString(str); OSMetaClass::applyToInstancesOfClassName(sym, instanceMatch, &ctx); sym->release(); - } else { @@ -5073,7 +5074,6 @@ bool IOService::matchInternal(OSDictionary * table, uint32_t options, uint32_t * { count = table->getCount(); done = 0; - str = OSDynamicCast(OSString, table->getObject(gIOProviderClassKey)); if (str) { @@ -5226,6 +5226,9 @@ bool IOService::matchPassive(OSDictionary * table, uint32_t options) assert( table ); + OSArray* aliasServiceRegIds = NULL; + IOService* foundAlternateService = NULL; + #if MATCH_DEBUG OSDictionary * root = table; #endif @@ -5249,7 +5252,7 @@ bool IOService::matchPassive(OSDictionary * table, uint32_t options) // do family specific matching match = where->matchPropertyTable( table, &score ); - + if( !match) { #if IOMATCHDEBUG if( kIOLogMatch & getDebugFlags( table )) @@ -5273,7 +5276,6 @@ bool IOService::matchPassive(OSDictionary * table, uint32_t options) nextTable = OSDynamicCast(OSDictionary, table->getObject( gIOParentMatchKey )); if(nextTable) { - // look for a matching entry anywhere up to root match = false; matchParent = true; @@ -5292,11 +5294,56 @@ bool IOService::matchPassive(OSDictionary * table, uint32_t options) 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); diff --git a/iokit/Kernel/IOUserClient.cpp b/iokit/Kernel/IOUserClient.cpp index 06e4a612c..1faa211e2 100644 --- a/iokit/Kernel/IOUserClient.cpp +++ b/iokit/Kernel/IOUserClient.cpp @@ -485,6 +485,55 @@ iokit_remove_reference( io_object_t obj ) 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 ) { @@ -1642,21 +1691,16 @@ kern_return_t is_io_object_get_class( 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 ); } @@ -1827,7 +1871,6 @@ static kern_return_t internal_io_service_match_property_table( obj = matching_size ? OSUnserializeXML(matching, matching_size) : OSUnserializeXML(matching); if( (dict = OSDynamicCast( OSDictionary, obj))) { - *matches = service->passiveMatch( dict ); kr = kIOReturnSuccess; } else @@ -4977,7 +5020,7 @@ kern_return_t iokit_user_client_trap(struct iokit_user_client_trap_args *args) } } - userClient->release(); + iokit_remove_connect_reference(userClient); } return result; @@ -5141,7 +5184,6 @@ IOReturn IOUserClient::externalMethod( uint32_t selector, IOExternalMethodArgume return (err); } - #if __LP64__ OSMetaClassDefineReservedUnused(IOUserClient, 0); OSMetaClassDefineReservedUnused(IOUserClient, 1); diff --git a/iokit/bsddev/IOKitBSDInit.cpp b/iokit/bsddev/IOKitBSDInit.cpp index 3e45ff1ff..6e67a3d89 100644 --- a/iokit/bsddev/IOKitBSDInit.cpp +++ b/iokit/bsddev/IOKitBSDInit.cpp @@ -830,7 +830,7 @@ IOOpenPolledCoreFile(const char * filename) static void IOClosePolledCoreFile(void) { - IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightState); + IOPolledFilePollersClose(gIOPolledCoreFileVars, kIOPolledPostflightCoreDumpState); IOPolledFileClose(&gIOPolledCoreFileVars, NULL, NULL, 0, 0, 0); } diff --git a/libkern/Makefile b/libkern/Makefile index 806567fc4..affdcb336 100644 --- a/libkern/Makefile +++ b/libkern/Makefile @@ -7,18 +7,18 @@ include $(MakeInc_cmd) 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 diff --git a/libkern/os/Makefile b/libkern/os/Makefile new file mode 100644 index 000000000..88789bb56 --- /dev/null +++ b/libkern/os/Makefile @@ -0,0 +1,42 @@ +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) diff --git a/libkern/os/overflow.h b/libkern/os/overflow.h new file mode 100644 index 000000000..2b6034ca6 --- /dev/null +++ b/libkern/os/overflow.h @@ -0,0 +1,111 @@ +/* + * 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 + +/* 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 */ diff --git a/libsyscall/Libsyscall.xcodeproj/project.pbxproj b/libsyscall/Libsyscall.xcodeproj/project.pbxproj index 10371199f..bdf83643f 100644 --- a/libsyscall/Libsyscall.xcodeproj/project.pbxproj +++ b/libsyscall/Libsyscall.xcodeproj/project.pbxproj @@ -105,6 +105,7 @@ 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 */; }; @@ -435,6 +436,7 @@ 2BA88DCB1810A3CE00EB63F6 /* coalition.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coalition.c; sourceTree = ""; }; 374A36E214748EE400AAF39D /* varargs_wrappers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = varargs_wrappers.s; sourceTree = ""; }; 37DDFB7614748713009D3355 /* syscall.map */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = syscall.map; sourceTree = ""; }; + 3F538F881A659C5600B37EFD /* persona.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = persona.c; sourceTree = ""; }; 435F3CA91B06B7BA005ED9EF /* work_interval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = work_interval.c; sourceTree = ""; }; 467DAFD3157E8AF200CE68F0 /* guarded_open_np.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = guarded_open_np.c; sourceTree = ""; }; 4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mach_approximate_time.c; sourceTree = ""; }; @@ -726,6 +728,7 @@ 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 */, @@ -1336,6 +1339,7 @@ 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 */, diff --git a/libsyscall/mach/host.c b/libsyscall/mach/host.c index 335038434..c6587951f 100644 --- a/libsyscall/mach/host.c +++ b/libsyscall/mach/host.c @@ -26,6 +26,7 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ +#include #include #include #include @@ -40,3 +41,18 @@ host_get_atm_diagnostic_flag(host_t host __unused, 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; +} diff --git a/libsyscall/wrappers/persona.c b/libsyscall/wrappers/persona.c new file mode 100644 index 000000000..e167cc38f --- /dev/null +++ b/libsyscall/wrappers/persona.c @@ -0,0 +1,91 @@ +/* + * 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 +#include +#include +#include +#include + +#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); +} diff --git a/libsyscall/wrappers/spawn/posix_spawn.c b/libsyscall/wrappers/spawn/posix_spawn.c index 88b6cabf3..4c36d0e90 100644 --- a/libsyscall/wrappers/spawn/posix_spawn.c +++ b/libsyscall/wrappers/spawn/posix_spawn.c @@ -133,7 +133,7 @@ posix_spawnattr_init(posix_spawnattr_t *attr) /* Default is to inherit parent's coalition(s) */ (*psattrp)->psa_coalition_info = NULL; - (*psattrp)->reserved = NULL; + (*psattrp)->psa_persona_info = NULL; /* * old coalition field @@ -178,7 +178,7 @@ posix_spawnattr_init(posix_spawnattr_t *attr) */ 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) @@ -191,6 +191,7 @@ 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; @@ -778,6 +779,29 @@ posix_spawn_destroycoalition_info_np(posix_spawnattr_t *attr) 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 @@ -1523,6 +1547,124 @@ posix_spawnattr_get_darwin_role_np(const posix_spawnattr_t * __restrict attr, ui 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 * @@ -1600,6 +1742,10 @@ posix_spawn(pid_t * __restrict pid, const char * __restrict path, 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 = diff --git a/libsyscall/wrappers/spawn/spawn_private.h b/libsyscall/wrappers/spawn/spawn_private.h index f98d2d2bd..3513946a4 100644 --- a/libsyscall/wrappers/spawn/spawn_private.h +++ b/libsyscall/wrappers/spawn/spawn_private.h @@ -57,4 +57,9 @@ int posix_spawnattr_get_qos_clamp_np(const posix_spawnattr_t * __restrict, u 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_*/ diff --git a/osfmk/Makefile b/osfmk/Makefile index 27250311c..c39c53ccc 100644 --- a/osfmk/Makefile +++ b/osfmk/Makefile @@ -24,6 +24,7 @@ INSTINC_SUBDIRS = \ vm \ libsa \ kdp \ + console \ kperf \ prng @@ -41,6 +42,7 @@ INSTINC_SUBDIRS_ARM = \ mach \ arm \ arm64 + INSTINC_SUBDIRS_ARM64 = \ mach \ arm \ diff --git a/osfmk/atm/atm.c b/osfmk/atm/atm.c index 6ce1a1e71..707a5be66 100644 --- a/osfmk/atm/atm.c +++ b/osfmk/atm/atm.c @@ -107,6 +107,7 @@ atm_get_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 @@ -143,6 +144,7 @@ struct ipc_voucher_attr_manager atm_manager = { .ivam_extract_content = atm_extract_content, .ivam_command = atm_command, .ivam_release = atm_release, + .ivam_flags = IVAM_FLAGS_NONE, }; #if DEVELOPMENT || DEBUG @@ -306,6 +308,7 @@ atm_get_value( 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; @@ -322,6 +325,7 @@ atm_get_value( /* 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; diff --git a/osfmk/bank/bank.c b/osfmk/bank/bank.c index 03e862e93..875380934 100644 --- a/osfmk/bank/bank.c +++ b/osfmk/bank/bank.c @@ -40,7 +40,7 @@ #include #include #include - +#include #include static zone_t bank_task_zone, bank_account_zone; @@ -51,6 +51,7 @@ 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))) @@ -64,13 +65,39 @@ queue_head_t bank_accounts_list; 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( @@ -89,6 +116,7 @@ bank_get_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 @@ -125,6 +153,7 @@ struct ipc_voucher_attr_manager bank_manager = { .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), }; @@ -170,6 +199,7 @@ bank_init() 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. */ @@ -226,8 +256,11 @@ bank_release_value( 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; } @@ -265,11 +298,14 @@ bank_get_value( 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; @@ -283,24 +319,68 @@ bank_get_value( /* 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]; @@ -310,28 +390,49 @@ bank_get_value( 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; @@ -341,6 +442,52 @@ bank_get_value( *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; @@ -383,6 +530,10 @@ bank_extract_content( 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; @@ -395,9 +546,13 @@ bank_extract_content( } 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); } @@ -431,6 +586,9 @@ bank_command( 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; @@ -454,6 +612,10 @@ bank_command( 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) { @@ -473,6 +635,46 @@ bank_command( 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; } @@ -502,7 +704,7 @@ bank_release( 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; @@ -513,13 +715,26 @@ bank_task_alloc_init(void) 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); @@ -529,6 +744,26 @@ bank_task_alloc_init(void) 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. @@ -538,7 +773,9 @@ bank_task_alloc_init(void) 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; @@ -558,15 +795,18 @@ bank_account_alloc_init( 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; @@ -600,6 +840,8 @@ bank_account_alloc_init( 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; @@ -619,14 +861,18 @@ bank_account_alloc_init( * 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); @@ -641,13 +887,16 @@ get_bank_task_context(task_t 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); @@ -698,6 +947,8 @@ bank_account_dealloc_with_sync( { 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); @@ -732,6 +983,8 @@ bank_account_dealloc_with_sync( /* 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); @@ -758,6 +1011,9 @@ bank_rollup_chit_to_tasks( 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; @@ -788,11 +1044,30 @@ bank_rollup_chit_to_tasks( * 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. @@ -816,9 +1091,46 @@ init_bank_ledgers(void) { 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 @@ -864,9 +1176,46 @@ bank_billed_time(bank_task_t bank_task) 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 @@ -926,6 +1275,7 @@ bank_get_voucher_ledger(ipc_voucher_t voucher) 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; @@ -944,11 +1294,21 @@ bank_get_voucher_ledger(ipc_voucher_t voucher) 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); } diff --git a/osfmk/bank/bank_internal.h b/osfmk/bank/bank_internal.h index 155f794ac..2f0d9353c 100644 --- a/osfmk/bank/bank_internal.h +++ b/osfmk/bank/bank_internal.h @@ -42,6 +42,8 @@ /* 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 @@ -51,7 +53,6 @@ struct bank_element { 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 @@ -61,21 +62,31 @@ typedef struct bank_element * bank_element_t; #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 @@ -108,6 +119,8 @@ struct bank_account { 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 @@ -118,7 +131,6 @@ struct bank_account { #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 @@ -152,8 +164,11 @@ struct _bank_ledger_indices { 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); diff --git a/osfmk/bank/bank_types.h b/osfmk/bank/bank_types.h index c0ce5720f..17ec0e151 100644 --- a/osfmk/bank/bank_types.h +++ b/osfmk/bank/bank_types.h @@ -38,6 +38,27 @@ #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_ */ diff --git a/osfmk/console/Makefile b/osfmk/console/Makefile index ae4830c80..90da36439 100644 --- a/osfmk/console/Makefile +++ b/osfmk/console/Makefile @@ -9,15 +9,16 @@ include $(MakeInc_def) 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) diff --git a/osfmk/console/i386/serial_console.c b/osfmk/console/i386/serial_console.c index 245a995fe..0d51045ef 100644 --- a/osfmk/console/i386/serial_console.c +++ b/osfmk/console/i386/serial_console.c @@ -193,7 +193,10 @@ _cnputc(char c) * 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. */ @@ -255,9 +258,11 @@ console_ring_try_empty(void) 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); diff --git a/osfmk/console/video_console.c b/osfmk/console/video_console.c index 4ba6ed72d..e9a1555a5 100644 --- a/osfmk/console/video_console.c +++ b/osfmk/console/video_console.c @@ -1860,7 +1860,9 @@ static boolean_t vc_needsave; 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; @@ -2591,14 +2593,15 @@ void vc_progress_setdiskspeed(uint32_t speed) 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) { @@ -2608,32 +2611,59 @@ vc_progress_task(__unused void *arg0, __unused void *arg) 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); } @@ -2652,10 +2682,10 @@ static boolean_t gc_graphics_boot = FALSE; 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 ) { @@ -2704,28 +2734,29 @@ vc_initialize(__unused struct vc_info * vinfo_p) 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 @@ -2738,14 +2769,28 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int op) 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 */ @@ -2756,36 +2801,17 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int op) } 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) @@ -2807,24 +2833,23 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int op) // 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) @@ -2859,7 +2884,18 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int 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; @@ -2903,8 +2939,8 @@ initialize_screen(PE_Video * boot_vinfo, unsigned int op) 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; @@ -3222,10 +3258,5 @@ vc_set_progressmeter(int new_value) } -void -vc_set_options(int new_value) -{ - vc_user_options = new_value; -} diff --git a/osfmk/console/video_console.h b/osfmk/console/video_console.h index c4631540c..468dc9e61 100644 --- a/osfmk/console/video_console.h +++ b/osfmk/console/video_console.h @@ -41,6 +41,32 @@ 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, @@ -70,7 +96,8 @@ struct vc_info 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 { @@ -88,6 +115,8 @@ 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, @@ -112,14 +141,7 @@ extern int vc_progressmeter_value; 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 } diff --git a/osfmk/device/device.defs b/osfmk/device/device.defs index 94c706eff..4948c08a4 100644 --- a/osfmk/device/device.defs +++ b/osfmk/device/device.defs @@ -135,7 +135,7 @@ type io_connect_t = mach_port_t #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 */ ; diff --git a/osfmk/device/device_types.h b/osfmk/device/device_types.h index af4991c49..fb2562055 100644 --- a/osfmk/device/device_types.h +++ b/osfmk/device/device_types.h @@ -123,6 +123,7 @@ typedef struct IOObject * io_object_t; 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 ); diff --git a/osfmk/device/iokit_rpc.c b/osfmk/device/iokit_rpc.c index 1412f4115..e855d1a0a 100644 --- a/osfmk/device/iokit_rpc.c +++ b/osfmk/device/iokit_rpc.c @@ -73,6 +73,7 @@ */ 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 ); @@ -170,7 +171,7 @@ iokit_lookup_connect_port( 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; @@ -200,7 +201,7 @@ iokit_lookup_connect_ref(io_object_t connectRef, ipc_space_t space) 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); diff --git a/osfmk/i386/acpi.c b/osfmk/i386/acpi.c index 9bd836e2c..19d39a9c8 100644 --- a/osfmk/i386/acpi.c +++ b/osfmk/i386/acpi.c @@ -140,6 +140,10 @@ acpi_hibernate(void *refcon) } } + +#if CONFIG_VMX + vmx_suspend(); +#endif kdebug_enable = 0; IOCPURunPlatformQuiesceActions(); @@ -202,13 +206,6 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) 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 */ @@ -231,6 +228,9 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) #if HIBERNATION acpi_sleep_cpu(acpi_hibernate, &data); #else +#if CONFIG_VMX + vmx_suspend(); +#endif acpi_sleep_cpu(func, refcon); #endif @@ -274,16 +274,16 @@ acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) /* 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 /* diff --git a/osfmk/i386/i386_init.c b/osfmk/i386/i386_init.c index dc528cce1..f6546ad34 100644 --- a/osfmk/i386/i386_init.c +++ b/osfmk/i386/i386_init.c @@ -560,7 +560,7 @@ do_init_slave(boolean_t fast_restart) #if CONFIG_VMX /* resume VT operation */ - vmx_resume(); + vmx_resume(FALSE); #endif #if CONFIG_MTRR diff --git a/osfmk/i386/mp.c b/osfmk/i386/mp.c index 8d13b3645..219948221 100644 --- a/osfmk/i386/mp.c +++ b/osfmk/i386/mp.c @@ -881,12 +881,14 @@ mp_rendezvous_action(void) /* 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 */ @@ -900,7 +902,7 @@ mp_rendezvous_action(void) 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 */ @@ -962,7 +964,7 @@ mp_rendezvous(void (*setup_func)(void *), 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 */ @@ -1788,7 +1790,7 @@ mp_kdp_exit(void) #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)); diff --git a/osfmk/i386/vmx/vmx_cpu.c b/osfmk/i386/vmx/vmx_cpu.c index 0b0b36580..49b36f846 100644 --- a/osfmk/i386/vmx/vmx_cpu.c +++ b/osfmk/i386/vmx/vmx_cpu.c @@ -137,6 +137,9 @@ vmx_cpu_init() 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; @@ -145,6 +148,8 @@ vmx_cpu_init() /* 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; @@ -171,6 +176,9 @@ vmx_on(void *arg __unused) 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) @@ -192,6 +200,8 @@ vmx_on(void *arg __unused) cpu->specs.vmx_on = TRUE; } + VMX_KPRINTF("[%d]vmx_on() return state: %d\n", + cpu_number(), cpu->specs.vmx_on); } /* ----------------------------------------------------------------------------- @@ -204,6 +214,9 @@ vmx_off(void *arg __unused) 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(); @@ -214,6 +227,9 @@ vmx_off(void *arg __unused) cpu->specs.vmx_on = FALSE; } + + VMX_KPRINTF("[%d]vmx_off() return state: %d\n", + cpu_number(), cpu->specs.vmx_on); } /* ----------------------------------------------------------------------------- @@ -357,14 +373,27 @@ vmx_suspend() 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); } /* ----------------------------------------------------------------------------- diff --git a/osfmk/i386/vmx/vmx_cpu.h b/osfmk/i386/vmx/vmx_cpu.h index 2abe13d50..9ee53a530 100644 --- a/osfmk/i386/vmx/vmx_cpu.h +++ b/osfmk/i386/vmx/vmx_cpu.h @@ -62,7 +62,7 @@ typedef struct vmx_cpu { 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) diff --git a/osfmk/ipc/ipc_importance.c b/osfmk/ipc/ipc_importance.c index bd48b1da7..089f6afd5 100644 --- a/osfmk/ipc/ipc_importance.c +++ b/osfmk/ipc/ipc_importance.c @@ -3020,6 +3020,7 @@ ipc_importance_get_value( 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 @@ -3054,6 +3055,7 @@ struct ipc_voucher_attr_manager ipc_importance_manager = { .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)) @@ -3156,6 +3158,7 @@ ipc_importance_get_value( 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; @@ -3167,6 +3170,7 @@ ipc_importance_get_value( if (0 != content_size) return KERN_INVALID_ARGUMENT; + *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE; /* never an out voucher */ switch (command) { diff --git a/osfmk/ipc/ipc_kmsg.c b/osfmk/ipc/ipc_kmsg.c index f5737416a..5d78a1848 100644 --- a/osfmk/ipc/ipc_kmsg.c +++ b/osfmk/ipc/ipc_kmsg.c @@ -1401,6 +1401,8 @@ ipc_kmsg_send( 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); diff --git a/osfmk/ipc/ipc_voucher.c b/osfmk/ipc/ipc_voucher.c index 47e9bdf3a..f914dec6f 100644 --- a/osfmk/ipc/ipc_voucher.c +++ b/osfmk/ipc/ipc_voucher.c @@ -183,6 +183,14 @@ static void ivgt_lookup(iv_index_t, 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); @@ -535,6 +543,7 @@ convert_voucher_to_port(ipc_voucher_t voucher) #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; \ @@ -546,6 +555,7 @@ convert_voucher_to_port(ipc_voucher_t voucher) #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; \ @@ -898,7 +908,11 @@ ivace_reference_by_index( 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); } @@ -914,7 +928,8 @@ ivace_reference_by_index( 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; @@ -942,8 +957,8 @@ restart: /* 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++; } @@ -970,6 +985,7 @@ restart: 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; @@ -1015,6 +1031,12 @@ static void ivace_release( 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; @@ -1187,6 +1209,7 @@ ipc_replace_voucher_value( 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; @@ -1226,7 +1249,7 @@ ipc_replace_voucher_value( 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; @@ -1242,7 +1265,7 @@ ipc_replace_voucher_value( * 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); /* @@ -1296,7 +1319,8 @@ ipc_directly_replace_voucher_value( * 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); /* @@ -1909,6 +1933,7 @@ ipc_register_well_known_mach_voucher_attr_manager( 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(); @@ -2333,7 +2358,6 @@ mach_voucher_attr_control_get_values( return KERN_SUCCESS; } - /* * Routine: mach_voucher_attr_control_create_mach_voucher * Purpose: @@ -2584,6 +2608,183 @@ host_register_mach_voucher_attr_manager( 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) @@ -2640,6 +2841,7 @@ user_data_get_value( 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 @@ -2674,6 +2876,7 @@ struct ipc_voucher_attr_manager user_data_manager = { .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; @@ -2829,6 +3032,7 @@ user_data_get_value( 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; @@ -2838,6 +3042,7 @@ user_data_get_value( /* never an out voucher */ *out_value_voucher = IPC_VOUCHER_NULL; + *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE; switch (command) { diff --git a/osfmk/ipc/ipc_voucher.h b/osfmk/ipc/ipc_voucher.h index a83695fac..bc8061e31 100644 --- a/osfmk/ipc/ipc_voucher.h +++ b/osfmk/ipc/ipc_voucher.h @@ -118,7 +118,8 @@ struct ivac_entry_s { 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) */ @@ -134,7 +135,7 @@ typedef ivac_entry *ivac_entry_t; #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 @@ -155,6 +156,8 @@ typedef ipc_voucher_attr_control_t iv_attr_control_t; #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) @@ -263,6 +266,7 @@ typedef kern_return_t (*ipc_voucher_attr_manager_get_value_t)(ipc_voucher_attr_m 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, @@ -285,14 +289,21 @@ typedef kern_return_t (*ipc_voucher_attr_manager_command_t)(ipc_voucher_attr_man 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 */ diff --git a/osfmk/ipc/mach_msg.c b/osfmk/ipc/mach_msg.c index 98282d730..6c4e472dc 100644 --- a/osfmk/ipc/mach_msg.c +++ b/osfmk/ipc/mach_msg.c @@ -104,6 +104,7 @@ #include #include #include +#include #include #include @@ -333,6 +334,9 @@ mach_msg_receive_results(void) #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); diff --git a/osfmk/ipc/mach_port.c b/osfmk/ipc/mach_port.c index f6750072f..c606f8147 100644 --- a/osfmk/ipc/mach_port.c +++ b/osfmk/ipc/mach_port.c @@ -2132,6 +2132,9 @@ task_set_port_space( { kern_return_t kr; + if (space == IS_NULL) + return KERN_INVALID_TASK; + is_write_lock(space); if (!is_active(space)) { diff --git a/osfmk/kern/assert.h b/osfmk/kern/assert.h index 4c60056fb..c5e2f4516 100644 --- a/osfmk/kern/assert.h +++ b/osfmk/kern/assert.h @@ -77,7 +77,10 @@ extern void Assert( #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 @@ -86,6 +89,8 @@ __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 @@ -93,6 +98,7 @@ __END_DECLS #define assert(ex) ((void)0) #define assert_static(ex) _Static_assert((ex), #ex) +#define assertf(ex, fmt, args...) ((void)0) #define __assert_only __unused diff --git a/osfmk/kern/coalition.c b/osfmk/kern/coalition.c index a21666275..fd2afa858 100644 --- a/osfmk/kern/coalition.c +++ b/osfmk/kern/coalition.c @@ -499,8 +499,8 @@ coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_us 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 */ diff --git a/osfmk/kern/host.c b/osfmk/kern/host.c index f1aa2eaf6..dba639a8c 100644 --- a/osfmk/kern/host.c +++ b/osfmk/kern/host.c @@ -77,6 +77,9 @@ #include #include +#include +#include + #include #include #include @@ -962,3 +965,11 @@ host_set_atm_diagnostic_flag(host_priv_t host_priv, uint32_t diagnostic_flag) 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); +} diff --git a/osfmk/kern/ipc_mig.c b/osfmk/kern/ipc_mig.c index 7239bdb29..5dff6a1a2 100644 --- a/osfmk/kern/ipc_mig.c +++ b/osfmk/kern/ipc_mig.c @@ -126,9 +126,16 @@ mach_msg_send_from_kernel( 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); } @@ -156,9 +163,16 @@ mach_msg_send_from_kernel_proper( 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); } @@ -186,16 +200,19 @@ mach_msg_send_from_kernel_with_options( 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) { @@ -228,14 +245,17 @@ mach_msg_send_from_kernel_with_options_legacy( 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) { @@ -342,9 +362,17 @@ mach_msg_rpc_from_kernel_body( 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; diff --git a/osfmk/kern/ipc_sync.c b/osfmk/kern/ipc_sync.c index fdc418c1d..941bd1839 100644 --- a/osfmk/kern/ipc_sync.c +++ b/osfmk/kern/ipc_sync.c @@ -75,9 +75,15 @@ port_name_to_semaphore( 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; } /* diff --git a/osfmk/kern/locks.c b/osfmk/kern/locks.c index 2f782cd12..c81671591 100644 --- a/osfmk/kern/locks.c +++ b/osfmk/kern/locks.c @@ -1103,13 +1103,13 @@ void lck_rw_clear_promotion(thread_t thread) /* 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); } } diff --git a/osfmk/kern/machine.c b/osfmk/kern/machine.c index dfe33564d..f6b498fb4 100644 --- a/osfmk/kern/machine.c +++ b/osfmk/kern/machine.c @@ -289,7 +289,17 @@ processor_doshutdown( /* 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); @@ -298,25 +308,45 @@ processor_doshutdown( } /* - *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); diff --git a/osfmk/kern/priority.c b/osfmk/kern/priority.c index ffc92cb7b..f50696079 100644 --- a/osfmk/kern/priority.c +++ b/osfmk/kern/priority.c @@ -104,6 +104,8 @@ thread_quantum_expire( 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); /* @@ -216,6 +218,8 @@ thread_quantum_expire( 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); } /* @@ -231,8 +235,16 @@ thread_quantum_expire( 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); } @@ -720,30 +732,16 @@ sched_set_thread_throttled(thread_t thread, boolean_t wants_throttle) 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); diff --git a/osfmk/kern/processor_data.h b/osfmk/kern/processor_data.h index f2a1a8ba3..799a31dc7 100644 --- a/osfmk/kern/processor_data.h +++ b/osfmk/kern/processor_data.h @@ -60,11 +60,11 @@ struct processor_data { 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 { diff --git a/osfmk/kern/sched.h b/osfmk/kern/sched.h index 1a46180a9..d8f470150 100644 --- a/osfmk/kern/sched.h +++ b/osfmk/kern/sched.h @@ -335,9 +335,11 @@ extern uint32_t sched_fixed_shift; 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; diff --git a/osfmk/kern/sched_average.c b/osfmk/kern/sched_average.c index a23ef953f..411dfb47c 100644 --- a/osfmk/kern/sched_average.c +++ b/osfmk/kern/sched_average.c @@ -71,7 +71,9 @@ #if CONFIG_TELEMETRY #include #endif - + +#include + uint32_t avenrun[3] = {0, 0, 0}; uint32_t mach_factor[3] = {0, 0, 0}; @@ -205,6 +207,10 @@ compute_averages(uint64_t stdelta) 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. */ diff --git a/osfmk/kern/sched_prim.c b/osfmk/kern/sched_prim.c index 5ee363dc7..8c70db47d 100644 --- a/osfmk/kern/sched_prim.c +++ b/osfmk/kern/sched_prim.c @@ -200,9 +200,10 @@ int sched_pri_decay_band_limit = DEFAULT_DECAY_BAND_LIMIT; 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; @@ -478,6 +479,7 @@ sched_timeshare_timebase_init(void) assert((abstime >> 32) == 0 && (uint32_t)abstime != 0); sched_telemetry_interval = (uint32_t)abstime; #endif + } #endif /* CONFIG_SCHED_TIMESHARE_CORE */ @@ -2736,6 +2738,14 @@ thread_block_reason( /* 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; @@ -3951,6 +3961,8 @@ set_sched_pri( 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, @@ -3958,8 +3970,6 @@ set_sched_pri( 0, /* eventually, 'reason' */ 0); - thread->sched_pri = priority; - if (is_current_thread) { nurgency = thread_get_urgency(thread, &urgency_param1, &urgency_param2); /* @@ -4406,6 +4416,7 @@ sched_startup(void) 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) @@ -4515,6 +4526,7 @@ sched_timeshare_maintenance_continue(void) */ 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, @@ -4584,7 +4596,10 @@ sched_init_thread(void (*continuation)(void)) { thread_block(THREAD_CONTINUE_NULL); - sched_maintenance_thread = current_thread(); + thread_t thread = current_thread(); + + sched_maintenance_thread = thread; + continuation(); /*NOTREACHED*/ diff --git a/osfmk/kern/startup.c b/osfmk/kern/startup.c index eada1fb64..ac6acbc04 100644 --- a/osfmk/kern/startup.c +++ b/osfmk/kern/startup.c @@ -182,6 +182,7 @@ void scale_setup(void); 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. @@ -289,6 +290,9 @@ kernel_bootstrap(void) doprnt_hide_pointers = FALSE; } + kernel_bootstrap_log("console_init"); + console_init(); + kernel_bootstrap_log("stackshot_lock_init"); stackshot_lock_init(); diff --git a/osfmk/kern/syscall_subr.c b/osfmk/kern/syscall_subr.c index f0c067b03..1ebf39e4f 100644 --- a/osfmk/kern/syscall_subr.c +++ b/osfmk/kern/syscall_subr.c @@ -415,6 +415,14 @@ thread_depress_abstime( 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; @@ -510,6 +518,14 @@ thread_poll_yield( 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; diff --git a/osfmk/kern/task.c b/osfmk/kern/task.c index d0e982ee4..7de13feab 100644 --- a/osfmk/kern/task.c +++ b/osfmk/kern/task.c @@ -343,6 +343,34 @@ task_atm_reset(__unused task_t task) { } +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 @@ -1080,17 +1108,12 @@ task_deallocate( /* * 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)); @@ -4355,7 +4378,7 @@ task_wakeups_monitor_ctl(task_t task, uint32_t *flags, int32_t *rate_hz) * 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); diff --git a/osfmk/kern/task.h b/osfmk/kern/task.h index 5ddff0c75..9f1192a4a 100644 --- a/osfmk/kern/task.h +++ b/osfmk/kern/task.h @@ -812,6 +812,8 @@ extern void task_importance_mark_receiver(task_t task, boolean_t receiving); 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 diff --git a/osfmk/kern/thread.c b/osfmk/kern/thread.c index 535d1e319..64b5ea604 100644 --- a/osfmk/kern/thread.c +++ b/osfmk/kern/thread.c @@ -239,7 +239,7 @@ thread_bootstrap(void) 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; @@ -1011,12 +1011,9 @@ thread_create_internal( 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 */ @@ -2496,6 +2493,17 @@ thread_get_current_voucher_origin_pid( 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) { diff --git a/osfmk/kern/thread.h b/osfmk/kern/thread.h index 07fc07fd3..bec1a5105 100644 --- a/osfmk/kern/thread.h +++ b/osfmk/kern/thread.h @@ -161,6 +161,7 @@ struct 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 */ @@ -1006,6 +1007,8 @@ extern void thread_update_io_stats(thread_t thread, int size, int io_flags); 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 */ diff --git a/osfmk/kern/thread_act.c b/osfmk/kern/thread_act.c index 6c1b2e379..1d1376e81 100644 --- a/osfmk/kern/thread_act.c +++ b/osfmk/kern/thread_act.c @@ -795,6 +795,14 @@ special_handler_continue(void) 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); diff --git a/osfmk/kern/thread_policy.c b/osfmk/kern/thread_policy.c index 9a82a198f..a7043c78b 100644 --- a/osfmk/kern/thread_policy.c +++ b/osfmk/kern/thread_policy.c @@ -838,17 +838,12 @@ thread_task_priority( 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); @@ -880,6 +875,11 @@ thread_policy_reset( 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); @@ -899,11 +899,11 @@ thread_policy_reset( 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); diff --git a/osfmk/mach/mach_host.defs b/osfmk/mach/mach_host.defs index bc44a2842..1e4bb3d39 100644 --- a/osfmk/mach/mach_host.defs +++ b/osfmk/mach/mach_host.defs @@ -325,4 +325,27 @@ routine mach_memory_info( 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 : */ diff --git a/osfmk/mach/mach_voucher_types.h b/osfmk/mach/mach_voucher_types.h index 0f3d674bc..0c5a4b516 100644 --- a/osfmk/mach/mach_voucher_types.h +++ b/osfmk/mach/mach_voucher_types.h @@ -136,6 +136,8 @@ typedef mach_voucher_attr_recipe_command_t *mach_voucher_attr_recipe_command_arr #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) @@ -229,6 +231,9 @@ typedef mach_msg_type_number_t mach_voucher_attr_value_handle_array_size_t; #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; diff --git a/osfmk/mach/machine.h b/osfmk/mach/machine.h index 9c459178a..09ea8bb8a 100644 --- a/osfmk/mach/machine.h +++ b/osfmk/mach/machine.h @@ -405,6 +405,7 @@ __END_DECLS #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 diff --git a/osfmk/mach/message.h b/osfmk/mach/message.h index b3769d484..9b483dd5a 100644 --- a/osfmk/mach/message.h +++ b/osfmk/mach/message.h @@ -711,18 +711,16 @@ typedef integer_t mach_msg_option_t; /* 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 */ diff --git a/osfmk/vm/vm_apple_protect.c b/osfmk/vm/vm_apple_protect.c index 81301fd11..91208ca11 100644 --- a/osfmk/vm/vm_apple_protect.c +++ b/osfmk/vm/vm_apple_protect.c @@ -172,7 +172,7 @@ decl_lck_mtx_data(,apple_protect_pager_lock) /* * 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. diff --git a/osfmk/vm/vm_compressor_backing_store.c b/osfmk/vm/vm_compressor_backing_store.c index f21599eb6..33f4f7cc4 100644 --- a/osfmk/vm/vm_compressor_backing_store.c +++ b/osfmk/vm/vm_compressor_backing_store.c @@ -702,7 +702,6 @@ vm_swapout_thread(void) 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); diff --git a/osfmk/vm/vm_fault.c b/osfmk/vm/vm_fault.c index 655c302d2..8a0d7b95f 100644 --- a/osfmk/vm/vm_fault.c +++ b/osfmk/vm/vm_fault.c @@ -2886,11 +2886,12 @@ vm_fault_enter(vm_page_t m, } 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 @@ -2905,7 +2906,6 @@ vm_fault_enter(vm_page_t m, * 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; diff --git a/osfmk/vm/vm_map.c b/osfmk/vm/vm_map.c index ac2edb6a5..773ab89ff 100644 --- a/osfmk/vm/vm_map.c +++ b/osfmk/vm/vm_map.c @@ -4376,6 +4376,7 @@ vm_map_submap( return(result); } + /* * vm_map_protect: * @@ -4549,6 +4550,7 @@ 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, diff --git a/osfmk/vm/vm_resident.c b/osfmk/vm/vm_resident.c index 25c1edb26..2ad202d0e 100644 --- a/osfmk/vm/vm_resident.c +++ b/osfmk/vm/vm_resident.c @@ -7236,10 +7236,16 @@ process_account(mach_memory_info_t * sites, unsigned int __unused num_sites) } 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); diff --git a/osfmk/x86_64/pmap.c b/osfmk/x86_64/pmap.c index 45be58257..209e96335 100644 --- a/osfmk/x86_64/pmap.c +++ b/osfmk/x86_64/pmap.c @@ -1334,12 +1334,14 @@ pmap_create_options( 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); diff --git a/tools/lldbmacros/bank.py b/tools/lldbmacros/bank.py index 6874636ae..e54ee3487 100644 --- a/tools/lldbmacros/bank.py +++ b/tools/lldbmacros/bank.py @@ -3,18 +3,18 @@ from utils import * @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}" @@ -26,15 +26,15 @@ def GetBankElementSummary(bank_element): @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}" @@ -45,15 +45,15 @@ def GetBankTaskSummary(bank_task): @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}" diff --git a/tools/lldbmacros/ipc.py b/tools/lldbmacros/ipc.py index d9c3745af..f06087393 100644 --- a/tools/lldbmacros/ipc.py +++ b/tools/lldbmacros/ipc.py @@ -1090,6 +1090,8 @@ def GetBankHandleSummary(handle_ptr): 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 *') diff --git a/tools/tests/MPMMTest/KQMPMMtest.c b/tools/tests/MPMMTest/KQMPMMtest.c index 0686350f3..66f15b320 100644 --- a/tools/tests/MPMMTest/KQMPMMtest.c +++ b/tools/tests/MPMMTest/KQMPMMtest.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -841,7 +842,9 @@ int main(int argc, char *argv[]) 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); diff --git a/tools/tests/MPMMTest/MPMMtest.c b/tools/tests/MPMMTest/MPMMtest.c index 17b0a1acb..7dc344fd4 100644 --- a/tools/tests/MPMMTest/MPMMtest.c +++ b/tools/tests/MPMMTest/MPMMtest.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -952,7 +953,9 @@ int main(int argc, char *argv[]) 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) { diff --git a/tools/tests/MPMMTest/MPMMtest_run.sh b/tools/tests/MPMMTest/MPMMtest_run.sh new file mode 100755 index 000000000..517c730eb --- /dev/null +++ b/tools/tests/MPMMTest/MPMMtest_run.sh @@ -0,0 +1,36 @@ +#!/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; + + diff --git a/tools/tests/MPMMTest/Makefile b/tools/tests/MPMMTest/Makefile index f5cb5de8b..d24156460 100644 --- a/tools/tests/MPMMTest/Makefile +++ b/tools/tests/MPMMTest/Makefile @@ -26,7 +26,7 @@ DSTROOT?=$(shell /bin/pwd) 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)) @@ -54,5 +54,9 @@ $(DSTROOT)/KQMPMMtest_64D: KQMPMMtest.c ${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 diff --git a/tools/tests/Makefile.common b/tools/tests/Makefile.common index 70e7f02bf..06b7e36f1 100644 --- a/tools/tests/Makefile.common +++ b/tools/tests/Makefile.common @@ -46,3 +46,11 @@ else endif DEPLOYMENT_TARGET_DEFINES = -DPLATFORM_$(PLATFORM) + +ifeq ($(RC_XBS),YES) +_v = +else ifeq ($(VERBOSE),YES) +_v = +else +_v = @ +endif diff --git a/tools/tests/execperf/Makefile b/tools/tests/execperf/Makefile index ebf4bcdbd..e67fe1313 100644 --- a/tools/tests/execperf/Makefile +++ b/tools/tests/execperf/Makefile @@ -39,25 +39,25 @@ clean: 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 diff --git a/tools/tests/execperf/test.sh b/tools/tests/execperf/test.sh index c9a940bd2..810fa9c13 100755 --- a/tools/tests/execperf/test.sh +++ b/tools/tests/execperf/test.sh @@ -24,11 +24,17 @@ if [ "${PERFDATA_DIR}" == "" ]; then fi case "$PRODUCT" in + "Watch OS") + COUNT=500 + ;; "iPhone OS") COUNT=1000 ;; + "Mac OS X") + COUNT=6000 + ;; *) - COUNT=10000 + COUNT=1000 ;; esac diff --git a/tools/tests/personas/Makefile b/tools/tests/personas/Makefile new file mode 100644 index 000000000..f3421742f --- /dev/null +++ b/tools/tests/personas/Makefile @@ -0,0 +1,37 @@ +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 diff --git a/tools/tests/personas/persona_mgr.c b/tools/tests/personas/persona_mgr.c new file mode 100644 index 000000000..f6776c873 --- /dev/null +++ b/tools/tests/personas/persona_mgr.c @@ -0,0 +1,291 @@ +/* + * persona_mgr.c + * Tool to manage personas + * + * Jeremy C. Andrus + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "persona_test.h" + +/* internal */ +#include +#include +#include +#include +#include + +#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; +} diff --git a/tools/tests/personas/persona_spawn.c b/tools/tests/personas/persona_spawn.c new file mode 100644 index 000000000..b6e7782ce --- /dev/null +++ b/tools/tests/personas/persona_spawn.c @@ -0,0 +1,389 @@ +/* + * spawn_persona.c + * Use new POSIX spawn attributes to create a new process in a persona + * + * Jeremy C. Andrus + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "persona_test.h" + +/* internal */ +#include +#include +#include +#include +#include + +#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; +} diff --git a/tools/tests/personas/persona_test.h b/tools/tests/personas/persona_test.h new file mode 100644 index 000000000..e88d08ad7 --- /dev/null +++ b/tools/tests/personas/persona_test.h @@ -0,0 +1,216 @@ +/* + * persona_test.h + * + * Jeremy C. Andrus + * + */ +#ifndef _PERSONA_TEST_H_ +#define _PERSONA_TEST_H_ + +/* internal */ +#include +#include +#include + +//#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_ */ -- 2.45.2