]> git.saurik.com Git - apple/xnu.git/commitdiff
xnu-3248.40.184.tar.gz os-x-10114 v3248.40.184
authorApple <opensource@apple.com>
Wed, 13 Jul 2016 22:52:56 +0000 (22:52 +0000)
committerApple <opensource@apple.com>
Wed, 13 Jul 2016 22:52:56 +0000 (22:52 +0000)
187 files changed:
bsd/bsm/audit_kevents.h
bsd/conf/files
bsd/hfs/hfs_quota.c
bsd/kern/bsd_init.c
bsd/kern/kdebug.c
bsd/kern/kern_credential.c
bsd/kern/kern_cs.c
bsd/kern/kern_csr.c
bsd/kern/kern_descrip.c
bsd/kern/kern_event.c
bsd/kern/kern_exec.c
bsd/kern/kern_exit.c
bsd/kern/kern_fork.c
bsd/kern/kern_memorystatus.c
bsd/kern/kern_persona.c [new file with mode: 0644]
bsd/kern/kern_proc.c
bsd/kern/kern_prot.c
bsd/kern/kern_shutdown.c
bsd/kern/kern_xxx.c
bsd/kern/kpi_socket.c
bsd/kern/kpi_socketfilter.c
bsd/kern/mach_loader.c
bsd/kern/mach_loader.h
bsd/kern/posix_sem.c
bsd/kern/posix_shm.c
bsd/kern/sys_persona.c [new file with mode: 0644]
bsd/kern/syscalls.master
bsd/kern/sysv_msg.c
bsd/kern/trace.codes
bsd/kern/ubc_subr.c
bsd/kern/uipc_socket.c
bsd/kern/uipc_socket2.c
bsd/kern/uipc_syscalls.c
bsd/kern/uipc_usrreq.c
bsd/miscfs/Makefile
bsd/miscfs/routefs/Makefile [new file with mode: 0644]
bsd/miscfs/routefs/routefs.h [new file with mode: 0644]
bsd/miscfs/routefs/routefs_ops.c [new file with mode: 0644]
bsd/net/dlil.c
bsd/net/if.c
bsd/net/if_var.h
bsd/net/kpi_interface.c
bsd/net/necp.c
bsd/net/ntstat.c
bsd/net/ntstat.h
bsd/net/pf.c
bsd/net/pf_norm.c
bsd/netinet/flow_divert.c
bsd/netinet/flow_divert.h
bsd/netinet/flow_divert_proto.h
bsd/netinet/in.c
bsd/netinet/in_pcblist.c
bsd/netinet/ip_output.c
bsd/netinet/mp_pcb.c
bsd/netinet/mp_proto.c
bsd/netinet/mptcp.c
bsd/netinet/mptcp_opt.c
bsd/netinet/mptcp_subr.c
bsd/netinet/mptcp_usrreq.c
bsd/netinet/mptcp_var.h
bsd/netinet/tcp_input.c
bsd/netinet/tcp_output.c
bsd/netinet/tcp_subr.c
bsd/netinet/tcp_timer.c
bsd/netinet/tcp_usrreq.c
bsd/netinet/tcp_var.h
bsd/netinet6/esp_input.c
bsd/netinet6/ip6_forward.c
bsd/netinet6/ipsec.c
bsd/netinet6/nd6_rtr.c
bsd/netinet6/udp6_usrreq.c
bsd/netkey/key.c
bsd/sys/Makefile
bsd/sys/codedir_internal.h
bsd/sys/codesign.h
bsd/sys/csr.h
bsd/sys/fsctl.h
bsd/sys/imgact.h
bsd/sys/kdebug.h
bsd/sys/kern_memorystatus.h
bsd/sys/mount.h
bsd/sys/mount_internal.h
bsd/sys/persona.h [new file with mode: 0644]
bsd/sys/proc.h
bsd/sys/proc_internal.h
bsd/sys/reboot.h
bsd/sys/socket.h
bsd/sys/socketvar.h
bsd/sys/spawn_internal.h
bsd/sys/ubc_internal.h
bsd/sys/vnode.h
bsd/vfs/vfs_cache.c
bsd/vfs/vfs_conf.c
bsd/vfs/vfs_subr.c
bsd/vfs/vfs_syscalls.c
bsd/vm/vm_unix.c
config/MASTER
config/MASTER.x86_64
config/MasterVersion
config/Private.exports
config/Private.x86_64.exports
iokit/IOKit/IOKitKeysPrivate.h
iokit/IOKit/IOPolledInterface.h
iokit/IOKit/IOService.h
iokit/IOKit/IOUserClient.h
iokit/Kernel/IOCPU.cpp
iokit/Kernel/IODeviceTreeSupport.cpp
iokit/Kernel/IOMemoryDescriptor.cpp
iokit/Kernel/IOPMrootDomain.cpp
iokit/Kernel/IOPolledInterface.cpp
iokit/Kernel/IOService.cpp
iokit/Kernel/IOUserClient.cpp
iokit/bsddev/IOKitBSDInit.cpp
libkern/Makefile
libkern/os/Makefile [new file with mode: 0644]
libkern/os/overflow.h [new file with mode: 0644]
libsyscall/Libsyscall.xcodeproj/project.pbxproj
libsyscall/mach/host.c
libsyscall/wrappers/persona.c [new file with mode: 0644]
libsyscall/wrappers/spawn/posix_spawn.c
libsyscall/wrappers/spawn/spawn_private.h
osfmk/Makefile
osfmk/atm/atm.c
osfmk/bank/bank.c
osfmk/bank/bank_internal.h
osfmk/bank/bank_types.h
osfmk/console/Makefile
osfmk/console/i386/serial_console.c
osfmk/console/video_console.c
osfmk/console/video_console.h
osfmk/device/device.defs
osfmk/device/device_types.h
osfmk/device/iokit_rpc.c
osfmk/i386/acpi.c
osfmk/i386/i386_init.c
osfmk/i386/mp.c
osfmk/i386/vmx/vmx_cpu.c
osfmk/i386/vmx/vmx_cpu.h
osfmk/ipc/ipc_importance.c
osfmk/ipc/ipc_kmsg.c
osfmk/ipc/ipc_voucher.c
osfmk/ipc/ipc_voucher.h
osfmk/ipc/mach_msg.c
osfmk/ipc/mach_port.c
osfmk/kern/assert.h
osfmk/kern/coalition.c
osfmk/kern/host.c
osfmk/kern/ipc_mig.c
osfmk/kern/ipc_sync.c
osfmk/kern/locks.c
osfmk/kern/machine.c
osfmk/kern/priority.c
osfmk/kern/processor_data.h
osfmk/kern/sched.h
osfmk/kern/sched_average.c
osfmk/kern/sched_prim.c
osfmk/kern/startup.c
osfmk/kern/syscall_subr.c
osfmk/kern/task.c
osfmk/kern/task.h
osfmk/kern/thread.c
osfmk/kern/thread.h
osfmk/kern/thread_act.c
osfmk/kern/thread_policy.c
osfmk/mach/mach_host.defs
osfmk/mach/mach_voucher_types.h
osfmk/mach/machine.h
osfmk/mach/message.h
osfmk/vm/vm_apple_protect.c
osfmk/vm/vm_compressor_backing_store.c
osfmk/vm/vm_fault.c
osfmk/vm/vm_map.c
osfmk/vm/vm_resident.c
osfmk/x86_64/pmap.c
tools/lldbmacros/bank.py
tools/lldbmacros/ipc.py
tools/tests/MPMMTest/KQMPMMtest.c
tools/tests/MPMMTest/MPMMtest.c
tools/tests/MPMMTest/MPMMtest_run.sh [new file with mode: 0755]
tools/tests/MPMMTest/Makefile
tools/tests/Makefile.common
tools/tests/execperf/Makefile
tools/tests/execperf/test.sh
tools/tests/personas/Makefile [new file with mode: 0644]
tools/tests/personas/persona_mgr.c [new file with mode: 0644]
tools/tests/personas/persona_spawn.c [new file with mode: 0644]
tools/tests/personas/persona_test.h [new file with mode: 0644]

index e6ea2a05b0c7f86951b3ee1c263b363efe972bae..eb75536e26dab188bd1cfce54ff386679e71a875 100644 (file)
 #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_ */
index 2e3baa92b6d6cbdc22531aa3b972bf792f9c0b3f..190d2964b0138676bef9ab9de71182b4ef4b81f3 100644 (file)
@@ -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
index 989bd67bf1f728548eef2b8d5cc809ab9fe879c9..10a9e4e8b73240c4c09a17676d010a2d72f20c0c 100644 (file)
@@ -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);
 
index 4f871df3293a65ac1eefa69b915302a421bf03d0..8cf33e20a423093f58edb3e319ce4f8427925028 100644 (file)
@@ -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;
index 505fbf81da0e8a55c0bd5c0c62b206cadbaa1664..556e0d0bb6e2391be8a296fd4ae547bb5c9ee9a7 100644 (file)
@@ -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);
        }
 }
 
index 0d2a07e02636ba788543ca7d68d4ca45f96444ac..0558ce4499be9acb0d3172f431e008ba1eec3786 100644 (file)
@@ -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);
index fb3a2012cc90b9fe60c8f0dc7bab43542f37cb06..c15dd4f118ed0f1df620e90bf8b4b219b45be412 100644 (file)
@@ -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
index 4b5c26815c9c1035a9300fdcd5d1884bf796398f..1805b76e77d161137236f58a82f988e35b1ed8ad 100644 (file)
@@ -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
  * 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
  */
index 62a9d94f730065bccdf78dd1b77d895ad243f45d..e089c5363b9c89eeb31b17b26b1d81985ddbc06e 100644 (file)
@@ -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;
index d80579a350d0045fd778141825b923ba4073f15a..6bc84137c321faccf14151711f9b4989cff444c8 100644 (file)
@@ -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
index e2e7d1526403588d395587c067f781de55fe3e5b..20b1f0317a610aa49e332cbc21555defa5903312 100644 (file)
 #include <sys/signal.h>
 #include <sys/aio_kern.h>
 #include <sys/sysproto.h>
+#include <sys/persona.h>
 #if SYSV_SHM
 #include <sys/shm_internal.h>          /* shmexec() */
 #endif
 #include <kern/assert.h>
 #include <kern/task.h>
 #include <kern/coalition.h>
+#include <kern/kalloc.h>
 
 #if CONFIG_MACF
 #include <security/mac.h>
@@ -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]
  *     <ex_imgact>:???                 [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) {
                                /*
index fca7ab3293c0d0ec34269589e654d2b0ef8d2827..d99430bd0aa50498fb02c1b4841d7edd79de434e 100644 (file)
 #include <sys/shm_internal.h>  /* shmexit */
 #endif
 #include <sys/acct.h>          /* acct_process */
+#if CONFIG_PERSONAS
+#include <sys/persona.h>
+#endif
 
 #include <security/audit/audit.h>
 #include <bsm/audit_kevents.h>
@@ -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);
 
        /*
index 7b3e2440e5900b36c05b8ea0cddb357008700b66..e23d52ead5f6cecf5963efc58a8b9249a8235cb1 100644 (file)
@@ -93,7 +93,9 @@
 #include <sys/acct.h>
 #include <sys/codesign.h>
 #include <sys/sysproto.h>
-
+#if CONFIG_PERSONAS
+#include <sys/persona.h>
+#endif
 #if CONFIG_DTRACE
 /* Do not include dtrace.h, it redefines kmem_[alloc/free] */
 extern void dtrace_fasttrap_fork(proc_t, proc_t);
@@ -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;
index 97296a8a87a2327e7680f5c2af7168ab3a9e17ab..22f7edbd3572dd87a491255d28a695f9e107dbf0 100644 (file)
@@ -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 (file)
index 0000000..5638b79
--- /dev/null
@@ -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 <sys/kernel.h>
+#include <sys/kernel_types.h>
+#include <sys/persona.h>
+
+#if CONFIG_PERSONAS
+#include <kern/assert.h>
+#include <kern/simple_lock.h>
+#include <kern/task.h>
+#include <kern/zalloc.h>
+
+#include <sys/param.h>
+#include <sys/proc_internal.h>
+#include <sys/kauth.h>
+#include <sys/proc_info.h>
+#include <sys/resourcevar.h>
+
+#define pna_info(fmt, ...) \
+       printf("%s:  " fmt "\n", __func__, ## __VA_ARGS__)
+
+#define pna_err(fmt, ...) \
+       printf("ERROR[%s]:  " fmt "\n", __func__, ## __VA_ARGS__)
+
+#define MAX_PERSONAS     512
+
+#define TEMP_PERSONA_ID  499
+
+#define FIRST_PERSONA_ID 501
+#define PERSONA_ID_STEP   10
+
+#define PERSONA_SYSTEM_UID    ((uid_t)99)
+#define PERSONA_SYSTEM_LOGIN  "system"
+
+#define PERSONA_MAGIC         (0x0aa55aa0)
+#define persona_valid(p)      ((p)->pna_valid == PERSONA_MAGIC)
+#define persona_mkinvalid(p)  ((p)->pna_valid = ~(PERSONA_MAGIC))
+
+static LIST_HEAD(personalist, persona) all_personas;
+static uint32_t g_total_personas;
+uint32_t g_max_personas = MAX_PERSONAS;
+
+struct persona *g_system_persona = NULL;
+
+static uid_t g_next_persona_id;
+
+lck_mtx_t all_personas_lock;
+lck_attr_t *persona_lck_attr;
+lck_grp_t *persona_lck_grp;
+lck_grp_attr_t *persona_lck_grp_attr;
+
+static zone_t persona_zone;
+
+kauth_cred_t g_default_persona_cred;
+
+#define lock_personas()    lck_mtx_lock(&all_personas_lock)
+#define unlock_personas()  lck_mtx_unlock(&all_personas_lock)
+
+
+extern void mach_kauth_cred_uthread_update(void);
+
+void personas_bootstrap(void)
+{
+       struct posix_cred pcred;
+
+       persona_dbg("Initializing persona subsystem");
+       LIST_INIT(&all_personas);
+       g_total_personas = 0;
+
+       g_next_persona_id = FIRST_PERSONA_ID;
+
+       persona_lck_grp_attr = lck_grp_attr_alloc_init();
+       lck_grp_attr_setstat(persona_lck_grp_attr);
+
+       persona_lck_grp = lck_grp_alloc_init("personas", persona_lck_grp_attr);
+       persona_lck_attr = lck_attr_alloc_init();
+
+       lck_mtx_init(&all_personas_lock, persona_lck_grp, persona_lck_attr);
+
+       persona_zone = zinit(sizeof(struct persona),
+                            MAX_PERSONAS * sizeof(struct persona),
+                            MAX_PERSONAS, "personas");
+       assert(persona_zone != NULL);
+
+       /*
+        * setup the default credentials that a persona temporarily
+        * inherits (to work around kauth APIs)
+        */
+       bzero(&pcred, sizeof(pcred));
+       pcred.cr_uid = pcred.cr_ruid = pcred.cr_svuid = TEMP_PERSONA_ID;
+       pcred.cr_rgid = pcred.cr_svgid = TEMP_PERSONA_ID;
+       pcred.cr_groups[0] = TEMP_PERSONA_ID;
+       pcred.cr_ngroups = 1;
+       pcred.cr_flags = CRF_NOMEMBERD;
+       pcred.cr_gmuid = KAUTH_UID_NONE;
+
+       g_default_persona_cred = posix_cred_create(&pcred);
+       if (!g_default_persona_cred)
+               panic("couldn't create default persona credentials!");
+
+       g_system_persona = persona_alloc(PERSONA_SYSTEM_UID,
+                                        PERSONA_SYSTEM_LOGIN,
+                                        PERSONA_SYSTEM, NULL);
+       assert(g_system_persona != NULL);
+}
+
+struct persona *persona_alloc(uid_t id, const char *login, int type, int *error)
+{
+       struct persona *persona, *tmp;
+       int err = 0;
+       kauth_cred_t tmp_cred;
+       gid_t new_group;
+
+       if (!login) {
+               pna_err("Must provide a login name for a new persona!");
+               if (error)
+                       *error = EINVAL;
+               return NULL;
+       }
+
+       if (type <= PERSONA_INVALID || type > PERSONA_TYPE_MAX) {
+               pna_err("Invalid type: %d", type);
+               if (error)
+                       *error = EINVAL;
+               return NULL;
+       }
+
+       persona = (struct persona *)zalloc(persona_zone);
+       if (!persona) {
+               if (error)
+                       *error = ENOMEM;
+               return NULL;
+       }
+
+       bzero(persona, sizeof(*persona));
+
+       if (hw_atomic_add(&g_total_personas, 1) > MAX_PERSONAS) {
+               /* too many personas! */
+               pna_err("too many active personas!");
+               err = EBUSY;
+               goto out_error;
+       }
+
+       strncpy(persona->pna_login, login, sizeof(persona->pna_login)-1);
+
+       LIST_INIT(&persona->pna_members);
+       lck_mtx_init(&persona->pna_lock, persona_lck_grp, persona_lck_attr);
+       persona->pna_refcount = 1;
+
+       /*
+        * Setup initial (temporary) kauth_cred structure
+        * We need to do this here because all kauth calls require
+        * an existing cred structure.
+        */
+       persona->pna_cred = kauth_cred_create(g_default_persona_cred);
+       if (!persona->pna_cred) {
+               pna_err("could not copy initial credentials!");
+               err = EIO;
+               goto out_error;
+       }
+
+       lock_personas();
+try_again:
+       if (id != PERSONA_ID_NONE)
+               persona->pna_id = id;
+       else
+               persona->pna_id = g_next_persona_id;
+
+       persona_dbg("Adding %d (%s) to global list...", persona->pna_id, persona->pna_login);
+
+       err = 0;
+       LIST_FOREACH(tmp, &all_personas, pna_list) {
+               if (id == PERSONA_ID_NONE && tmp->pna_id == id) {
+                       /*
+                        * someone else manually claimed this ID, and we're
+                        * trying to allocate an ID for the caller: try again
+                        */
+                       g_next_persona_id += PERSONA_ID_STEP;
+                       goto try_again;
+               }
+               if (strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0
+                   || tmp->pna_id == id) {
+                       /*
+                        * Disallow use of identical login names and re-use
+                        * of previously allocated persona IDs
+                        */
+                       err = EEXIST;
+                       break;
+               }
+       }
+       if (err)
+               goto out_unlock;
+
+       /* ensure the cred has proper UID/GID defaults */
+       kauth_cred_ref(persona->pna_cred);
+       tmp_cred = kauth_cred_setuidgid(persona->pna_cred,
+                                       persona->pna_id,
+                                       persona->pna_id);
+       kauth_cred_unref(&persona->pna_cred);
+       if (tmp_cred != persona->pna_cred)
+               persona->pna_cred = tmp_cred;
+
+       if (!persona->pna_cred) {
+               err = EACCES;
+               goto out_unlock;
+       }
+
+       /* it should be a member of exactly 1 group (equal to its UID) */
+       new_group = (gid_t)persona->pna_id;
+
+       kauth_cred_ref(persona->pna_cred);
+       /* opt _out_ of memberd as a default */
+       tmp_cred = kauth_cred_setgroups(persona->pna_cred,
+                                       &new_group, 1, KAUTH_UID_NONE);
+       kauth_cred_unref(&persona->pna_cred);
+       if (tmp_cred != persona->pna_cred)
+               persona->pna_cred = tmp_cred;
+
+       if (!persona->pna_cred) {
+               err = EACCES;
+               goto out_unlock;
+       }
+
+       persona->pna_type = type;
+
+       /* insert the, now valid, persona into the global list! */
+       persona->pna_valid = PERSONA_MAGIC;
+       LIST_INSERT_HEAD(&all_personas, persona, pna_list);
+
+       /* if the kernel supplied the persona ID, increment for next time */
+       if (id == PERSONA_ID_NONE)
+               g_next_persona_id += PERSONA_ID_STEP;
+
+out_unlock:
+       unlock_personas();
+
+       if (err) {
+               switch (err) {
+               case EEXIST:
+                       persona_dbg("Login '%s' (%d) already exists",
+                                   login, persona->pna_id);
+                       break;
+               case EACCES:
+                       persona_dbg("kauth_error for persona:%d", persona->pna_id);
+                       break;
+               default:
+                       persona_dbg("Unknown error:%d", err);
+               }
+               goto out_error;
+       }
+
+       return persona;
+
+out_error:
+       (void)hw_atomic_add(&g_total_personas, -1);
+       zfree(persona_zone, persona);
+       if (error)
+               *error = err;
+       return NULL;
+}
+
+int persona_invalidate(struct persona *persona)
+{
+       int error = 0;
+       if (!persona)
+               return EINVAL;
+
+       lock_personas();
+       persona_lock(persona);
+
+       if (!persona_valid(persona))
+               panic("Double-invalidation of persona %p", persona);
+
+       LIST_REMOVE(persona, pna_list);
+       if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX)
+               panic("persona ref count underflow!\n");
+       persona_mkinvalid(persona);
+
+       persona_unlock(persona);
+       unlock_personas();
+
+       return error;
+}
+
+static struct persona *persona_get_locked(struct persona *persona)
+{
+       if (persona->pna_refcount) {
+               persona->pna_refcount++;
+               return persona;
+       }
+       return NULL;
+}
+
+struct persona *persona_get(struct persona *persona)
+{
+       struct persona *ret;
+       if (!persona)
+               return NULL;
+       persona_lock(persona);
+       ret = persona_get_locked(persona);
+       persona_unlock(persona);
+
+       return ret;
+}
+
+void persona_put(struct persona *persona)
+{
+       int destroy = 0;
+
+       if (!persona)
+               return;
+
+       persona_lock(persona);
+       if (persona->pna_refcount >= 0) {
+               if (--(persona->pna_refcount) == 0)
+                       destroy = 1;
+       }
+       persona_unlock(persona);
+
+       if (!destroy)
+               return;
+
+       persona_dbg("Destroying persona %s", persona_desc(persona, 0));
+
+       /* release our credential reference */
+       if (persona->pna_cred)
+               kauth_cred_unref(&persona->pna_cred);
+
+       /* remove it from the global list and decrement the count */
+       lock_personas();
+       if (persona_valid(persona)) {
+               LIST_REMOVE(persona, pna_list);
+               if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX)
+                       panic("persona count underflow!\n");
+               persona_mkinvalid(persona);
+       }
+       unlock_personas();
+
+       assert(LIST_EMPTY(&persona->pna_members));
+       memset(persona, 0, sizeof(*persona));
+       zfree(persona_zone, persona);
+}
+
+uid_t persona_get_id(struct persona *persona)
+{
+       if (persona)
+               return persona->pna_id;
+       return PERSONA_ID_NONE;
+}
+
+struct persona *persona_lookup(uid_t id)
+{
+       struct persona *persona, *tmp;
+
+       persona = NULL;
+
+       /*
+        * simple, linear lookup for now: there shouldn't be too many
+        * of these in memory at any given time.
+        */
+       lock_personas();
+       LIST_FOREACH(tmp, &all_personas, pna_list) {
+               persona_lock(tmp);
+               if (tmp->pna_id == id && persona_valid(tmp)) {
+                       persona = persona_get_locked(tmp);
+                       persona_unlock(tmp);
+                       break;
+               }
+               persona_unlock(tmp);
+       }
+       unlock_personas();
+
+       return persona;
+}
+
+int persona_find(const char *login, uid_t uid,
+                struct persona **persona, size_t *plen)
+{
+       struct persona *tmp;
+       int match = 0;
+       size_t found = 0;
+
+       if (login)
+               match++;
+       if (uid != PERSONA_ID_NONE)
+               match++;
+
+       if (match == 0)
+               return EINVAL;
+
+       persona_dbg("Searching with %d parameters (l:\"%s\", u:%d)",
+                   match, login, uid);
+
+       lock_personas();
+       LIST_FOREACH(tmp, &all_personas, pna_list) {
+               int m = 0;
+               persona_lock(tmp);
+               if (login && strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0)
+                       m++;
+               if (uid != PERSONA_ID_NONE && uid == tmp->pna_id)
+                       m++;
+               if (m == match) {
+                       if (persona && *plen > found)
+                               persona[found] = persona_get_locked(tmp);
+                       found++;
+               }
+#ifdef PERSONA_DEBUG
+               if (m > 0)
+                       persona_dbg("ID:%d Matched %d/%d, found:%d, *plen:%d",
+                                   tmp->pna_id, m, match, (int)found, (int)*plen);
+#endif
+               persona_unlock(tmp);
+       }
+       unlock_personas();
+
+       *plen = found;
+       if (!found)
+               return ESRCH;
+       return 0;
+}
+
+struct persona *persona_proc_get(pid_t pid)
+{
+       struct persona *persona;
+       proc_t p = proc_find(pid);
+
+       if (!p)
+               return NULL;
+
+       proc_lock(p);
+       persona = persona_get(p->p_persona);
+       proc_unlock(p);
+
+       proc_rele(p);
+
+       return persona;
+}
+
+struct persona *current_persona_get(void)
+{
+       proc_t p = current_proc();
+       struct persona *persona;
+
+       proc_lock(p);
+       persona = persona_get(p->p_persona);
+       proc_unlock(p);
+
+       return persona;
+}
+
+/**
+ * inherit a persona from parent to child
+ */
+int persona_proc_inherit(proc_t child, proc_t parent)
+{
+       if (child->p_persona != NULL) {
+               persona_dbg("proc_inherit: child already in persona: %s",
+                           persona_desc(child->p_persona, 0));
+               return -1;
+       }
+
+       /* no persona to inherit */
+       if (parent->p_persona == NULL)
+               return 0;
+
+       return persona_proc_adopt(child, parent->p_persona, parent->p_ucred);
+}
+
+int persona_proc_adopt_id(proc_t p, uid_t id, kauth_cred_t auth_override)
+{
+       int ret;
+       struct persona *persona;
+
+       persona = persona_lookup(id);
+       if (!persona)
+               return ESRCH;
+
+       ret = persona_proc_adopt(p, persona, auth_override);
+
+       /* put the reference from the lookup() */
+       persona_put(persona);
+
+       return ret;
+}
+
+
+typedef enum e_persona_reset_op {
+       PROC_REMOVE_PERSONA = 1,
+       PROC_RESET_OLD_PERSONA = 2,
+} persona_reset_op_t;
+
+/*
+ * internal cleanup routine for proc_set_cred_internal
+ *
+ */
+static struct persona *proc_reset_persona_internal(proc_t p, persona_reset_op_t op,
+                                                  struct persona *old_persona,
+                                                  struct persona *new_persona)
+{
+#if (DEVELOPMENT || DEBUG)
+       persona_lock_assert_held(new_persona);
+#endif
+
+       switch (op) {
+       case PROC_REMOVE_PERSONA:
+               old_persona = p->p_persona;
+               /* fall through */
+       case PROC_RESET_OLD_PERSONA:
+               break;
+       default:
+               /* invalid arguments */
+               return NULL;
+       }
+
+       /* unlock the new persona (locked on entry) */
+       persona_unlock(new_persona);
+       /* lock the old persona and the process */
+       persona_lock(old_persona);
+       proc_lock(p);
+
+       switch (op) {
+       case PROC_REMOVE_PERSONA:
+               LIST_REMOVE(p, p_persona_list);
+               p->p_persona = NULL;
+               break;
+       case PROC_RESET_OLD_PERSONA:
+               p->p_persona = old_persona;
+               LIST_INSERT_HEAD(&old_persona->pna_members, p, p_persona_list);
+               break;
+       }
+
+       proc_unlock(p);
+       persona_unlock(old_persona);
+
+       /* re-lock the new persona */
+       persona_lock(new_persona);
+       return old_persona;
+}
+
+/*
+ * Assumes persona is locked.
+ * On success, takes a reference to 'persona' and returns the
+ * previous persona the process had adopted. The caller is
+ * responsible to release the reference.
+ */
+static struct persona *proc_set_cred_internal(proc_t p, struct persona *persona,
+                                             kauth_cred_t auth_override, int *rlim_error)
+{
+       struct persona *old_persona = NULL;
+       kauth_cred_t my_cred, my_new_cred;
+       uid_t old_uid, new_uid;
+       int count;
+
+       /*
+        * This operation must be done under the proc trans lock
+        * by the thread which took the trans lock!
+        */
+       assert(((p->p_lflag & P_LINTRANSIT) == P_LINTRANSIT) &&
+              p->p_transholder == current_thread());
+       assert(persona != NULL);
+
+       /* no work to do if we "re-adopt" the same persona */
+       if (p->p_persona == persona)
+               return NULL;
+
+       /*
+        * If p is in a persona, then we need to remove 'p' from the list of
+        * processes in that persona. To do this, we need to drop the lock
+        * held on the incoming (new) persona and lock the old one.
+        */
+       if (p->p_persona) {
+               old_persona = proc_reset_persona_internal(p, PROC_REMOVE_PERSONA,
+                                                         NULL, persona);
+       }
+
+       if (auth_override)
+               my_new_cred = auth_override;
+       else
+               my_new_cred = persona->pna_cred;
+
+       if (!my_new_cred)
+               panic("NULL credentials (persona:%p)", persona);
+
+       *rlim_error = 0;
+
+       kauth_cred_ref(my_new_cred);
+
+       new_uid = persona->pna_id;
+
+       /*
+        * Check to see if we will hit a proc rlimit by moving the process
+        * into the persona. If so, we'll bail early before actually moving
+        * the process or changing its credentials.
+        */
+       if (new_uid != 0 &&
+           (rlim_t)chgproccnt(new_uid, 0) > p->p_rlimit[RLIMIT_NPROC].rlim_cur) {
+               pna_err("PID:%d hit proc rlimit in new persona(%d): %s",
+                       p->p_pid, new_uid, persona_desc(persona, 1));
+               *rlim_error = EACCES;
+               (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA,
+                                                 old_persona, persona);
+               kauth_cred_unref(&my_new_cred);
+               return NULL;
+       }
+
+       /*
+        * Set the new credentials on the proc
+        */
+set_proc_cred:
+       my_cred = kauth_cred_proc_ref(p);
+       persona_dbg("proc_adopt PID:%d, %s -> %s",
+                   p->p_pid,
+                   persona_desc(old_persona, 1),
+                   persona_desc(persona, 1));
+
+       old_uid = kauth_cred_getruid(my_cred);
+
+       if (my_cred != my_new_cred) {
+               kauth_cred_t old_cred = my_cred;
+
+               proc_ucred_lock(p);
+               /*
+                * We need to protect against a race where another thread
+                * also changed the credential after we took our
+                * reference.  If p_ucred has changed then we should
+                * restart this again with the new cred.
+                */
+               if (p->p_ucred != my_cred) {
+                       proc_ucred_unlock(p);
+                       kauth_cred_unref(&my_cred);
+                       /* try again */
+                       goto set_proc_cred;
+               }
+
+               /* update the credential and take a ref for the proc */
+               kauth_cred_ref(my_new_cred);
+               p->p_ucred = my_new_cred;
+
+               /* update cred on proc (and current thread) */
+               mach_kauth_cred_uthread_update();
+               PROC_UPDATE_CREDS_ONPROC(p);
+
+               /* drop the proc's old ref on the credential */
+               kauth_cred_unref(&old_cred);
+               proc_ucred_unlock(p);
+       }
+
+       /* drop this function's reference to the old cred */
+       kauth_cred_unref(&my_cred);
+
+       /*
+        * Update the proc count.
+        * If the UIDs are the same, then there is no work to do.
+        */
+       if (old_persona)
+               old_uid = old_persona->pna_id;
+
+       if (new_uid != old_uid) {
+               count = chgproccnt(old_uid, -1);
+               persona_dbg("Decrement %s:%d proc_count to: %d",
+                           old_persona ? "Persona" : "UID", old_uid, count);
+
+               /*
+                * Increment the proc count on the UID associated with
+                * the new persona. Enforce the resource limit just
+                * as in fork1()
+                */
+               count = chgproccnt(new_uid, 1);
+               persona_dbg("Increment Persona:%d (UID:%d) proc_count to: %d",
+                           new_uid, kauth_cred_getuid(my_new_cred), count);
+       }
+
+       OSBitOrAtomic(P_ADOPTPERSONA, &p->p_flag);
+
+       proc_lock(p);
+       p->p_persona = persona_get_locked(persona);
+       LIST_INSERT_HEAD(&persona->pna_members, p, p_persona_list);
+       proc_unlock(p);
+
+       kauth_cred_unref(&my_new_cred);
+
+       return old_persona;
+}
+
+int persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override)
+{
+       int error;
+       struct persona *old_persona;
+       struct session * sessp;
+
+       if (!persona)
+               return EINVAL;
+
+       persona_dbg("%d adopting Persona %d (%s)", proc_pid(p),
+                   persona->pna_id, persona_desc(persona, 0));
+
+       persona_lock(persona);
+       if (!persona->pna_cred || !persona_valid(persona)) {
+               persona_dbg("Invalid persona (%s): NULL credentials!", persona_desc(persona, 1));
+               persona_unlock(persona);
+               return EINVAL;
+       }
+
+       /* the persona credentials can no longer be adjusted */
+       persona->pna_cred_locked = 1;
+
+       /*
+        * assume the persona: this may drop and re-acquire the persona lock!
+        */
+       error = 0;
+       old_persona = proc_set_cred_internal(p, persona, auth_override, &error);
+
+       /* join the process group associated with the persona */
+       if (persona->pna_pgid) {
+               uid_t uid = kauth_cred_getuid(persona->pna_cred);
+               persona_dbg(" PID:%d, pgid:%d%s",
+                           p->p_pid, persona->pna_pgid,
+                           persona->pna_pgid == uid ? ", new_session" : ".");
+               enterpgrp(p, persona->pna_pgid, persona->pna_pgid == uid);
+       }
+
+       /* set the login name of the session */
+       sessp = proc_session(p);
+       if (sessp != SESSION_NULL) {
+               session_lock(sessp);
+               bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME);
+               session_unlock(sessp);
+               session_rele(sessp);
+       }
+
+       persona_unlock(persona);
+
+       set_security_token(p);
+
+       /*
+        * Drop the reference to the old persona.
+        */
+       if (old_persona)
+               persona_put(old_persona);
+
+       persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
+       return error;
+}
+
+int persona_proc_drop(proc_t p)
+{
+       struct persona *persona = NULL;
+
+       persona_dbg("PID:%d, %s -> <none>", p->p_pid, persona_desc(p->p_persona, 0));
+
+       /*
+        * There are really no other credentials for us to assume,
+        * so we'll just continue running with the credentials
+        * we got from the persona.
+        */
+
+       /*
+        * the locks must be taken in reverse order here, so
+        * we have to be careful not to cause deadlock
+        */
+try_again:
+       proc_lock(p);
+       if (p->p_persona) {
+               uid_t puid, ruid;
+               if (!persona_try_lock(p->p_persona)) {
+                       proc_unlock(p);
+                       mutex_pause(0); /* back-off time */
+                       goto try_again;
+               }
+               persona = p->p_persona;
+               LIST_REMOVE(p, p_persona_list);
+               p->p_persona = NULL;
+
+               ruid = kauth_cred_getruid(p->p_ucred);
+               puid = kauth_cred_getuid(persona->pna_cred);
+               proc_unlock(p);
+               (void)chgproccnt(ruid, 1);
+               (void)chgproccnt(puid, -1);
+       } else {
+               proc_unlock(p);
+       }
+
+       /*
+        * if the proc had a persona, then it is still locked here
+        * (preserving proper lock ordering)
+        */
+
+       if (persona) {
+               persona_unlock(persona);
+               persona_put(persona);
+       }
+
+       return 0;
+}
+
+int persona_get_type(struct persona *persona)
+{
+       int type;
+
+       if (!persona)
+               return PERSONA_INVALID;
+
+       persona_lock(persona);
+       if (!persona_valid(persona)) {
+               persona_unlock(persona);
+               return PERSONA_INVALID;
+       }
+       type = persona->pna_type;
+       persona_unlock(persona);
+
+       return type;
+}
+
+int persona_set_cred(struct persona *persona, kauth_cred_t cred)
+{
+       int ret = 0;
+       kauth_cred_t my_cred;
+       if (!persona || !cred)
+               return EINVAL;
+
+       persona_lock(persona);
+       if (!persona_valid(persona)) {
+               ret = EINVAL;
+               goto out_unlock;
+       }
+       if (persona->pna_cred_locked) {
+               ret = EPERM;
+               goto out_unlock;
+       }
+
+       /* create a new cred from the passed-in cred */
+       my_cred = kauth_cred_create(cred);
+
+       /* ensure that the UID matches the persona ID */
+       my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
+                                      persona->pna_id, persona->pna_id,
+                                      KAUTH_UID_NONE);
+
+       /* TODO: clear the saved GID?! */
+
+       /* replace the persona's cred with the new one */
+       if (persona->pna_cred)
+               kauth_cred_unref(&persona->pna_cred);
+       persona->pna_cred = my_cred;
+
+out_unlock:
+       persona_unlock(persona);
+       return ret;
+}
+
+int persona_set_cred_from_proc(struct persona *persona, proc_t proc)
+{
+       int ret = 0;
+       kauth_cred_t parent_cred, my_cred;
+       if (!persona || !proc)
+               return EINVAL;
+
+       persona_lock(persona);
+       if (!persona_valid(persona)) {
+               ret = EINVAL;
+               goto out_unlock;
+       }
+       if (persona->pna_cred_locked) {
+               ret = EPERM;
+               goto out_unlock;
+       }
+
+       parent_cred = kauth_cred_proc_ref(proc);
+
+       /* TODO: clear the saved UID/GID! */
+
+       /* create a new cred from the proc's cred */
+       my_cred = kauth_cred_create(parent_cred);
+
+       /* ensure that the UID matches the persona ID */
+       my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
+                                      persona->pna_id, persona->pna_id,
+                                      KAUTH_UID_NONE);
+
+       /* replace the persona's cred with the new one */
+       if (persona->pna_cred)
+               kauth_cred_unref(&persona->pna_cred);
+       persona->pna_cred = my_cred;
+
+       kauth_cred_unref(&parent_cred);
+
+out_unlock:
+       persona_unlock(persona);
+       return ret;
+}
+
+kauth_cred_t persona_get_cred(struct persona *persona)
+{
+       kauth_cred_t cred = NULL;
+
+       if (!persona)
+               return NULL;
+
+       persona_lock(persona);
+       if (!persona_valid(persona))
+               goto out_unlock;
+
+       if (persona->pna_cred) {
+               kauth_cred_ref(persona->pna_cred);
+               cred = persona->pna_cred;
+       }
+
+out_unlock:
+       persona_unlock(persona);
+
+       return cred;
+}
+
+uid_t persona_get_uid(struct persona *persona)
+{
+       uid_t uid = UID_MAX;
+
+       if (!persona || !persona->pna_cred)
+               return UID_MAX;
+
+       persona_lock(persona);
+       if (persona_valid(persona)) {
+               uid = kauth_cred_getuid(persona->pna_cred);
+               assert(uid == persona->pna_id);
+       }
+       persona_unlock(persona);
+
+       return uid;
+}
+
+int persona_set_gid(struct persona *persona, gid_t gid)
+{
+       int ret = 0;
+       kauth_cred_t my_cred, new_cred;
+
+       if (!persona || !persona->pna_cred)
+               return EINVAL;
+
+       persona_lock(persona);
+       if (!persona_valid(persona)) {
+               ret = EINVAL;
+               goto out_unlock;
+       }
+       if (persona->pna_cred_locked) {
+               ret = EPERM;
+               goto out_unlock;
+       }
+
+       my_cred = persona->pna_cred;
+       kauth_cred_ref(my_cred);
+       new_cred = kauth_cred_setresgid(my_cred, gid, gid, gid);
+       if (new_cred != my_cred)
+               persona->pna_cred = new_cred;
+       kauth_cred_unref(&my_cred);
+
+out_unlock:
+       persona_unlock(persona);
+       return ret;
+}
+
+gid_t persona_get_gid(struct persona *persona)
+{
+       gid_t gid = GID_MAX;
+
+       if (!persona || !persona->pna_cred)
+               return GID_MAX;
+
+       persona_lock(persona);
+       if (persona_valid(persona))
+               gid = kauth_cred_getgid(persona->pna_cred);
+       persona_unlock(persona);
+
+       return gid;
+}
+
+int persona_set_groups(struct persona *persona, gid_t *groups, int ngroups, uid_t gmuid)
+{
+       int ret = 0;
+       kauth_cred_t my_cred, new_cred;
+
+       if (!persona || !persona->pna_cred)
+               return EINVAL;
+       if (ngroups > NGROUPS_MAX)
+               return EINVAL;
+
+       persona_lock(persona);
+       if (!persona_valid(persona)) {
+               ret = EINVAL;
+               goto out_unlock;
+       }
+       if (persona->pna_cred_locked) {
+               ret = EPERM;
+               goto out_unlock;
+       }
+
+       my_cred = persona->pna_cred;
+       kauth_cred_ref(my_cred);
+       new_cred = kauth_cred_setgroups(my_cred, groups, ngroups, gmuid);
+       if (new_cred != my_cred)
+               persona->pna_cred = new_cred;
+       kauth_cred_unref(&my_cred);
+
+out_unlock:
+       persona_unlock(persona);
+       return ret;
+}
+
+int persona_get_groups(struct persona *persona, int *ngroups, gid_t *groups, int groups_sz)
+{
+       int ret = EINVAL;
+       if (!persona || !persona->pna_cred || !groups || !ngroups)
+               return EINVAL;
+
+       *ngroups = groups_sz;
+
+       persona_lock(persona);
+       if (persona_valid(persona)) {
+               kauth_cred_getgroups(persona->pna_cred, groups, ngroups);
+               ret = 0;
+       }
+       persona_unlock(persona);
+
+       return ret;
+}
+
+uid_t persona_get_gmuid(struct persona *persona)
+{
+       uid_t gmuid = KAUTH_UID_NONE;
+
+       if (!persona || !persona->pna_cred)
+               return gmuid;
+
+       persona_lock(persona);
+       if (!persona_valid(persona))
+               goto out_unlock;
+
+       posix_cred_t pcred = posix_cred_get(persona->pna_cred);
+       gmuid = pcred->cr_gmuid;
+
+out_unlock:
+       persona_unlock(persona);
+       return gmuid;
+}
+
+int persona_get_login(struct persona *persona, char login[MAXLOGNAME+1])
+{
+       int ret = EINVAL;
+       if (!persona || !persona->pna_cred)
+               return EINVAL;
+
+       persona_lock(persona);
+       if (!persona_valid(persona))
+               goto out_unlock;
+
+       strlcpy(login, persona->pna_login, MAXLOGNAME);
+       ret = 0;
+
+out_unlock:
+       persona_unlock(persona);
+       login[MAXLOGNAME] = 0;
+
+       return ret;
+}
+
+#else /* !CONFIG_PERSONAS */
+
+/*
+ * symbol exports for kext compatibility
+ */
+
+uid_t persona_get_id(__unused struct persona *persona)
+{
+       return PERSONA_ID_NONE;
+}
+
+int persona_get_type(__unused struct persona *persona)
+{
+       return PERSONA_INVALID;
+}
+
+kauth_cred_t persona_get_cred(__unused struct persona *persona)
+{
+       return NULL;
+}
+
+struct persona *persona_lookup(__unused uid_t id)
+{
+       return NULL;
+}
+
+int persona_find(__unused const char *login,
+                __unused uid_t uid,
+                __unused struct persona **persona,
+                __unused size_t *plen)
+{
+       return ENOTSUP;
+}
+
+struct persona *current_persona_get(void)
+{
+       return NULL;
+}
+
+struct persona *persona_get(struct persona *persona)
+{
+       return persona;
+}
+
+void persona_put(__unused struct persona *persona)
+{
+       return;
+}
+#endif
index 9213b82f3ff312fc7f294d0ef7fbc35656ef5ae8..bd82153262eff9d82c855078cf0ab0b25edde96b 100644 (file)
 #include <sys/priv.h>
 #include <sys/proc_info.h>
 #include <sys/bsdtask_info.h>
+#include <sys/persona.h>
 
 #if CONFIG_MEMORYSTATUS
 #include <sys/kern_memorystatus.h>
@@ -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;
index e90c68c55b80b7109f686800d1ca750f684c7ced..75980efdca6ae81e0809504b66e8472e746ba91a 100644 (file)
@@ -95,8 +95,7 @@
 #include <sys/timeb.h>
 #include <sys/times.h>
 #include <sys/malloc.h>
-
-#define chgproccnt_ok(p) 1
+#include <sys/persona.h>
 
 #include <security/audit/audit.h>
 
@@ -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
index 3858ce83e10ed21b146a0a5f050d8a50e7ad697b..59a44295f75d3b47979964368417d95e392684c3 100644 (file)
@@ -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++) {
index dde94f5a20aeea88538e48a20ee874e751472129..0fe1cfa3070eff6ed338259b5f822772cdd84dd3 100644 (file)
@@ -85,6 +85,9 @@
 #include <security/mac_framework.h>
 #endif
 
+int pshm_cache_purge_all(proc_t p);
+int psem_cache_purge_all(proc_t p);
+
 int
 reboot(struct proc *p, register struct reboot_args *uap, __unused int32_t *retval)
 {
@@ -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;
+}
index 09818e3b0ec0c57badc311e661da5992b4159164..82a6475869a2f90d2b27007527c0fa792b8deec9 100644 (file)
@@ -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 {
index bdebcc1825d875e628e53c11d7f8d3f90144d8a4..1f35405f4e9ac0de0b10ea193a6a5e86d255c1d5 100644 (file)
@@ -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) {
index b5666e88155c142c2501c6bfda80e9c5889b7b34..0d7ba242d27ff95ea07bba2625de1fbcbf9684e6 100644 (file)
@@ -82,6 +82,8 @@
 #include <vm/vm_protos.h> 
 #include <IOKit/IOReturn.h>    /* for kIOReturnNotPrivileged */
 
+#include <os/overflow.h>
+
 /*
  * XXX vm/pmap.h should not treat these prototypes as MACH_KERNEL_PRIVATE
  * when KERNEL is defined.
@@ -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;
index 5600cb42fa11955f2d0d59c0e21bfd14a7837594..bcef8baa37a948eab9dc4d9bd797b52fe0c308b5 100644 (file)
@@ -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
index 42a12cb7e794713d6116d30781b0b66c24dc5872..1b166106cee047b67079a64013c7d16d7740d786 100644 (file)
@@ -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
index e14baf815e932decbe39ea8506c53805c35e514a..38faf2939a0fcd7f369a7ac676b749c853da293a 100644 (file)
@@ -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 (file)
index 0000000..3272922
--- /dev/null
@@ -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 <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/kernel_types.h>
+#include <sys/sysproto.h>
+
+#include <sys/kauth.h>
+#include <sys/malloc.h>
+#include <sys/persona.h>
+#include <sys/proc.h>
+
+#include <libkern/libkern.h>
+
+static int kpersona_copyin(user_addr_t infop, struct kpersona_info *kinfo)
+{
+       uint32_t info_v = 0;
+       int error;
+
+       error = copyin(infop, &info_v, sizeof(info_v));
+       if (error)
+               return error;
+
+       /* only support a single version of the struct for now */
+       if (info_v != PERSONA_INFO_V1)
+               return EINVAL;
+
+       error = copyin(infop, kinfo, sizeof(*kinfo));
+
+       /* enforce NULL termination on strings */
+       kinfo->persona_name[MAXLOGNAME] = 0;
+
+       return error;
+}
+
+static int kpersona_copyout(struct kpersona_info *kinfo, user_addr_t infop)
+{
+       uint32_t info_v;
+       int error;
+
+       error = copyin(infop, &info_v, sizeof(info_v));
+       if (error)
+               return error;
+
+       /* only support a single version of the struct for now */
+       /* TODO: in the future compare info_v to kinfo->persona_info_version */
+       if (info_v != PERSONA_INFO_V1)
+               return EINVAL;
+
+       error = copyout(kinfo, infop, sizeof(*kinfo));
+       return error;
+}
+
+
+static int kpersona_alloc_syscall(user_addr_t infop, user_addr_t idp)
+{
+       int error;
+       struct kpersona_info kinfo;
+       struct persona *persona;
+       uid_t id = PERSONA_ID_NONE;
+       const char *login;
+
+       /*
+        * TODO: rdar://problem/19981151
+        * Add entitlement check!
+        */
+       if (!kauth_cred_issuser(kauth_cred_get()))
+               return EPERM;
+
+       error = kpersona_copyin(infop, &kinfo);
+       if (error)
+               return error;
+
+       login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
+       if (kinfo.persona_id != PERSONA_ID_NONE && kinfo.persona_id != (uid_t)0)
+               id = kinfo.persona_id;
+
+       error = 0;
+       persona = persona_alloc(id, login, kinfo.persona_type, &error);
+       if (!persona)
+               return error;
+
+       if (kinfo.persona_gid) {
+               error = persona_set_gid(persona, kinfo.persona_gid);
+               if (error)
+                       goto out_error;
+       }
+
+       if (kinfo.persona_ngroups > 0) {
+               /* force gmuid 0 to *opt-out* of memberd */
+               if (kinfo.persona_gmuid == 0)
+                       kinfo.persona_gmuid = KAUTH_UID_NONE;
+
+               error = persona_set_groups(persona, kinfo.persona_groups,
+                                          kinfo.persona_ngroups,
+                                          kinfo.persona_gmuid);
+               if (error)
+                       goto out_error;
+       }
+
+       error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
+       if (error)
+               goto out_error;
+       error = kpersona_copyout(&kinfo, infop);
+
+       /*
+        * On success, we have a persona structure in the global list with a
+        * single reference count on it. The corresponding _dealloc() call
+        * will release this reference.
+        */
+       return error;
+
+out_error:
+       printf("%s:  ERROR:%d\n", __func__, error);
+       if (persona)
+               persona_put(persona);
+       return error;
+}
+
+static int kpersona_dealloc_syscall(user_addr_t idp)
+{
+       int error;
+       uid_t persona_id;
+       struct persona *persona;
+
+       if (!kauth_cred_issuser(kauth_cred_get()))
+               return EPERM;
+
+       error = copyin(idp, &persona_id, sizeof(persona_id));
+       if (error)
+               return error;
+
+       persona = persona_lookup(persona_id);
+       if (!persona)
+               return ESRCH;
+
+       /* invalidate the persona (deny subsequent spawn/fork) */
+       error = persona_invalidate(persona);
+
+       /* one reference from the _lookup() */
+       persona_put(persona);
+
+       /* one reference from the _alloc() */
+       if (!error)
+               persona_put(persona);
+
+       return error;
+}
+
+static int kpersona_get_syscall(user_addr_t idp)
+{
+       int error;
+       struct persona *persona = current_persona_get();
+
+       if (!persona)
+               return ESRCH;
+
+       error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
+       persona_put(persona);
+
+       return error;
+}
+
+static int kpersona_info_syscall(user_addr_t idp, user_addr_t infop)
+{
+       int error;
+       uid_t persona_id;
+       struct persona *persona;
+       struct kpersona_info kinfo;
+
+       error = copyin(idp, &persona_id, sizeof(persona_id));
+       if (error)
+               return error;
+
+       /*
+        * TODO: rdar://problem/19981151
+        * Add entitlement check!
+        */
+
+       persona = persona_lookup(persona_id);
+       if (!persona)
+               return ESRCH;
+
+       persona_dbg("FOUND: persona:%p, id:%d, gid:%d, login:\"%s\"",
+                   persona, persona->pna_id, persona_get_gid(persona),
+                   persona->pna_login);
+
+       memset(&kinfo, 0, sizeof(kinfo));
+       kinfo.persona_info_version = PERSONA_INFO_V1;
+       kinfo.persona_id = persona->pna_id;
+       kinfo.persona_type = persona->pna_type;
+       kinfo.persona_gid = persona_get_gid(persona);
+       int ngroups = 0;
+       persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
+       kinfo.persona_ngroups = ngroups;
+       kinfo.persona_gmuid = persona_get_gmuid(persona);
+
+       /*
+        * NULL termination is assured b/c persona_name is
+        * exactly MAXLOGNAME + 1 bytes (and has been memset to 0)
+        */
+       strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
+
+       persona_put(persona);
+
+       error = kpersona_copyout(&kinfo, infop);
+
+       return error;
+}
+
+static int kpersona_pidinfo_syscall(user_addr_t idp, user_addr_t infop)
+{
+       int error;
+       pid_t pid;
+       struct persona *persona;
+       struct kpersona_info kinfo;
+
+       error = copyin(idp, &pid, sizeof(pid));
+       if (error)
+               return error;
+
+       if (!kauth_cred_issuser(kauth_cred_get())
+           && (pid != current_proc()->p_pid))
+               return EPERM;
+
+       persona = persona_proc_get(pid);
+       if (!persona)
+               return ESRCH;
+
+       memset(&kinfo, 0, sizeof(kinfo));
+       kinfo.persona_info_version = PERSONA_INFO_V1;
+       kinfo.persona_id = persona->pna_id;
+       kinfo.persona_type = persona->pna_type;
+       kinfo.persona_gid = persona_get_gid(persona);
+       int ngroups = 0;
+       persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
+       kinfo.persona_ngroups = ngroups;
+       kinfo.persona_gmuid = persona_get_gmuid(persona);
+
+       strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
+
+       persona_put(persona);
+
+       error = kpersona_copyout(&kinfo, infop);
+
+       return error;
+}
+
+static int kpersona_find_syscall(user_addr_t infop, user_addr_t idp, user_addr_t idlenp)
+{
+       int error;
+       struct kpersona_info kinfo;
+       const char *login;
+       size_t u_idlen, k_idlen = 0;
+       struct persona **persona = NULL;
+
+       error = copyin(idlenp, &u_idlen, sizeof(u_idlen));
+       if (error)
+               return error;
+
+       if (u_idlen > g_max_personas)
+               u_idlen = g_max_personas;
+
+       error = kpersona_copyin(infop, &kinfo);
+       if (error)
+               goto out;
+
+       login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
+
+       if (u_idlen > 0) {
+               MALLOC(persona, struct persona **, sizeof(*persona) * u_idlen,
+                      M_TEMP, M_WAITOK|M_ZERO);
+               if (!persona) {
+                       error = ENOMEM;
+                       goto out;
+               }
+       }
+
+       k_idlen = u_idlen;
+       error = persona_find(login, kinfo.persona_id, persona, &k_idlen);
+       if (error)
+               goto out;
+
+       /* copyout all the IDs of each persona we found */
+       for (size_t i = 0; i < k_idlen; i++) {
+               if (i >= u_idlen)
+                       break;
+               error = copyout(&persona[i]->pna_id,
+                               idp + (i * sizeof(persona[i]->pna_id)),
+                               sizeof(persona[i]->pna_id));
+               if (error)
+                       goto out;
+       }
+
+out:
+       if (persona) {
+               for (size_t i = 0; i < u_idlen; i++)
+                       persona_put(persona[i]);
+               FREE(persona, M_TEMP);
+       }
+
+       (void)copyout(&k_idlen, idlenp, sizeof(u_idlen));
+
+       return error;
+}
+
+
+/*
+ * Syscall entry point / demux.
+ */
+int persona(__unused proc_t p, struct persona_args *pargs, __unused int32_t *retval)
+{
+       int error;
+       uint32_t op = pargs->operation;
+       /* uint32_t flags = pargs->flags; */
+       user_addr_t infop = pargs->info;
+       user_addr_t idp = pargs->id;
+
+       switch (op) {
+       case PERSONA_OP_ALLOC:
+               error = kpersona_alloc_syscall(infop, idp);
+               break;
+       case PERSONA_OP_DEALLOC:
+               error = kpersona_dealloc_syscall(idp);
+               break;
+       case PERSONA_OP_GET:
+               error = kpersona_get_syscall(idp);
+               break;
+       case PERSONA_OP_INFO:
+               error = kpersona_info_syscall(idp, infop);
+               break;
+       case PERSONA_OP_PIDINFO:
+               error = kpersona_pidinfo_syscall(idp, infop);
+               break;
+       case PERSONA_OP_FIND:
+               error = kpersona_find_syscall(infop, idp, pargs->idlen);
+               break;
+       default:
+               error = ENOSYS;
+               break;
+       }
+
+       return error;
+}
index 066065c6a85946ab9d9358ee3eac67b29bb97e1d..faf5af9d4974b811afad529483e3481745dcf70c 100644 (file)
 442    AUE_CLOSE       ALL     { int guarded_close_np(int fd, const guardid_t *guard); }
 443    AUE_KQUEUE      ALL     { int guarded_kqueue_np(const guardid_t *guard, u_int guardflags); }
 444    AUE_NULL        ALL     { int change_fdguard_np(int fd, const guardid_t *guard, u_int guardflags, const guardid_t *nguard, u_int nguardflags, int *fdflagsp); }
-445    AUE_NULL        ALL     { int nosys(void); } { old __proc_suppress } 
+445    AUE_USRCTL      ALL     { int usrctl(uint32_t flags); }
 446    AUE_NULL        ALL     { int proc_rlimit_control(pid_t pid, int flavor, void *arg); }
 #if SOCKETS
 447    AUE_CONNECT     ALL     { int connectx(int socket, const sa_endpoints_t *endpoints, sae_associd_t associd, unsigned int flags, const struct iovec *iov, unsigned int iovcnt, size_t *len, sae_connid_t *connid); }
 492    AUE_NULL        ALL { int enosys(void); }
 #endif /* CONFIG_TELEMETRY */
 493    AUE_NULL        ALL     { user_ssize_t grab_pgo_data (user_addr_t uuid, int flags, user_addr_t buffer, user_ssize_t size); }
+#if CONFIG_PERSONAS
+494    AUE_PERSONA     ALL     { int persona(uint32_t operation, uint32_t flags, struct kpersona_info *info, uid_t *id, size_t *idlen) NO_SYSCALL_STUB; }
+#else
 494    AUE_NULL        ALL     { int enosys(void); }
+#endif
 495    AUE_NULL        ALL     { int enosys(void); }
 496    AUE_NULL        ALL     { int enosys(void); }
 497    AUE_NULL        ALL     { int enosys(void); }
index daca446302bab8699c6bcf6d2ca4462df7b13a29..cc35787f83238fab7e3c0dc2e269fde782243228 100644 (file)
@@ -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;
index b37035dfe694da54e4df5c7d9fd87a98358f2584..c03b66bc5fd7ad97770bf2e41c5fce8a918b0b79 100644 (file)
 0x14000A8      MACH_THREAD_BIND
 0x14000AC      MACH_WAITQ_PROMOTE
 0x14000B0      MACH_WAITQ_DEMOTE
+0x14000B4      MACH_SCHED_LOAD
+0x14000B8      MACH_REC_CORES_FAILSAFE
+0x14000BC      MACH_SCHED_QUANTUM_EXPIRED
 0x1500000      MACH_MSGID_INVALID
 0x1600000      MTX_SLEEP
 0x1600004      MTX_SLEEP_DEADLINE
 0x40c0784      BSC_sendmsg_x
 0x40c0788      BSC_thread_selfusage
 0x40c07a4      BSC_mremap_encrypted
-0x40c07b8      BSC_reserved
+0x40c07b8      BSC_persona
 0x40c07cc      BSC_work_interval_ctl
 0x40e0104      BSC_msync_extended_info
 0x40e0264      BSC_pread_extended_info
 0x2700E010     PERF_SRAMEMA_DOM1
 0x2700E020     PERF_SRAMEMA_DOM2
 0x2700E030     PERF_SRAMEMA_DOM3
+0x28100004     BANK_SETTLE_CPU_TIME
+0x28100008     BANK_SECURE_ORIGINATOR_CHANGED
 0x2a100004     ATM_MIN_CALLED
 0x2a100008     ATM_LINK_LIST_TRIM
 0x2a200004     ATM_VALUE_REPLACED
index 7d7fbe7c61102632a26689f3be6856eba0c0cedf..3e4353a97071cc952f11470e917fcca342aa805e 100644 (file)
@@ -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: "
index ede272ba6763e4004bbf75e2f6db410e22aa4c1f..29568b48ae1edbbf2a30b3314ef848039323cddd 100644 (file)
@@ -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 {
index f8b94b90469b28646301a04dfc9591179bc54b01..40e7f19191e608441d57a19f4a1aef0a0016fea6 100644 (file)
@@ -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);
index 03330fbcbca27bdb133de8c64a154adbd9124748..beee6c37f61d02a07d1dce226f547555cfe2c8c8 100644 (file)
@@ -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);
 
index cfe63ef281a3b34ea43ff97511598d2d42a8ae22..f94c740fd82362dee080b2f005772b1668e2cfbe 100644 (file)
@@ -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,
index 1d54c5b951061cfd4729b71ae8b926de34795cee..d052d38c743cd573e7f6eafb3016b36e4b8e52ab 100644 (file)
@@ -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 (file)
index 0000000..1076c57
--- /dev/null
@@ -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 (file)
index 0000000..b6ba019
--- /dev/null
@@ -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 <sys/appleapiopts.h>
+
+
+__BEGIN_DECLS
+
+
+#ifdef BSD_KERNEL_PRIVATE
+
+struct routefs_args {
+    char    route_path[MAXPATHLEN]; /* path name of the target route */
+    vnode_t route_rvp;  /* vnode of the target of route */
+    
+};
+
+struct routefs_mount {
+    char    route_path[MAXPATHLEN]; /* path name of the target route */
+    mount_t route_mount;
+    vnode_t route_rvp;  /* vnode of the target of route */
+    int route_vpvid;  /* vnode of the target of route */
+};
+
+
+/*
+ * Function: routefs_kernel_mount
+ *
+ * Purpose:
+ *   mount routefs
+ *   any links created with devfs_make_link().
+ */
+int     routefs_kernel_mount(char * routepath);
+
+#endif /* BSD_KERNEL_PRIVATE */
+
+__END_DECLS
+
+
+#endif /* !_MISCFS_ROUTEFS_DEVFS_H_ */
diff --git a/bsd/miscfs/routefs/routefs_ops.c b/bsd/miscfs/routefs/routefs_ops.c
new file mode 100644 (file)
index 0000000..1943254
--- /dev/null
@@ -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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/vnode_internal.h>
+#include <sys/proc.h>
+#include <sys/kauth.h>
+#include <sys/mount_internal.h>
+#include <sys/fcntl.h>
+#include <sys/unistd.h>
+#include <sys/malloc.h>
+#include <vfs/vfs_support.h>
+
+#include <libkern/OSAtomic.h>
+
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#endif
+
+#include "routefs.h"
+
+static int routefs_init(__unused struct vfsconf *vfsp);
+static int routefs_mount(struct mount *mp, __unused vnode_t devvp, __unused user_addr_t data, vfs_context_t ctx);
+static int routefs_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t ctx);
+static int routefs_unmount( struct mount *mp, int mntflags, __unused vfs_context_t ctx);
+static int routefs_root(struct mount *mp, struct vnode **vpp, __unused vfs_context_t ctx);
+static int routefs_statfs( struct mount *mp, struct vfsstatfs *sbp, __unused vfs_context_t ctx);
+static int routefs_vfs_getattr(__unused mount_t mp, struct vfs_attr *fsap, __unused vfs_context_t ctx);
+static int routefs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx);
+static int routefs_vget(__unused struct mount *mp, __unused ino64_t ino, __unused struct vnode **vpp, __unused vfs_context_t ctx);
+static int routefs_fhtovp (__unused struct mount *mp, __unused int fhlen, __unused unsigned char *fhp, __unused struct vnode **vpp, __unused vfs_context_t ctx);
+static int routefs_vptofh (__unused struct vnode *vp, __unused int *fhlenp, __unused unsigned char *fhp, __unused vfs_context_t ctx);
+static int routefs_sysctl(__unused int *name, __unused u_int namelen, __unused user_addr_t oldp,
+                          __unused size_t *oldlenp, __unused user_addr_t newp,
+                          __unused size_t newlen, __unused vfs_context_t ctx);
+static int routefserr_lookup(__unused struct vnop_lookup_args * args);
+
+static int routefserr_setlabel(__unused struct vnop_setlabel_args * args);
+
+
+lck_grp_t       * routefs_lck_grp;
+lck_grp_attr_t  * routefs_lck_grp_attr;
+lck_attr_t      * routefs_lck_attr;
+lck_mtx_t         routefs_mutex;
+
+#define ROUTEFS_LOCK()    lck_mtx_lock(&routefs_mutex)
+#define ROUTEFS_UNLOCK()  lck_mtx_unlock(&routefs_mutex)
+static int _lock_inited = 0;
+static boolean_t _fs_alreadyMounted = FALSE;  /* atleast a mount of this filesystem is present */
+
+static int
+routefs_init(__unused struct vfsconf *vfsp)
+{
+    routefs_lck_grp_attr = lck_grp_attr_alloc_init();
+    routefs_lck_grp = lck_grp_alloc_init("routefs_lock", routefs_lck_grp_attr);
+    routefs_lck_attr = lck_attr_alloc_init();
+    lck_mtx_init(&routefs_mutex, routefs_lck_grp, routefs_lck_attr);
+    _lock_inited = 1;
+    
+    return 0;
+}
+
+static int
+routefs_mount(struct mount *mp, __unused vnode_t devvp,  user_addr_t data, vfs_context_t ctx)
+{
+    struct routefs_mount *routefs_mp_p = NULL; /* routefs specific mount info */
+       int error=EINVAL;
+    struct routefs_args * rargs = (struct routefs_args *)data;
+    
+       /*-
+        *  If they just want to update, we don't need to do anything.
+        */
+       if (mp->mnt_flag & MNT_UPDATE)
+       {
+               return 0;
+       }
+
+    
+    /* check for root mount only */
+    if ((error = proc_suser(current_proc()))!= 0) {
+        goto out;
+    }
+        
+    if (vfs_iskernelmount(mp) == FALSE) {
+        error = EPERM;
+        goto out;
+    }
+    
+    if (_fs_alreadyMounted == TRUE) {
+        /* if a filesystem is mounted, it needs to be unmounted prior to mount again */
+        error = EPERM;
+        goto out;
+    }
+    
+       /* Advisory locking should be handled at the VFS layer */
+       vfs_setlocklocal(mp);
+
+       /*-
+        *  Well, it's not an update, it's a real mount request.
+        *  Time to get dirty.
+        * HERE we should check to see if we are already mounted here.
+        */
+
+       MALLOC(routefs_mp_p, struct routefs_mount *, sizeof(struct routefs_mount),
+              M_TEMP, M_WAITOK);
+       if (routefs_mp_p == NULL)
+               return (ENOMEM);
+       bzero(routefs_mp_p, sizeof(*routefs_mp_p));
+    
+       routefs_mp_p->route_mount = mp;
+
+    if (rargs->route_rvp == NULLVP) {
+        error = EACCES;
+        goto out;
+    }
+        
+    strlcpy(routefs_mp_p->route_path,rargs->route_path, MAXPATHLEN);
+    routefs_mp_p->route_rvp = rargs->route_rvp;
+    routefs_mp_p->route_vpvid = vnode_vid(rargs->route_rvp);
+    
+    if (vnode_ref(routefs_mp_p->route_rvp) != 0) {
+        error = EACCES;
+        goto out;
+    }
+
+    /*
+        *  Fill out some fields
+        */
+       __IGNORE_WCASTALIGN(mp->mnt_data = (qaddr_t)routefs_mp_p);
+       mp->mnt_vfsstat.f_fsid.val[0] = (int32_t)(uintptr_t)routefs_mp_p;
+       mp->mnt_vfsstat.f_fsid.val[1] = vfs_typenum(mp);
+       mp->mnt_flag |= MNT_LOCAL;
+
+       /*-
+        *  Copy in the name of the directory the filesystem
+        *  is to be mounted on.
+        *  And we clear the remainder of the character strings
+        *  to be tidy.
+        */
+       
+       bzero(mp->mnt_vfsstat.f_mntfromname, MAXPATHLEN);
+       bcopy("routefs",mp->mnt_vfsstat.f_mntfromname, 5);
+       (void)routefs_statfs(mp, &mp->mnt_vfsstat, ctx);
+    _fs_alreadyMounted = TRUE;      /* yep, fs is in play now */
+       error = 0;
+out:
+    if (error != 0) {
+        if (routefs_mp_p != NULL)
+            FREE((caddr_t)routefs_mp_p, M_TEMP);
+    }
+       return error;
+}
+
+
+static int
+routefs_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t ctx)
+{
+       return 0;
+}
+
+/*-
+ *  Unmount the filesystem described by mp.
+ */
+static int
+routefs_unmount( struct mount *mp, int mntflags, __unused vfs_context_t ctx)
+{
+       struct routefs_mount *routefs_mp_p = (struct routefs_mount *)mp->mnt_data;
+       int flags = 0;
+       int force = 0;
+       int error;
+       
+    /* check for root unmount only */
+    if ((error = proc_suser(current_proc()))!= 0) {
+        return(error);
+    }
+
+       if (mntflags & MNT_FORCE) {
+               flags |= FORCECLOSE;
+               force = 1;
+       }
+    /* giveup the ioref of vnode, no longer need it */
+    if (routefs_mp_p->route_rvp != NULLVP) {
+        if (vnode_getwithref(routefs_mp_p->route_rvp) == 0) {
+            vnode_rele(routefs_mp_p->route_rvp);
+            vnode_put(routefs_mp_p->route_rvp);
+            routefs_mp_p->route_rvp = NULLVP;
+        }
+    }
+    /* no vnodes, ignore any errors */
+    (void)vflush(mp, NULLVP, flags);
+       FREE((caddr_t)routefs_mp_p, M_TEMP);
+       mp->mnt_data = (qaddr_t)0;
+       mp->mnt_flag &= ~MNT_LOCAL;
+    _fs_alreadyMounted = FALSE; /* unmounted the fs, only one allowed at a time */
+       return 0;
+}
+
+/* return the address of the root vnode  in *vpp */
+static int
+routefs_root(struct mount *mp, struct vnode **vpp, __unused vfs_context_t ctx)
+{
+       struct routefs_mount *routefs_mp_p = (struct routefs_mount *)(mp->mnt_data);
+       int error=0;
+
+    /* check for nullvp incase its being rolled */
+    if (routefs_mp_p->route_rvp == NULLVP) {
+        ROUTEFS_LOCK();
+        if (routefs_mp_p->route_rvp == NULLVP) {
+            ROUTEFS_UNLOCK();
+            error = EACCES;
+            goto out;
+        }
+        ROUTEFS_UNLOCK();
+    }
+    if (vnode_getwithvid(routefs_mp_p->route_rvp, routefs_mp_p->route_vpvid) != 0) {
+        /* only one in the path., since no vnodes with this, you can hold across this call */
+        ROUTEFS_LOCK();
+        if (vnode_getwithref(routefs_mp_p->route_rvp) == 0) {
+            vnode_rele(routefs_mp_p->route_rvp);
+            vnode_put(routefs_mp_p->route_rvp);
+            routefs_mp_p->route_rvp = NULLVP;
+            routefs_mp_p->route_vpvid = -1;
+            error = vnode_lookup(routefs_mp_p->route_path, FREAD|O_DIRECTORY, &routefs_mp_p->route_rvp, ctx);
+            if (error == 0)
+                routefs_mp_p->route_vpvid = vnode_vid(routefs_mp_p->route_rvp);
+        } else {
+            error = EACCES;
+        }
+        ROUTEFS_UNLOCK();
+        
+        if (error != 0)
+               goto out;
+    }
+    *vpp = routefs_mp_p->route_rvp;
+out:
+       return error;
+}
+
+static int
+routefs_statfs( struct mount *mp, struct vfsstatfs *sbp, __unused vfs_context_t ctx)
+{
+       struct routefs_mount *routefs_mp_p = (struct routefs_mount *)mp->mnt_data;
+
+       /*-
+        *  Fill in the stat block.
+        */
+       //sbp->f_type   = mp->mnt_vfsstat.f_type;
+       sbp->f_flags  = 0;              /* XXX */
+       sbp->f_bsize  = 512;
+       sbp->f_iosize = 512;
+       sbp->f_blocks = (sizeof(struct routefs_mount)+ sbp->f_bsize) / sbp->f_bsize;
+       sbp->f_bfree  = 0;
+       sbp->f_bavail = 0;
+       sbp->f_files  = 0;
+       sbp->f_ffree  = 0;
+       sbp->f_fsid.val[0] = (int32_t)(uintptr_t)routefs_mp_p;
+       sbp->f_fsid.val[1] = vfs_typenum(mp);
+
+       return 0;
+}
+
+static int
+routefs_vfs_getattr(__unused mount_t mp, struct vfs_attr *fsap, __unused vfs_context_t ctx)
+{
+       VFSATTR_RETURN(fsap, f_objcount, 1);
+       VFSATTR_RETURN(fsap, f_maxobjcount, 1);
+       VFSATTR_RETURN(fsap, f_bsize, 512);
+       VFSATTR_RETURN(fsap, f_iosize, 512);
+       if (VFSATTR_IS_ACTIVE(fsap, f_blocks) || VFSATTR_IS_ACTIVE(fsap, f_bused)) {
+               fsap->f_blocks = (sizeof(struct routefs_mount)+ fsap->f_bsize) / fsap->f_bsize;
+               fsap->f_bused = fsap->f_blocks;
+               VFSATTR_SET_SUPPORTED(fsap, f_blocks);
+               VFSATTR_SET_SUPPORTED(fsap, f_bused);
+       }
+       VFSATTR_RETURN(fsap, f_bfree, 0);
+       VFSATTR_RETURN(fsap, f_bavail, 0);
+       VFSATTR_RETURN(fsap, f_files, 0);
+       VFSATTR_RETURN(fsap, f_ffree, 0);
+       VFSATTR_RETURN(fsap, f_fssubtype, 0);
+       
+       if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
+               fsap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] =
+                       VOL_CAP_FMT_SYMBOLICLINKS |
+                       VOL_CAP_FMT_HARDLINKS |
+                       VOL_CAP_FMT_NO_ROOT_TIMES |
+                       VOL_CAP_FMT_CASE_SENSITIVE |
+                       VOL_CAP_FMT_CASE_PRESERVING |
+                       VOL_CAP_FMT_FAST_STATFS |
+                       VOL_CAP_FMT_2TB_FILESIZE |
+                       VOL_CAP_FMT_HIDDEN_FILES;
+               fsap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] =
+                       VOL_CAP_INT_ATTRLIST ;
+               fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
+               fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
+               
+               fsap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] =
+                       VOL_CAP_FMT_PERSISTENTOBJECTIDS |
+                       VOL_CAP_FMT_SYMBOLICLINKS |
+                       VOL_CAP_FMT_HARDLINKS |
+                       VOL_CAP_FMT_JOURNAL |
+                       VOL_CAP_FMT_JOURNAL_ACTIVE |
+                       VOL_CAP_FMT_NO_ROOT_TIMES |
+                       VOL_CAP_FMT_SPARSE_FILES |
+                       VOL_CAP_FMT_ZERO_RUNS |
+                       VOL_CAP_FMT_CASE_SENSITIVE |
+                       VOL_CAP_FMT_CASE_PRESERVING |
+                       VOL_CAP_FMT_FAST_STATFS |
+                       VOL_CAP_FMT_2TB_FILESIZE |
+                       VOL_CAP_FMT_OPENDENYMODES |
+                       VOL_CAP_FMT_HIDDEN_FILES |
+                       VOL_CAP_FMT_PATH_FROM_ID |
+                       VOL_CAP_FMT_NO_VOLUME_SIZES;
+               fsap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] =
+                       VOL_CAP_INT_SEARCHFS |
+                       VOL_CAP_INT_ATTRLIST |
+                       VOL_CAP_INT_NFSEXPORT |
+                       VOL_CAP_INT_READDIRATTR |
+                       VOL_CAP_INT_EXCHANGEDATA |
+                       VOL_CAP_INT_COPYFILE |
+                       VOL_CAP_INT_ALLOCATE |
+                       VOL_CAP_INT_VOL_RENAME |
+                       VOL_CAP_INT_ADVLOCK |
+                       VOL_CAP_INT_FLOCK |
+                       VOL_CAP_INT_EXTENDED_SECURITY |
+                       VOL_CAP_INT_USERACCESS |
+                       VOL_CAP_INT_MANLOCK |
+                       VOL_CAP_INT_EXTENDED_ATTR |
+                       VOL_CAP_INT_NAMEDSTREAMS;
+               fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
+               fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
+               
+               VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
+       }
+       
+       if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
+               fsap->f_attributes.validattr.commonattr =
+                       ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID |
+                       ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |
+                       ATTR_CMN_PAROBJID |
+                       ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME |
+                       ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK |
+                       ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID;
+               fsap->f_attributes.validattr.volattr =
+                       ATTR_VOL_FSTYPE | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |
+                       ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |
+                       ATTR_VOL_OBJCOUNT | ATTR_VOL_MAXOBJCOUNT |
+                       ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |
+                       ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |
+                       ATTR_VOL_ATTRIBUTES;
+               fsap->f_attributes.validattr.dirattr =
+                       ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS;
+               fsap->f_attributes.validattr.fileattr =
+                       ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |
+                       ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE |
+                       ATTR_FILE_DATALENGTH;
+               fsap->f_attributes.validattr.forkattr = 0;
+               
+               fsap->f_attributes.nativeattr.commonattr =
+                       ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID |
+                       ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |
+                       ATTR_CMN_PAROBJID |
+                       ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME |
+                       ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK |
+                       ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID;
+               fsap->f_attributes.nativeattr.volattr =
+                       ATTR_VOL_FSTYPE | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |
+                       ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |
+                       ATTR_VOL_OBJCOUNT | ATTR_VOL_MAXOBJCOUNT |
+                       ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |
+                       ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |
+                       ATTR_VOL_ATTRIBUTES;
+               fsap->f_attributes.nativeattr.dirattr =
+                       ATTR_DIR_MOUNTSTATUS;
+               fsap->f_attributes.nativeattr.fileattr =
+                       ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |
+                       ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE |
+                       ATTR_FILE_DATALENGTH;
+               fsap->f_attributes.nativeattr.forkattr = 0;
+
+               VFSATTR_SET_SUPPORTED(fsap, f_attributes);
+       }
+       
+       return 0;
+}
+
+static int
+routefs_sync(__unused struct mount *mp, __unused int waitfor, __unused vfs_context_t ctx)
+{
+    return (0);
+}
+
+
+static int
+routefs_vget(__unused struct mount *mp, __unused ino64_t ino, __unused struct vnode **vpp, __unused vfs_context_t ctx)
+{
+       return ENOTSUP;
+}
+
+static int
+routefs_fhtovp (__unused struct mount *mp, __unused int fhlen, __unused unsigned char *fhp, __unused struct vnode **vpp, __unused vfs_context_t ctx)
+{
+       return (EINVAL);
+}
+
+
+static int
+routefs_vptofh (__unused struct vnode *vp, __unused int *fhlenp, __unused unsigned char *fhp, __unused vfs_context_t ctx)
+{
+       return (EINVAL);
+}
+
+static int
+routefs_sysctl(__unused int *name, __unused u_int namelen, __unused user_addr_t oldp,
+             __unused size_t *oldlenp, __unused user_addr_t newp, 
+             __unused size_t newlen, __unused vfs_context_t ctx)
+{
+    return (ENOTSUP);
+}
+
+#include <sys/namei.h>
+#define MOBILE_DIR_PATH "/private/var/mobile"
+/*
+ * Function: routefs_kernel_mount
+ * Purpose:
+ *   Mount routefs at the given mount point from within the kernel.
+ */
+int
+routefs_kernel_mount(char * routepath)
+{
+    int error = EINVAL;
+       vfs_context_t ctx = vfs_context_kernel();
+       char fsname[] = "routefs";
+    struct routefs_args args;
+    char mounthere[] = MOBILE_DIR_PATH;  /* !const because of internal casting */
+    
+    bzero(&args, sizeof(struct routefs_args));
+    strlcpy(args.route_path, routepath, MAXPATHLEN);
+    error = vnode_lookup(args.route_path, FREAD|O_DIRECTORY, &args.route_rvp, ctx);
+    if (error) {
+        goto out;
+       }
+
+    if (!vnode_isdir(args.route_rvp)) {
+        error = EACCES;
+        goto out;
+    }
+
+    error = kernel_mount(fsname, NULLVP, NULLVP, mounthere, &args, 0, MNT_DONTBROWSE, KERNEL_MOUNT_NOAUTH, ctx);
+       if (error) {
+               goto out;
+       }
+
+out:
+    if(args.route_rvp != NULLVP)
+        (void) vnode_put(args.route_rvp);
+       return (error);
+}
+
+struct vfsops routefs_vfsops = {
+       .vfs_mount = routefs_mount,
+       .vfs_start = routefs_start,
+       .vfs_unmount = routefs_unmount,
+       .vfs_root = routefs_root,
+       .vfs_getattr = routefs_vfs_getattr,
+       .vfs_sync = routefs_sync,
+       .vfs_vget = routefs_vget,
+       .vfs_fhtovp = routefs_fhtovp,
+       .vfs_vptofh = routefs_vptofh,
+       .vfs_init = routefs_init,
+       .vfs_sysctl = routefs_sysctl,
+       // There are other VFS ops that we do not support
+};
+
+static int routefserr_lookup(__unused struct vnop_lookup_args * args)
+{
+    return (ENOTSUP);
+}
+
+static int routefserr_setlabel(__unused struct vnop_setlabel_args * args)
+{
+    return (ENOTSUP);
+    
+}
+
+#define VOPFUNC int (*)(void *)
+
+/* The following ops are used by directories and symlinks */
+int (**routefs_vnodeop_p)(void *);
+static struct vnodeopv_entry_desc routefs_vnodeop_entries[] = {
+    { &vnop_default_desc, (VOPFUNC)vn_default_error },
+    { &vnop_lookup_desc, (VOPFUNC)routefserr_lookup },         /* lookup */
+    { &vnop_create_desc, (VOPFUNC)err_create },                /* create */
+    { &vnop_whiteout_desc, (VOPFUNC)err_whiteout },            /* whiteout */
+    { &vnop_mknod_desc, (VOPFUNC)err_mknod },          /* mknod */
+    { &vnop_open_desc, (VOPFUNC)err_open },                    /* open */
+    { &vnop_close_desc, (VOPFUNC)err_close },          /* close */
+    { &vnop_getattr_desc, (VOPFUNC)err_getattr },              /* getattr */
+    { &vnop_setattr_desc, (VOPFUNC)err_setattr },              /* setattr */
+    { &vnop_read_desc, (VOPFUNC)err_read },            /* read */
+    { &vnop_write_desc, (VOPFUNC)err_write },          /* write */
+    { &vnop_ioctl_desc, (VOPFUNC)err_ioctl },          /* ioctl */
+    { &vnop_select_desc, (VOPFUNC)err_select },                /* select */
+    { &vnop_revoke_desc, (VOPFUNC)err_revoke },                /* revoke */
+    { &vnop_mmap_desc, (VOPFUNC)err_mmap },                    /* mmap */
+    { &vnop_fsync_desc, (VOPFUNC)nop_fsync },          /* fsync */
+    { &vnop_remove_desc, (VOPFUNC)err_remove },        /* remove */
+    { &vnop_link_desc, (VOPFUNC)err_link },            /* link */
+    { &vnop_rename_desc, (VOPFUNC)err_rename },                /* rename */
+    { &vnop_mkdir_desc, (VOPFUNC)err_mkdir },          /* mkdir */
+    { &vnop_rmdir_desc, (VOPFUNC)err_rmdir },          /* rmdir */
+    { &vnop_symlink_desc, (VOPFUNC)err_symlink },              /* symlink */
+    { &vnop_readdir_desc, (VOPFUNC)err_readdir },              /* readdir */
+    { &vnop_readlink_desc, (VOPFUNC)err_readlink },    /* readlink */
+    { &vnop_inactive_desc, (VOPFUNC)err_inactive },    /* inactive */
+    { &vnop_reclaim_desc, (VOPFUNC)err_reclaim },              /* reclaim */
+    { &vnop_strategy_desc, (VOPFUNC)err_strategy },            /* strategy */
+    { &vnop_pathconf_desc, (VOPFUNC)err_pathconf },    /* pathconf */
+    { &vnop_advlock_desc, (VOPFUNC)err_advlock },              /* advlock */
+    { &vnop_bwrite_desc, (VOPFUNC)err_bwrite },
+    { &vnop_pagein_desc, (VOPFUNC)err_pagein },                /* Pagein */
+    { &vnop_pageout_desc, (VOPFUNC)err_pageout },              /* Pageout */
+    { &vnop_copyfile_desc, (VOPFUNC)err_copyfile },            /* Copyfile */
+    { &vnop_blktooff_desc, (VOPFUNC)err_blktooff },            /* blktooff */
+    { &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },            /* offtoblk */
+    { &vnop_blockmap_desc, (VOPFUNC)err_blockmap },            /* blockmap */
+#if CONFIG_MACF
+    { &vnop_setlabel_desc, (VOPFUNC)routefserr_setlabel },       /* setlabel */
+#endif
+    { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc routefs_vnodeop_opv_desc =
+{ &routefs_vnodeop_p, routefs_vnodeop_entries };
+
+
+
index 6d89331b1b60b41bfe56f0a88f4e15c45c879c35..b7fa46e15bc0a04ec69c611c525222b19e6a3327 100644 (file)
@@ -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;
        }
 
index 57d48d21c671d98c62d5eeed21edb39ef65aeff1..a58ce458ccefecb50fc7026c75009a492e604343 100644 (file)
@@ -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;
index fc7ce88fb6e9f53a5d090a0ebc2809e92e7b7883..21066e6520ec26ff946f88b2679adb875af39a1f 100644 (file)
@@ -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;
 };
index a2289eca07988f5ccef6ec2ace53f6804296d7c2..a64dd0f03717b53dd0d2a5d957ce4f406643f11e 100644 (file)
@@ -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) {
index e40268c30a510fec642b7ee5ea67bf4a51208e28..6da23d1a345365b4bd555ed5c46ba961ba08ef6d 100644 (file)
@@ -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);
index 9d42c7c6de847f31c010cc1954754d809c279e42..f742a560a052c05679212a27f75db1ab678d67fe 100644 (file)
@@ -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)
index 1d479d3ab6d8bd5279b634a7a45676e81d253a0a..ae6cdf1a0785356b133ba57354463bc51ecf0f26 100644 (file)
@@ -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
index 58a20fe4f79e1ff830b11a9993789400c49dcd66..92808d67edec6f8c01396afc0786588ad7049c3d 100644 (file)
@@ -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;
index d6c5801f5858b040f2ad15f9305cae2e3c07f561..3df62ef3c229c0a3b406a8bd65ea915cd3e595e5 100644 (file)
@@ -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;
                        }
                }
index 76e29f8d607a50027fec5e7b0856e9db2c77a13b..cc2e2c8fe54a2c4c19d149d6368f12d40040d747 100644 (file)
@@ -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;
index 0968d9ad0e486208f529435c37274cc4454fd3e3..1af72b8bef64ddbb0dd39779723d0b0915d0bbbe 100644 (file)
@@ -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);
index a3b025eb7f555008229b468a38211069b4345793..a2b89bb8b9b7aa3b0ce9043b9a783cf561cf813f 100644 (file)
@@ -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;
index f25e77c0534e9e21ee0d986fe8425ba3512876a1..9f65560f839c2f2923a741b5115ad051bf3b9856 100644 (file)
@@ -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);
        }
index 2a00f895f21969270f3904977bd685af7446f273..e54d8b996cc276a23c52c261d6434a2afc08d0b5 100644 (file)
@@ -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;
index 383751d4d07d559537beb48acd059ab80a1b5e6a..2788b0e79df671c1cbf22dd82b35e215b0257957 100644 (file)
@@ -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;
 
index 304c4c05ca15986d13de9c652c9ef59b3cc45e13..31ea83fd0b6a9022a2d9c451e33bdbe3945a64e1 100644 (file)
@@ -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);
 
index 08bb5102f3b2d24c41324b85de0faabae5e32dc9..65eafb0bef262421ecefe713dfafba1472ac8def 100644 (file)
@@ -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,
index d218931be567b8f8d4a0f89c21db93404325690d..71ea9f4a89cef360370e9f399d6582becd2302a6 100644 (file)
@@ -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,
index 414e76c5f1e9344ecf2bb7479374eb51fd1bb67b..834a26e209c57a44f321de0d933fa15674ccf3b1 100644 (file)
@@ -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;
 
index b4ecb3ff09e4985aab8be9dfc55aa0f6f3b800f3..d9a35da3030b24fe44d8d320dee28b5de10eddf1 100644 (file)
@@ -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);
+       }
+}
+
index d61ad1fc3c6e3d964c6d128991410648545df9ae..e0b8fbcbc9735960fa387c84896d80dc7bce9704 100644 (file)
@@ -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);
+}
index 905ab934a815cc68dce73b76dd59823c4f6bc857..c4fdf2c7edc5fa397684149746082b8f0171d9d5 100644 (file)
@@ -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,
index 1d65c4355d014eef26fc05f41964f588c56518e5..38e98861414f66a94fe409a2de42ae9ba454bfe9 100644 (file)
@@ -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);
                                }
index 86d4a71f308eabae174c7631e63647af1be5dab4..4dfd0bc8f68bd40ca3806f4c4b5d8e9dcbd1e20e 100644 (file)
@@ -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++;
index 65a171fed3df0d3e01c65efda38d85265d40217a..88d18875a4913b37296dc39ef9d06722e6c08e1b 100644 (file)
@@ -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;
index 0ffb340d0400845510d56e48104aea566f7cfd46..bda29ca8200288a32f622bad9fa02d6220014727 100644 (file)
@@ -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);
index bfc86e99493eafe2848062fe90fcabe18d49b3af..c80d20e8a6181301e2ba3dbe9c4a72921da09d51 100644 (file)
@@ -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);
index 26f5b49d089eb1ccd6a494e01e9675ad7a69e14d..510d1cd844b9b26766c80ee6b721edcea1ffa8de 100644 (file)
@@ -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 */
index 1718e87bd936f7b1a2543d5ade6b6f996dcee664..e51ea9b0f972fc870b021907bbd32488a8a5d031 100644 (file)
@@ -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) {
index 2f5ab8fbfbddbf4c3142b0453725b3e68f25a10f..ee5a7045369e7f99e4ad65b3dcc4b5ea926ac305 100644 (file)
@@ -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);
index 43259eea97cfef6487f415363436beb4474ffef8..d3ef5ed0c64bd5fdc4dbd2a97b2b1294cdacbe5a 100644 (file)
@@ -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
index 6227ff00321c2901c47fccfbaf2e235be65f254d..a363d3a92a45eed8487256afefe4122b72d66c0f 100644 (file)
@@ -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);
index 1e0f9eb37d52770d0a7eeb34dc1f3b8c1b83a0bb..f4b1f11cca45f952c32b507218d420677ba2d194 100644 (file)
@@ -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;
        }
index 0e2e7df2e3cf1632016ee7abca6a4f9c38493128..2d91aa70b219e51a54f666f1e2208b290e160b0d 100644 (file)
@@ -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);
index 4b4072516e57a176c8ae46a9c26b6505ba0fe625..f07d5649fd88cf2c2d0a025d0e6827b8947042ee 100644 (file)
@@ -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 \
index ffda243263399cab69c146320e46a900939cdbb8..556a311d00a320a68f89aa5cbb950ee076df1db1 100644 (file)
 
 #include <sys/codesign.h>
 
-const 
-CS_CodeDirectory *findCodeDirectory(
-       const CS_SuperBlob *embedded,
-       const char *lower_bound,
-       const char *upper_bound);
-
 #endif
index 58509c690509db284072bf6a310c9d1845d9dde0..5f78699a4e48a52b798a323ebd8b759dac4cb0dc 100644 (file)
@@ -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 */
 };
 
 
index b2f59f1c3104e4ef70cecc736258dab399f3b8db..cbff7a08bcdfcf0320336d3cf322f22fa1915cef 100644 (file)
@@ -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
  * 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);
index c99d47ba716c2e0b97d46617495081e9d22b5399..623d16f557a212950e89927ce14a5cdd00ff5686 100644 (file)
@@ -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
 //
 
 //
index 03db89c613ad3c3a12e826f75440f21326044e4e..57df28890825c0266e31c13d428e99d4cb1c3bfc 100644 (file)
@@ -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 */
 };
 
 /*
index d2e340aa206cc0341de5e852d68da85d6a6b6d27..956d7234c8cba775556c4a21b173be33d86c4abf 100644 (file)
@@ -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
index c740032913d340d90c32aff9361370e9278b4d55..0cb7e52b72c57807503d9e11bc120fdc6a6e1604 100644 (file)
@@ -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
 
index 2f6437348fdd4378881f3a1aad006645c0cedbde..2db9e57792d8d2494a985548acdcb2b4d47997b1 100644 (file)
@@ -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 */
index e73e4b9af554a610e9f96911d73ccbcae7415bad..2f966ca5dbb700d8e9e8962dfc0325824671b942 100644 (file)
@@ -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 (file)
index 0000000..d091205
--- /dev/null
@@ -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 <sys/param.h>
+
+enum {
+       PERSONA_INVALID = 0,
+       PERSONA_GUEST   = 1,
+       PERSONA_MANAGED = 2,
+       PERSONA_PRIV    = 3,
+       PERSONA_SYSTEM  = 4,
+
+       PERSONA_TYPE_MAX = PERSONA_SYSTEM,
+};
+
+#define PERSONA_ID_NONE ((uid_t)-1)
+
+struct kpersona_info {
+       uint32_t persona_info_version;
+
+       uid_t    persona_id; /* overlaps with UID */
+       int      persona_type;
+       gid_t    persona_gid;
+       uint32_t persona_ngroups;
+       gid_t    persona_groups[NGROUPS];
+       uid_t    persona_gmuid;
+       char     persona_name[MAXLOGNAME+1];
+
+       /* TODO: MAC policies?! */
+};
+
+#define PERSONA_INFO_V1       1
+#define PERSONA_INFO_V1_SIZE  (sizeof(struct kpersona_info))
+
+
+#define PERSONA_OP_ALLOC    1
+#define PERSONA_OP_DEALLOC  2
+#define PERSONA_OP_GET      3
+#define PERSONA_OP_INFO     4
+#define PERSONA_OP_PIDINFO  5
+#define PERSONA_OP_FIND     6
+
+#ifndef KERNEL
+/*
+ * user space persona interface
+ */
+
+/*
+ * kpersona_alloc: Allocate a new in-kernel persona
+ *
+ * Parameters:
+ *       info: Pointer to persona info structure describing the
+ *             attributes of the persona to create / allocate.
+ *
+ *         id: output: set to the ID of the created persona
+ *
+ * Note:
+ *      The 'persona_id' field of the 'info' parameter is ignored.
+ *
+ * Return:
+ *        != 0: ERROR
+ *        == 0: Success
+ */
+int kpersona_alloc(struct kpersona_info *info, uid_t *id);
+
+/*
+ * kpersona_dealloc: delete / destroy an in-kernel persona
+ *
+ * Parameters:
+ *         id: the ID of the persona to destroy
+ *
+ * Return:
+ *        < 0: ERROR
+ *          0: Success
+ */
+int kpersona_dealloc(uid_t id);
+
+
+/*
+ * kpersona_get: retrieve the persona with which the current thread is running
+ *
+ * Parameters:
+ *         id: output: will be filled with current thread's persona
+ *             (or current processes persona) on success.
+ *
+ * Return:
+ *        < 0: Thread is not running under any persona
+ *          0: Success (uuid is filled with running persona UUID)
+ */
+int kpersona_get(uid_t *id);
+
+
+/*
+ * kpersona_info: gather info about the given persona
+ *
+ * Parameters:
+ *         id: ID of the persona to investigate
+ *
+ *       info: output: filled in with persona attributes on success.
+ *
+ * Return:
+ *        < 0: ERROR
+ *          0: Success
+ */
+int kpersona_info(uid_t id, struct kpersona_info *info);
+
+
+/*
+ * kpersona_pidinfo: gather persona info about the given PID
+ *
+ * Parameters:
+ *        pid: PID of the process whose persona info we're to return
+ *
+ *       info: output: filled in with persona attributes on success.
+ *
+ * Return:
+ *        < 0: ERROR
+ *          0: Success
+ */
+int kpersona_pidinfo(pid_t pid, struct kpersona_info *info);
+
+
+/*
+ * kpersona_find: lookup the kernel's UUID of a persona
+ *
+ * Parameters:
+ *       name: Local login name of the persona.
+ *             Set this to NULL to find personas by 'uid'.
+ *
+ *        uid: UID of the persona.
+ *             Set this to -1 to find personas by 'name'
+ *
+ *         id: output: the ID(s) matching the input parameters
+ *      idlen: input - size of 'id' buffer (in number of IDs)
+ *             output - the total required size of the 'id' buffer
+ *                      (in number of IDs) - may be larger than input size
+ * Note:
+ *      At least one of 'name' or 'uid' must be set.
+ *
+ * Return:
+ *         < 0: ERROR
+ *        >= 0: The number of IDs found to match the input parameters
+ */
+int kpersona_find(const char *name, uid_t uid, uid_t *id, size_t *idlen);
+#endif /* !KERNEL */
+
+#ifdef KERNEL_PRIVATE
+/* XNU + kext private interface */
+#include <sys/cdefs.h>
+#include <sys/kauth.h>
+#include <libkern/libkern.h>
+
+#ifdef PERSONA_DEBUG
+#define persona_dbg(fmt, ...) \
+       printf("[%4d] %s:  " fmt "\n", \
+              current_proc() ? current_proc()->p_pid : -1, \
+              __func__, ## __VA_ARGS__)
+#else
+#define persona_dbg(fmt, ...) do { } while (0)
+#endif
+
+/*
+ * Persona
+ */
+#ifdef XNU_KERNEL_PRIVATE
+/* only XNU proper needs to see the persona structure */
+struct persona {
+       int32_t      pna_refcount;
+       int32_t      pna_valid;
+
+       uid_t        pna_id;
+       int          pna_type;
+       char         pna_login[MAXLOGNAME+1];
+
+       kauth_cred_t pna_cred;
+       uid_t        pna_pgid;
+
+       int          pna_cred_locked; /* set upon first adoption */
+
+       LIST_ENTRY(persona) pna_list;
+
+       /* this could go away if we used a coalition */
+       LIST_HEAD(, proc)   pna_members;
+
+       lck_mtx_t    pna_lock;
+
+       /*
+        * We can add things here such as PID maps, UID maps, etc.
+        */
+#ifdef PERSONA_DEBUG
+       char         pna_desc[128];
+#endif
+};
+
+#define persona_lock(persona)     lck_mtx_lock(&(persona)->pna_lock)
+#define persona_unlock(persona)   lck_mtx_unlock(&(persona)->pna_lock)
+#define persona_try_lock(persona) lck_mtx_try_lock(&(persona)->pna_lock)
+
+#define persona_lock_assert_held(persona) \
+       lck_mtx_assert(&(persona)->pna_lock, LCK_MTX_ASSERT_OWNED)
+
+#ifdef PERSONA_DEBUG
+static inline const char *persona_desc(struct persona *persona, int locked)
+{
+       if (!persona)
+               return "<none>";
+
+       if (persona->pna_desc[0] != 0)
+               return persona->pna_desc;
+
+       if (!locked)
+               persona_lock(persona);
+       if (persona->pna_desc[0] != 0)
+               goto out_unlock;
+
+       char *p = &persona->pna_desc[0];
+       char *end = p + sizeof(persona->pna_desc) - 1;
+
+       *end = 0;
+       p += snprintf(p, end - p, "%s/%d:%d",
+                     persona->pna_login,
+                     kauth_cred_getuid(persona->pna_cred),
+                     kauth_cred_getgid(persona->pna_cred));
+
+       if (p <= end)
+               *p = 0;
+out_unlock:
+       if (!locked)
+               persona_unlock(persona);
+
+       return persona->pna_desc;
+}
+#else /* !PERSONA_DEBUG */
+static inline const char *persona_desc(struct persona *persona, int locked)
+{
+       (void)persona;
+       (void)locked;
+       return "<persona>";
+}
+#endif
+
+#else /* !XNU_KERNEL_PRIVATE */
+/* kexts should only see an opaque persona structure */
+struct persona;
+#endif
+
+__BEGIN_DECLS
+
+#ifndef _KAUTH_CRED_T
+#define        _KAUTH_CRED_T
+typedef struct ucred *kauth_cred_t;
+#endif /* !_KAUTH_CRED_T */
+
+/* returns the persona ID for the given pesona structure */
+uid_t persona_get_id(struct persona *persona);
+
+/* returns the type of the persona (see enum above: PERSONA_GUEST, etc.) */
+int persona_get_type(struct persona *persona);
+
+/* returns ref on kauth_cred_t that must be dropped via kauth_cred_unref() */
+kauth_cred_t persona_get_cred(struct persona *persona);
+
+/* returns a reference that must be released with persona_put() */
+struct persona *persona_lookup(uid_t id);
+
+/*
+ * returns non-zero on error, on success returns 0 and updates 'plen' to
+ * total found (could be more than original value of 'plen')
+ */
+int persona_find(const char *login, uid_t uid,
+                struct persona **persona, size_t *plen);
+
+/* returns a reference to the persona tied to the current thread */
+struct persona *current_persona_get(void);
+
+/* get a reference to a persona structure */
+struct persona *persona_get(struct persona *persona);
+
+/* release a reference to a persona structure */
+void persona_put(struct persona *persona);
+
+#ifdef XNU_KERNEL_PRIVATE
+
+#if CONFIG_PERSONAS
+#include <sys/proc_internal.h>
+
+/*
+ * In-kernel persona API
+ */
+extern uint32_t g_max_personas;
+extern struct persona *g_system_persona;
+
+void personas_bootstrap(void);
+
+struct persona *persona_alloc(uid_t id, const char *login,
+                             int type, int *error);
+int persona_invalidate(struct persona *persona);
+
+static inline int proc_has_persona(proc_t p)
+{
+       if (p && p->p_persona)
+               return 1;
+       return 0;
+}
+
+static inline uid_t persona_id_from_proc(proc_t p)
+{
+       if (p && p->p_persona)
+               return p->p_persona->pna_id;
+       return PERSONA_ID_NONE;
+}
+
+int persona_proc_inherit(proc_t child, proc_t parent);
+
+int persona_proc_adopt_id(proc_t p, uid_t id,
+                         kauth_cred_t auth_override);
+int persona_proc_adopt(proc_t p, struct persona *persona,
+                      kauth_cred_t auth_override);
+int persona_proc_drop(proc_t p);
+
+int persona_set_cred(struct persona *persona, kauth_cred_t cred);
+int persona_set_cred_from_proc(struct persona *persona, proc_t proc);
+
+uid_t persona_get_uid(struct persona *persona);
+
+int persona_set_gid(struct persona *persona, gid_t gid);
+gid_t persona_get_gid(struct persona *persona);
+
+int persona_set_groups(struct persona *persona, gid_t *groups, int ngroups, uid_t gmuid);
+int persona_get_groups(struct persona *persona, int *ngroups, gid_t *groups, int groups_sz);
+
+uid_t persona_get_gmuid(struct persona *persona);
+
+int persona_get_login(struct persona *persona, char login[MAXLOGNAME+1]);
+
+/* returns a reference that must be released with persona_put() */
+struct persona *persona_proc_get(pid_t pid);
+
+#else /* !CONFIG_PERSONAS */
+
+static inline int proc_has_persona(__unused proc_t p)
+{
+       return 0;
+}
+
+static inline uid_t persona_id_from_proc(__unused proc_t p)
+{
+       return PERSONA_ID_NONE;
+}
+
+#endif /* CONFIG_PERSONAS */
+#endif /* XNU_KERNEL_PRIVATE */
+__END_DECLS
+
+#endif /* KERNEL_PRIVATE */
+
+#endif /* PRIVATE */
+#endif /* _SYS_PERSONA_H_ */
index 4d3b2cbd3c0457fcce929d4cc5f279213eb3906f..e8b0cc6c3630fb0c153f189ed09d6bf3b81da919 100644 (file)
@@ -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 *);
 
 /*! 
index f5ad0fcf4286bfc044a713051a33cf8a0180b9af..5e7a3750a71fd445d64814414c4d085207720370 100644 (file)
@@ -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 */
 
index 5f51c9952983e92d547e58c02b3c452e62aed493..cd48788121ad4c63702788dd35d019c647086915 100644 (file)
@@ -65,6 +65,8 @@
 #define _SYS_REBOOT_H_
 
 #include <sys/appleapiopts.h>
+#include <sys/cdefs.h>
+#include <stdint.h>
 
 /*
  * Arguments to reboot system call.
 #define RB_QUICK       0x400   /* quick and ungraceful reboot with file system caches flushed*/
 #define RB_PANIC       0x800   /* panic the kernel */
 
+#ifndef KERNEL
+__BEGIN_DECLS
+/* userspace reboot control */
+int usrctl(uint32_t flags);
+__END_DECLS
+#endif
+
 #endif /* __APPLE_API_PRIVATE */
 
 #ifdef __APPLE_API_OBSOLETE
index 8afe6c4daa073bcb7c698b965d6045a28289372d..8c577ce7cc0d01f1917c71caa313baf0235dbfae 100644 (file)
@@ -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;
index e0b810b0c10bfdafebb0827ea598d1d221b90d42..f83ffc529c9acde5768d3096c684061ab613d83f 100644 (file)
@@ -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 *);
index e794747db6ab98917fc1efc0903ba7b480211c60..e86e6c2fdd7c8582a54f39da1b321717c8daf4cc 100644 (file)
@@ -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;
 };
 
 
index f5b04763a43ffd7c26010b8694dc53959834f8af..d3a87d04984a8c87ee011b67e6f7314251bcb27b 100644 (file)
@@ -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;
index e243a7a2f59f041c7ee1397e02d9c089e16a74dd..bc8b67337798ea9611f3fc95ccb8d13b564e3454 100644 (file)
@@ -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);
index 36b1d24e6c88b01ab3ff35949855980cb2d05c31..b91ca2e445742f7aea211f4f46cb4b3a49eb5c00 100644 (file)
@@ -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;
 
                /*
index 69e1a23ab956edd948006889bb54cacc9df8c52d..71acea8d144add235d52380b9d21d0528089fecd 100644 (file)
@@ -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, "<unassigned>", 0, 0, 0, NULL, NULL, 0, 0, 0, NULL, 0, NULL},
        {NULL, "<unassigned>", 0, 0, 0, NULL, NULL, 0, 0, 0, NULL, 0, NULL},
 };
index ca47a42da818d41a88d3c3e0a76be9ea0ff1c993..4f31d45e7b2f4ee977f786e0494ebdce8a0e6423 100644 (file)
@@ -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)) ==
index adea23a19e24851d3a2c976671d002c5873f4285..0ed08b06f35bdef62e59752932d7a88ce2053bb6 100644 (file)
 #include <sys/ubc_internal.h>
 #include <sys/disk.h>
 #include <sys/content_protection.h>
+#include <sys/priv.h>
 #include <machine/cons.h>
 #include <machine/limits.h>
 #include <miscfs/specfs/specdev.h>
 #include <pexpert/pexpert.h>
 #include <IOKit/IOBSD.h>
 
+#if ROUTEFS
+#include <miscfs/routefs/routefs.h>
+#endif /* ROUTEFS */
+
 #if CONFIG_MACF
 #include <security/mac.h>
 #include <security/mac_framework.h>
@@ -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;
index 099a70fb612edb91629ecec103ed899c3cf2d03b..73abe01daf8edd951362d3351620c668f004f438 100644 (file)
@@ -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@
  * 
 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, "");
index d612020b7a8a0358d31249c20430a0b07a01fcad..4e5023e9b312887b067d78eb3e62852b7c044620 100644 (file)
@@ -137,6 +137,7 @@ options             MOCKFS          # Boot from an executable       # <mockfs>
 options                FIFO            # fifo support                  # <fifo>
 options                FDESC           # fdesc_fs support              # <fdesc>
 options                DEVFS           # devfs support                 # <devfs>
+options                ROUTEFS         # routefs support               # <routefs>
 options                JOURNALING      # journaling support            # <journaling>
 options                HFS_COMPRESSION # hfs compression           # <hfs_compression>
 options                CONFIG_HFS_STD  # hfs standard support      # <config_hfs_std>
@@ -486,6 +487,12 @@ options         CONFIG_STATIC_CPPINIT   # Static library initializes kext cpp ru
 #
 options                CONFIG_KEXT_BASEMENT            #       # <config_kext_basement>
 
+#
+# Persona Management
+#
+options                CONFIG_PERSONAS     # Persona management    # <config_personas>
+options                PERSONA_DEBUG       # Persona debugging     # <persona_debug>
+
 #
 # security configuration options
 #
index 3baa999632e24a676c91fbfe9f35512c15d996b0..3baf698bca9fc3b0b836c18c556f40e2e7441fab 100644 (file)
 #  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 ]
 #
 ######################################################################
 #
index 6e5b5944bdb5eb8452c37f2a8d36b25f8ee6b35e..1d3e768147d34ee96a21a871a1933c6e1e5baf00 100644 (file)
@@ -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.
index e04f8d26b834fa114b3eab349bf8f5b0eb1189e1..ea91129b6486c6fc06a8aa145bc72da40c6e70b9 100644 (file)
@@ -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
index 35ecccdf54a2778398df90ab61bce7744352cb22..ec3e604060edb08db8ea4108cb41e3374d8f4c4a 100644 (file)
@@ -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*
index 061c64ae3fcd3a48358f6b039100d36cee0dba03..3c294ff2277ce47735521a0d80cfd2f0375a5c28 100644 (file)
@@ -110,4 +110,6 @@ enum {
     kIOClassNameOverrideNone = 0x00000001,
 };
 
+#define kIOServiceLegacyMatchingRegistryIDKey "IOServiceLegacyMatchingRegistryID"
+
 #endif /* ! _IOKIT_IOKITKEYSPRIVATE_H */
index 584484eabd7c521b3fe42574cf692603ce99ba13..bec682028f9fe74ad64b08ae3891ca8ecafe8835 100644 (file)
 
 enum
 {
-    kIOPolledPreflightState   = 1,
-    kIOPolledBeforeSleepState = 2,
-    kIOPolledAfterSleepState  = 3,
-    kIOPolledPostflightState  = 4,
+    kIOPolledPreflightState           = 1,
+    kIOPolledBeforeSleepState         = 2,
+    kIOPolledAfterSleepState          = 3,
+    kIOPolledPostflightState          = 4,
 
     kIOPolledPreflightCoreDumpState   = 5,
+    kIOPolledPostflightCoreDumpState  = 6,
 };
 
 #if defined(__cplusplus)
index e369da9b4d5f63bb0e121a65008b0f34956194ae..d99ceba5bd16cba11eaaa4029fe01df160ad6501 100644 (file)
@@ -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, 
index 06a9931dbe750d178688c2833ea19114fbc28135..d6a149317b94284bb1e8eff14bda5e7ec3f5024e 100644 (file)
@@ -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,
index 47a17b5a110a396aa9a1fb5ae2723c8d80312e36..a6e41b648b5c5d3fc97df48879c97374db65a861 100644 (file)
@@ -35,6 +35,7 @@
 extern "C" {
 #include <machine/machine_routines.h>
 #include <pexpert/pexpert.h>
+#include <kern/cpu_number.h>
 }
 
 #include <machine/machine_routines.h>
@@ -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);
 }
 
index 965670f373a9bbe132264421a7b64aef93efd1c4..ce4a65ef70d3b678c918509141ba592a78775386 100644 (file)
@@ -37,6 +37,8 @@
 
 #include <pexpert/device_tree.h>
 
+typedef UInt32  dtptr_t;
+
 #include <machine/machine_routines.h>
 
 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]);
index f61b0f9d60e8e29ff3cf9cfb1fd6252c9c9aca14..0cff9ef1de4bf7839e59077c988faba8f841f930 100644 (file)
@@ -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;
index 2d6c7b79fa3d3f5e4b00ca1286b495ab75091792..61ec8bf5f02d19f1fb2fe1347a354d1ad41028ec 100644 (file)
@@ -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);
index 1917714fcfe0e8906dd7660ec746999f0905120c..09a1dd2fb8cf26edc31492a023389e12e75ae3cc 100644 (file)
@@ -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;
index 6ff5f289d8f1ea70fc85f7801a0fd8e7ebf8b25b..189f008fab66d24469a5b45b4bdbc550c89b0c46 100644 (file)
@@ -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);
index 06e4a612c2220a43946e1820b11265be1e5ef6f9..1faa211e2af7c2f2a5819536e5e43efa8257f645 100644 (file)
@@ -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);
index 3e45ff1ff7e7ad049ee14c5b99a54d36df11fbf8..6e67a3d892ddf73cec0f8c05d67cd90cc589c99e 100644 (file)
@@ -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);
 }
 
index 806567fc49f6be6b70451f76ea91dd95e3759033..affdcb336b6b5e54309cc21d3c3a167c8fa7386e 100644 (file)
@@ -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 (file)
index 0000000..88789bb
--- /dev/null
@@ -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 (file)
index 0000000..2b6034c
--- /dev/null
@@ -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 <sys/cdefs.h>
+
+/* compile-time assertion that 'x' and 'y' are equivalent types */
+#define __OS_TYPE_CHECK(x, y) do { \
+       _Static_assert(__builtin_types_compatible_p(typeof(x),typeof(y)), \
+                       "overflow arithmetic: incompatible types"); \
+} while (0)
+
+#define __os_add_overflow_func(T,U,V) _Generic((T), \
+               unsigned:           __builtin_uadd_overflow, \
+               unsigned long:      __builtin_uaddl_overflow, \
+               unsigned long long: __builtin_uaddll_overflow, \
+               int:                __builtin_sadd_overflow, \
+               long:               __builtin_saddl_overflow, \
+               long long:          __builtin_saddll_overflow \
+       )(T,U,V)
+
+#define __os_sub_overflow_func(T,U,V) _Generic((T), \
+               unsigned:           __builtin_usub_overflow, \
+               unsigned long:      __builtin_usubl_overflow, \
+               unsigned long long: __builtin_usubll_overflow, \
+               int:                __builtin_ssub_overflow, \
+               long:               __builtin_ssubl_overflow, \
+               long long:          __builtin_ssubll_overflow \
+       )(T,U,V)
+
+#define __os_mul_overflow_func(T,U,V) _Generic((T), \
+               unsigned:           __builtin_umul_overflow, \
+               unsigned long:      __builtin_umull_overflow, \
+               unsigned long long: __builtin_umulll_overflow, \
+               int:                __builtin_smul_overflow, \
+               long:               __builtin_smull_overflow, \
+               long long:          __builtin_smulll_overflow \
+       )(T,U,V)
+
+int __header_always_inline __attribute__((__warn_unused_result__))
+__os_warn_unused(const int x)
+{
+       return x;
+}
+
+#define os_add_overflow(a, b, res) __os_warn_unused(({ \
+       __OS_TYPE_CHECK((a), (b)); \
+       __OS_TYPE_CHECK((b), *(res)); \
+       __os_add_overflow_func((a), (b), (res)); \
+}))
+
+#define os_add3_overflow(a, b, c, res) __os_warn_unused(({ \
+       typeof(a) _tmp; \
+       int _s, _t; \
+       _s = os_add_overflow((a), (b), &_tmp); \
+       _t = os_add_overflow((c), _tmp, (res)); \
+       _s | _t; \
+}))
+
+#define os_sub_overflow(a, b, res) __os_warn_unused(({ \
+       __OS_TYPE_CHECK((a), (b)); \
+       __OS_TYPE_CHECK((b), *(res)); \
+       __os_sub_overflow_func((a), (b), (res)); \
+}))
+
+#define os_mul_overflow(a, b, res) __os_warn_unused(({ \
+       __OS_TYPE_CHECK((a), (b)); \
+       __OS_TYPE_CHECK((b), *(res)); \
+       __os_mul_overflow_func((a), (b), (res)); \
+}))
+
+#endif /* _OS_OVERFLOW_H */
index 10371199fa4636cdf985ff9813c47d829af442ea..bdf83643f4d30b1c14f82cb73a41704bc40df3e9 100644 (file)
                29A59AE6183B110C00E8B896 /* unlinkat.c in Sources */ = {isa = PBXBuildFile; fileRef = 29A59AE5183B110C00E8B896 /* unlinkat.c */; };
                2BA88DCC1810A3CE00EB63F6 /* coalition.c in Sources */ = {isa = PBXBuildFile; fileRef = 2BA88DCB1810A3CE00EB63F6 /* coalition.c */; };
                374A36E314748F1300AAF39D /* varargs_wrappers.s in Sources */ = {isa = PBXBuildFile; fileRef = 374A36E214748EE400AAF39D /* varargs_wrappers.s */; };
+               3F538F891A659C5600B37EFD /* persona.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F538F881A659C5600B37EFD /* persona.c */; };
                435F3CAA1B06B7BA005ED9EF /* work_interval.c in Sources */ = {isa = PBXBuildFile; fileRef = 435F3CA91B06B7BA005ED9EF /* work_interval.c */; };
                467DAFD4157E8AF200CE68F0 /* guarded_open_np.c in Sources */ = {isa = PBXBuildFile; fileRef = 467DAFD3157E8AF200CE68F0 /* guarded_open_np.c */; };
                4BDD5F1D1891AB2F004BF300 /* mach_approximate_time.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */; };
                2BA88DCB1810A3CE00EB63F6 /* coalition.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coalition.c; sourceTree = "<group>"; };
                374A36E214748EE400AAF39D /* varargs_wrappers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = varargs_wrappers.s; sourceTree = "<group>"; };
                37DDFB7614748713009D3355 /* syscall.map */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = syscall.map; sourceTree = "<group>"; };
+               3F538F881A659C5600B37EFD /* persona.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = persona.c; sourceTree = "<group>"; };
                435F3CA91B06B7BA005ED9EF /* work_interval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = work_interval.c; sourceTree = "<group>"; };
                467DAFD3157E8AF200CE68F0 /* guarded_open_np.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = guarded_open_np.c; sourceTree = "<group>"; };
                4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mach_approximate_time.c; sourceTree = "<group>"; };
                                4BDD5F1B1891AB2F004BF300 /* mach_approximate_time.c */,
                                4BDD5F1C1891AB2F004BF300 /* mach_approximate_time.s */,
                                030B179A135377B400DAD1F0 /* open_dprotected_np.c */,
+                               3F538F881A659C5600B37EFD /* persona.c */,
                                C6BEE9171806840200D25AAB /* posix_sem_obsolete.c */,
                                24B8C2611237F53900D36CC3 /* remove-counter.c */,
                                248AA966122C7CDA0085F5B1 /* rename.c */,
                                291D3C291354FDD100D46061 /* mach_vm.c in Sources */,
                                EE3F605A149A6D66003BAEBA /* getaudit.c in Sources */,
                                C9B6A5ED153795DE00749EBA /* alloc_once.c in Sources */,
+                               3F538F891A659C5600B37EFD /* persona.c in Sources */,
                                467DAFD4157E8AF200CE68F0 /* guarded_open_np.c in Sources */,
                                729B7D0A15C8938C000E2501 /* carbon_delete.c in Sources */,
                                A59CB9581666A1A200B064B3 /* munmap.c in Sources */,
index 33503843493ad31049281919231b48d63c661005..c6587951f02529fd3bcdf19515a6ed4fcb16b9c3 100644 (file)
@@ -26,6 +26,7 @@
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
+#include <TargetConditionals.h>
 #include <machine/cpu_capabilities.h>
 #include <mach/kern_return.h>
 #include <mach/mach_host.h>
@@ -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 (file)
index 0000000..e167cc3
--- /dev/null
@@ -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 <sys/cdefs.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <sys/persona.h>
+
+#include "strings.h"
+
+/* syscall entry point */
+int __persona(uint32_t operation, uint32_t flags, struct kpersona_info *info, uid_t *id, size_t *idlen);
+
+int kpersona_alloc(struct kpersona_info *info, uid_t *id)
+{
+       size_t idlen = 1;
+       return __persona(PERSONA_OP_ALLOC, 0, info, id, &idlen);
+}
+
+int kpersona_dealloc(uid_t id)
+{
+       size_t idlen = 1;
+       return __persona(PERSONA_OP_DEALLOC, 0, NULL, &id, &idlen);
+}
+
+int kpersona_get(uid_t *id)
+{
+       /* persona is a process-static identifier: cache it in a global */
+       static uid_t p_id = PERSONA_ID_NONE;
+       if (p_id == PERSONA_ID_NONE) {
+               int ret = 0;
+               size_t idlen = 1;
+               ret = __persona(PERSONA_OP_GET, 0, NULL, &p_id, &idlen);
+               if (ret != 0)
+                       return ret;
+       }
+       *id = p_id;
+       return 0;
+}
+
+int kpersona_info(uid_t id, struct kpersona_info *info)
+{
+       size_t idlen = 1;
+       return __persona(PERSONA_OP_INFO, 0, info, &id, &idlen);
+}
+
+int kpersona_pidinfo(pid_t pid, struct kpersona_info *info)
+{
+       size_t idlen = 1;
+       uid_t id = (uid_t)pid;
+       return __persona(PERSONA_OP_PIDINFO, 0, info, &id, &idlen);
+}
+
+int kpersona_find(const char *name, uid_t uid, uid_t *id, size_t *idlen)
+{
+       int ret;
+       struct kpersona_info kinfo;
+       kinfo.persona_info_version = PERSONA_INFO_V1;
+       kinfo.persona_id = uid;
+       kinfo.persona_type = 0;
+       kinfo.persona_gid = 0;
+       kinfo.persona_ngroups = 0;
+       kinfo.persona_groups[0] = 0;
+       kinfo.persona_name[0] = 0;
+       if (name)
+               strlcpy(kinfo.persona_name, name, sizeof(kinfo.persona_name));
+       ret = __persona(PERSONA_OP_FIND, 0, &kinfo, id, idlen);
+       if (ret < 0)
+               return ret;
+       return (int)(*idlen);
+}
index 88b6cabf3115709b66480963e5bd4c9941e9a35d..4c36d0e9016017f86ed0015ed5b2cef881cfa2cf 100644 (file)
@@ -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 =
index f98d2d2bd5152a70ea3485d4e628f080395299a3..3513946a43e10e0842a39953fb09022a31056ed2 100644 (file)
@@ -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_*/
index 27250311c5f0f291dc2dbd45fb2a5670499e7852..c39c53ccc48619d557b20e7b658eb6cf1ea80cd5 100644 (file)
@@ -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     \
index 6ce1a1e718db9ac25b4814a8ec4128f803283147..707a5be66e830fcd5e39dd46714b8a6fbfe3837d 100644 (file)
@@ -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;
index 03e862e9332db450e79dbfb67e42a2a6b8102f4c..875380934277b528ef3b5eeeb67ae5345240a34a 100644 (file)
@@ -40,7 +40,7 @@
 #include <kern/kalloc.h>
 #include <kern/ledger.h>
 #include <sys/kdebug.h>
-
+#include <IOKit/IOBSD.h>
 #include <mach/mach_voucher_attr_control.h>
 
 static zone_t bank_task_zone, bank_account_zone;
@@ -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);
        }
index 155f794ac2eb273b69baa614d77f4fd4783051bf..2f0d9353c7bf8c9bf700ec0d2edf58855fac4ff6 100644 (file)
@@ -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);
index c0ce5720f79c3a8a97580777552801f0180c0f3a..17ec0e15150beaeac01275e9441a911ffc4ce50c 100644 (file)
 #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_ */
index ae4830c80b24bf6f111332ed18958c8b8073ea04..90da364391128ae7512bfb563452e71b4ee06cb2 100644 (file)
@@ -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)
index 245a995fef671fa34f685120a410084ab0aa3d24..0d51045ef4a5071e3b1e607d4739dec9fa4979da 100644 (file)
@@ -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);
index 4ba6ed72df7efe130ed1941e8c22e10739f14b94..e9a1555a5edbdd49e02ccfa2ba784f5c6a9be05a 100644 (file)
@@ -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;
-}
 
 
index c4631540cabb6d702c6ea6208fa5f10c74728908..468dc9e61eecdb004eb5b3e9bf48919992b8f97d 100644 (file)
 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
 }
index 94c706eff7fa391e2688b1328c506afa7e894a56..4948c08a4fb25fac7dc48012f7a50fa504595863 100644 (file)
@@ -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 */
                ;
 
index af4991c497505b9690fe6a616319395484d473a8..fb25620554d9f3cc5a8aa009b55019ae80038dd2 100644 (file)
@@ -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 );
index 1412f4115a39788a028cad37ce7816e3f82bfc44..e855d1a0afe43fb4a3274da97b4651dbbae792d5 100644 (file)
@@ -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);
 
index 9bd836e2c29fb5587789a3b039f064ec3b772b53..19d39a9c899f066c4aa246068b507278c7248285 100644 (file)
@@ -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
 
        /*
index dc528cce12c9ad96a4231e83efc03b9b859f6f58..f6546ad34e95f726e356fce693ee0140354aca4f 100644 (file)
@@ -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
index 8d13b3645ba903750f3ff96a6c2419156feb7460..219948221d0fcad17dcf49e72cb009f8a1b58921 100644 (file)
@@ -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));
index 0b0b365806217e8e071058a2ccb36fc433183291..49b36f8468b46ac4b60f5ad1b627ccc3ffe857c9 100644 (file)
@@ -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 = &current_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 = &current_cpu_datap()->cpu_vmx;
+               cpu->specs.vmx_on = FALSE;
+       }
+
+       vmx_on(NULL);
 }
 
 /* -----------------------------------------------------------------------------
index 2abe13d50f5b9987999994a152ddaee27c61dd75..9ee53a53068ac7a44e8ca19a8758a6d7cc88333f 100644 (file)
@@ -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)
index bd48b1da7e51f5c54367cd5101ea72dbb206e5dc..089f6afd59cc2f2f44a6caa36c54fbbdce45232e 100644 (file)
@@ -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) {
index f5737416afa4c434aa8ea783fdbfff673de47e7f..5d78a18486dbb4e032c903d46ddde945a803e114 100644 (file)
@@ -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);
index 47e9bdf3a80bd651e04a7190eab7e64897bca9de..f914dec6f0e90339a9e2c8b0b377b4b05369c273 100644 (file)
@@ -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) {
 
index a83695fac26ec2a6ecc2a50d8f79582e2da941dc..bc8061e31783128f315d626319c467221ce4113b 100644 (file)
@@ -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 */
index 98282d7304f4506838bf4c791ccc550f35a26607..6c4e472dcb19534494314e95e805a11be7ae95c1 100644 (file)
 #include <ipc/ipc_space.h>
 #include <ipc/ipc_entry.h>
 #include <ipc/ipc_importance.h>
+#include <ipc/ipc_voucher.h>
 
 #include <machine/machine_routines.h>
 #include <security/mac_mach_internal.h>
@@ -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);
index f6750072fb496b939d51510bbc9d5e8cf857511a..c606f8147249050aa7c56bd434372ed64145f7aa 100644 (file)
@@ -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)) {
index 4c60056fbde755298b66df60136edb1643b68754..c5e2f45164a2aedb247b4ec7fd1bd2996cc7b2c2 100644 (file)
@@ -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
 
index a21666275328fdc78566db574d36d7b127e66ec7..fd2afa85800b83596fbdbde5d1129377d36c1c67 100644 (file)
@@ -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 */
index f1aa2eaf6e3ba00d3a0544ab70bf85ef35f5aaa5..dba639a8cb4e696344c50677d3cb48f18a1483a2 100644 (file)
@@ -77,6 +77,9 @@
 #include <mach/vm_map.h>
 #include <mach/task_info.h>
 
+#include <machine/commpage.h>
+#include <machine/cpu_capabilities.h>
+
 #include <kern/kern_types.h>
 #include <kern/assert.h>
 #include <kern/kalloc.h>
@@ -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);
+}
index 7239bdb299b234a1d5275456b91571cb191bc8c4..5dff6a1a2055f5df0971c40eb8356b6e02e77113 100644 (file)
@@ -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;
index fdc418c1dac168c731706147d633dbe84a49bf82..941bd183973d72484486e64b0e166ea5330d5395 100644 (file)
@@ -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;
 }
 
 /*
index 2f782cd124a016d2f090480372c05df1a26e74a1..c816715913b81fcb6cc47672d0c268eab09a7e6b 100644 (file)
@@ -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);
                }
        }
index dfe33564daf69530a432c5eaed8bbb2a31ff7678..f6b498fb468b782de1f12396bb5b3fac29a05fc1 100644 (file)
@@ -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);
 
index ffc92cb7ba80952332cfe6da7722b2b7cd19e012..f50696079972d9488990e1da585d074f740a71c5 100644 (file)
@@ -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);
index f2a1a8ba3efea646e187b064bb60e67809570eb4..799a31dc71c97e3dff3554cc01a08ec7bc35bb57 100644 (file)
@@ -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 {
index 1a46180a9d28946b74f7bd4d03d985664abbcbc5..d8f47015073cc25b45a5d0f4b1c9539d9c0066a4 100644 (file)
@@ -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;
 
index a23ef953fe8f00de0a40a1c2ffd42e4ca66a9169..411dfb47cb7128c79d6a39b868ff87e0c7bb9009 100644 (file)
@@ -71,7 +71,9 @@
 #if CONFIG_TELEMETRY
 #include <kern/telemetry.h>
 #endif
-       
+
+#include <sys/kdebug.h>
+
 uint32_t       avenrun[3] = {0, 0, 0};
 uint32_t       mach_factor[3] = {0, 0, 0};
 
@@ -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.
         */
index 5ee363dc7b114a44273ca6f1dc7f9c0c37f1fc7d..8c70db47d723c860f4a99f9b6a3fed308a92dbca 100644 (file)
@@ -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*/
index eada1fb64b249b9dc5530b30838cdc77cd682a62..ac6acbc04eab260fb183d0e2859b108c9c779d9f 100644 (file)
@@ -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();
 
index f0c067b03007c8652b42c863214ec4777e4dd7c0..1ebf39e4f9d4f405b1a9e9a0f41f3624b0595bb9 100644 (file)
@@ -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;
index d0e982ee42a0b03d8b95cb9cbfccbe3547b87138..7de13feab8ed3df4ae0f3c98a889abb36ef3bd4b 100644 (file)
@@ -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 <kern/btlog.h>
 
@@ -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);
index 5ddff0c754e76d457f68fa25d94642271c8ac5d4..9f1192a4aa1c4f1d3e869aec6800c43c615f00fe 100644 (file)
@@ -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
 
index 535d1e319972f16be4116ced05a026433164a0c6..64b5ea604379089ca96b7790ed19be8e870459ad 100644 (file)
@@ -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)
 {
index 07fc07fd3ab06827a903f9d6a56ce2347d6f16fb..bec1a510504f2d5fe221e8388d5cd6889318f8b1 100644 (file)
@@ -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 */
 
 
index 6c1b2e37959439466f37e3f0e791eeb8df68fc01..1d1376e8135e7441ae3e677df5bf8af4bab7844c 100644 (file)
@@ -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);
index 9a82a198fbd3e5b358e4ceb85946e98c8dddcd42..a7043c78bc6b14ae477a78068562f814cb5ef670 100644 (file)
@@ -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);
 
index bc44a2842f6103e2193633f3b79d001f790e0ef6..1e4bb3d39a0e7c02aeed9dfe3626efdcf02d508e 100644 (file)
@@ -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 : */
index 0f3d674bcf767be3a866034419c2439eee7668a3..0c5a4b5160c23823c4610585f2312cb1fa5fb59e 100644 (file)
@@ -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;
index 9c459178a7fa390d244a68cb15a0b7eecf5f35e2..09ea8bb8a9c174bb7ae332d95d3684facdf44f18 100644 (file)
@@ -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
index b3769d484398b876418cbc757db0e578f638c624..9b483dd5aa573a16e0cef7c969dfbd314c8a3844 100644 (file)
@@ -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 */
 
index 81301fd1102c610c5c2b9c655f841675f06e435f..91208ca11795b4ee92c0c61a81ca4c5db6a3e686 100644 (file)
@@ -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.
index f21599eb6dae74eaeca037358d74c3ba6bcb6d6b..33f4f7cc4f39245f1b60c9d6a48e41c316978dd2 100644 (file)
@@ -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);
index 655c302d25931f85e78370f29e89f2516e71957c..8a0d7b95fd0b7959f6de2d26b73e8641eee28257 100644 (file)
@@ -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;
index ac2edb6a5a3635e4cb59fab57314629c50602b75..773ab89fffa537b719f2f23861ae1c28eb699886 100644 (file)
@@ -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,
index 25c1edb266ec449eae17e8cb2f0579ba9956b958..2ad202d0ea1d62ad66988d4c5fc33e6317310bbd 100644 (file)
@@ -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);
index 45be582574e381f16b027ad6b772d5462cbb51d8..209e96335f4eb067585c1d99e332544916b6b40f 100644 (file)
@@ -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);
index 6874636aede086e779e5e046802e311b42c374e3..e54ee3487c06cf26b064be0c076f990b55cddc9e 100644 (file)
@@ -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}"
index d9c3745af92d27b94e1c0ffd03ad53ee07b82edd..f060873933704eb7d6b505dbacab49504a743c08 100644 (file)
@@ -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 *')
index 0686350f3a80649cc3cc029d0bd60ec7078a7ece..66f15b32002a64c4369a6469741020448d9f2c97 100644 (file)
@@ -4,6 +4,7 @@
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <libgen.h>
 #include <string.h>
 #include <err.h>
 #include <unistd.h>
@@ -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);
 
index 17b0a1acb10c0031186bfeebf589e3e3ab7b8836..7dc344fd43300822b9622a48e85c40185be35d9d 100644 (file)
@@ -4,6 +4,7 @@
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <libgen.h>
 #include <string.h>
 #include <err.h>
 #include <unistd.h>
@@ -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 (executable)
index 0000000..517c730
--- /dev/null
@@ -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;
+
+
index f5cb5de8bdb13be6db9d0eb019868530d84e89c1..d241564602fc8debfdc79797e5b64801e25c1f68 100644 (file)
@@ -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
index 70e7f02bfd44162091bd80410462954a230dd494..06b7e36f115d32204de836871a9537f8a88f68f7 100644 (file)
@@ -46,3 +46,11 @@ else
 endif
 
 DEPLOYMENT_TARGET_DEFINES = -DPLATFORM_$(PLATFORM)
+
+ifeq ($(RC_XBS),YES)
+_v =
+else ifeq ($(VERBOSE),YES)
+_v =
+else
+_v = @
+endif
index ebf4bcdbd5becbeb8b8bfa060617aee7f12a8f43..e67fe1313b0d5995c55ebefc611a44f3a41e84f8 100644 (file)
@@ -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
index c9a940bd20db9e5fa541ce0401f075e622dfc9c6..810fa9c133bce4660835579fd90f736444ef1a5d 100755 (executable)
@@ -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 (file)
index 0000000..f342174
--- /dev/null
@@ -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 (file)
index 0000000..f6776c8
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * persona_mgr.c
+ * Tool to manage personas
+ *
+ * Jeremy C. Andrus <jeremy_andrus@apple.com>
+ *
+ */
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/task.h>
+#include <mach/vm_param.h>
+#include <sys/kauth.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "persona_test.h"
+
+/* internal */
+#include <libproc.h>
+#include <spawn_private.h>
+#include <sys/persona.h>
+#include <sys/proc_info.h>
+#include <sys/spawn_internal.h>
+
+#define PROG_NAME   "Persona Manager"
+#define PROG_VMAJOR 0
+#define PROG_VMINOR 1
+
+enum {
+       PERSONA_OP_CREATE  = 1,
+       PERSONA_OP_DESTROY = 2,
+       PERSONA_OP_LOOKUP  = 3,
+       PERSONA_OP_MAX     = 3,
+};
+
+static struct mgr_config {
+       int verbose;
+} g;
+
+
+static int persona_op_create(struct kpersona_info *ki)
+{
+       int ret;
+       uid_t persona_id = 0;
+
+       info("Creating persona...");
+       ki->persona_info_version = PERSONA_INFO_V1;
+       ret = kpersona_alloc(ki, &persona_id);
+       if (ret == 0) {
+               info("Created persona %d:", persona_id);
+               dump_kpersona(NULL, ki);
+       } else {
+               err("kpersona_alloc return %d (errno:%d)", ret, errno);
+       }
+
+       return ret;
+}
+
+static int persona_op_destroy(struct kpersona_info *ki)
+{
+       int ret;
+
+       info("Destroying Persona %d...", ki->persona_id);
+       ki->persona_info_version = PERSONA_INFO_V1;
+       ret = kpersona_dealloc(ki->persona_id);
+       if (ret < 0)
+               err_print("destroy failed!");
+
+       return ret;
+}
+
+static int persona_op_lookup(struct kpersona_info *ki, pid_t pid, uid_t uid)
+{
+       int ret;
+
+       info("Looking up persona (pid:%d, uid:%d)", pid, uid);
+       if (pid > 0) {
+               ki->persona_info_version = PERSONA_INFO_V1;
+               ret = kpersona_pidinfo(pid, ki);
+               if (ret < 0)
+                       err_print("pidinfo failed!");
+               else
+                       dump_kpersona("Persona-for-pid:", ki);
+       } else {
+               int np = 0;
+               uid_t personas[128];
+               size_t npersonas = ARRAY_SZ(personas);
+               const char *name = NULL;
+               if (ki->persona_name[0] != 0)
+                       name = ki->persona_name;
+
+               np = kpersona_find(name, uid, personas, &npersonas);
+               if (np < 0)
+                       err("kpersona_find returned %d (errno:%d)", np, errno);
+               info("Found %zu persona%c", npersonas, npersonas != 1 ? 's' : ' ');
+               np = npersonas;
+               while (np--) {
+                       info("\tpersona[%d]=%d...", np, personas[np]);
+                       ki->persona_info_version = PERSONA_INFO_V1;
+                       ret = kpersona_info(personas[np], ki);
+                       if (ret < 0)
+                               err("kpersona_info failed (errno:%d) for persona[%d]", errno, personas[np]);
+                       dump_kpersona(NULL, ki);
+               }
+       }
+
+       return ret;
+}
+
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ *
+ * Main Entry Point
+ *
+ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ */
+static void usage_main(const char *progname, const char *msg, int verbose)
+{
+       const char *nm = basename((char *)progname);
+
+       if (msg)
+               printf("%s\n\n", msg);
+
+       printf("%s v%d.%d\n", PROG_NAME, PROG_VMAJOR, PROG_VMINOR);
+       printf("usage: %s [op] [-v] [-i id] [-t type] [-p pid] [-u uid] [-g gid] [-l login] [-G {groupspec}] [-m gmuid]\n", nm);
+       if (!verbose)
+               exit(1);
+
+       printf("\t%-15s\tOne of: create | destroy | lookup\n", "[op]");
+       printf("\t%-15s\tBe verbose\n", "-v");
+
+       printf("\t%-15s\tID of the persona\n", "-i id");
+       printf("\t%-15s\tType of the persona\n", "-t type");
+       printf("\t%-15s\tPID of the process whose persona info to lookup\n", "-p pid");
+       printf("\t%-15s\tUID to use in lookup\n", "-u uid");
+       printf("\t%-15s\tGID of the persona\n", "-g gid");
+       printf("\t%-15s\tLogin name of the persona\n", "-l login");
+       printf("\t%-15s\tGroups to which the persona will belong\n", "-G {groupspec}");
+       printf("\t%-15s\tgroupspec: G1{,G2,G3...}\n", " ");
+       printf("\t%-15s\tUID used for memberd lookup (opt-in to memberd)\n", "-m gmuid");
+
+       printf("\n");
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+       char ch;
+       int ret;
+
+       const char *op_str = NULL;
+       int persona_op = 0;
+       struct kpersona_info kinfo;
+       uid_t uid = (uid_t)-1;
+       pid_t pid = (pid_t)-1;
+
+       /*
+        * Defaults
+        */
+       g.verbose = 0;
+
+       if (geteuid() != 0)
+               err("%s must be run as root", argv[0] ? basename(argv[0]) : PROG_NAME);
+
+       if (argc < 2)
+               usage_main(argv[0], "Not enough arguments", 0);
+
+       op_str = argv[1];
+
+       if (strcmp(op_str, "create") == 0)
+               persona_op = PERSONA_OP_CREATE;
+       else if (strcmp(op_str, "destroy") == 0)
+               persona_op = PERSONA_OP_DESTROY;
+       else if (strcmp(op_str, "lookup") == 0)
+               persona_op = PERSONA_OP_LOOKUP;
+       else if (strcmp(op_str, "help") == 0 || strcmp(op_str, "-h") == 0)
+               usage_main(argv[0], NULL, 1);
+
+       if (persona_op <= 0 || persona_op > PERSONA_OP_MAX)
+               usage_main(argv[0], "Invalid [op]", 0);
+
+       memset(&kinfo, 0, sizeof(kinfo));
+       kinfo.persona_gmuid = KAUTH_UID_NONE;
+
+       /*
+        * Argument parse
+        */
+       optind = 2;
+       while ((ch = getopt(argc, argv, "vi:t:p:u:g:l:G:m:h")) != -1) {
+               switch (ch) {
+               case 'i':
+                       ret = atoi(optarg);
+                       if (ret <= 0)
+                               err("Invalid Persona ID: %s", optarg);
+                       kinfo.persona_id = (uid_t)ret;
+                       break;
+               case 't':
+                       ret = atoi(optarg);
+                       if (ret <= PERSONA_INVALID || ret > PERSONA_TYPE_MAX)
+                               err("Invalid type specification: %s", optarg);
+                       kinfo.persona_type = ret;
+                       break;
+               case 'p':
+                       ret = atoi(optarg);
+                       if (ret <= 0)
+                               err("Invalid PID: %s", optarg);
+                       pid = (pid_t)ret;
+                       break;
+               case 'u':
+                       ret = atoi(optarg);
+                       if (ret <= 0)
+                               err("Invalid UID: %s", optarg);
+                       uid = (uid_t)ret;
+                       break;
+               case 'g':
+                       kinfo.persona_gid = (gid_t)atoi(optarg);
+                       if (kinfo.persona_gid <= 500)
+                               err("Invalid GID: %d", kinfo.persona_gid);
+                       break;
+               case 'l':
+                       strncpy(kinfo.persona_name, optarg, MAXLOGNAME);
+                       break;
+               case 'G':
+                       ret = parse_groupspec(&kinfo, optarg);
+                       if (ret < 0)
+                               err("Invalid groupspec: \"%s\"", optarg);
+                       break;
+               case 'm':
+                       ret = atoi(optarg);
+                       if (ret < 0)
+                               err("Invalid group membership ID: %s", optarg);
+                       kinfo.persona_gmuid = (uid_t)ret;
+                       break;
+               case 'v':
+                       g.verbose = 1;
+                       break;
+               case 'h':
+               case '?':
+                       usage_main(argv[0], NULL, 1);
+               case ':':
+               default:
+                       printf("Invalid option: '%c'\n", ch);
+                       usage_main(argv[0], NULL, 0);
+               }
+       }
+
+       if (uid == (uid_t)-1)
+               uid = kinfo.persona_id;
+
+       if (kinfo.persona_gmuid && kinfo.persona_ngroups == 0) {
+               /*
+                * In order to set the group membership UID, we need to set at
+                * least one group: make it equal to either the GID or UID
+                */
+               kinfo.persona_ngroups = 1;
+               if (kinfo.persona_gid)
+                       kinfo.persona_groups[0] = kinfo.persona_gid;
+               else
+                       kinfo.persona_groups[0] = kinfo.persona_id;
+       }
+
+       if (g.verbose)
+               dump_kpersona("Input persona:", &kinfo);
+
+       switch (persona_op) {
+       case PERSONA_OP_CREATE:
+               ret = persona_op_create(&kinfo);
+               break;
+       case PERSONA_OP_DESTROY:
+               ret = persona_op_destroy(&kinfo);
+               break;
+       case PERSONA_OP_LOOKUP:
+               ret = persona_op_lookup(&kinfo, pid, uid);
+               break;
+       default:
+               err("Invalid persona op: %d", persona_op);
+       }
+
+       return ret;
+}
diff --git a/tools/tests/personas/persona_spawn.c b/tools/tests/personas/persona_spawn.c
new file mode 100644 (file)
index 0000000..b6e7782
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * spawn_persona.c
+ * Use new POSIX spawn attributes to create a new process in a persona
+ *
+ * Jeremy C. Andrus <jeremy_andrus@apple.com>
+ *
+ */
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/task.h>
+#include <mach/vm_param.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "persona_test.h"
+
+/* internal */
+#include <libproc.h>
+#include <spawn_private.h>
+#include <sys/persona.h>
+#include <sys/proc_info.h>
+#include <sys/spawn_internal.h>
+
+#define PERSONA_TEST_NAME   "Persona Spawn"
+#define PERSONA_TEST_VMAJOR 0
+#define PERSONA_TEST_VMINOR 1
+
+static struct test_config {
+       int verbose;
+       int wait_for_children;
+} g;
+
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ *
+ * Child Management
+ *
+ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ */
+struct child {
+       TAILQ_ENTRY(child) sibling;
+       int pid;
+};
+
+static pthread_mutex_t g_child_mtx;
+static TAILQ_HEAD(, child) g_children = TAILQ_HEAD_INITIALIZER(g_children);
+static int g_nchildren = 0;
+
+static pid_t spawn_child(int argc, char **argv, struct persona_args *pa)
+{
+       int ret;
+       uint32_t persona_flags = 0;
+       posix_spawnattr_t attr;
+       struct child *child = NULL;
+       extern char **environ;
+
+       (void)argc;
+
+       if (!pa) {
+               err_print("Invalid persona args!");
+               return -ERR_SYSTEM;
+       }
+
+       if (!pa->flags & PA_HAS_ID) {
+               err_print("No persona ID specified!");
+               return -ERR_SYSTEM;
+       }
+
+       if (g.verbose) {
+               dump_persona_args("Spawning new child with args: ", pa);
+               infov("\t prog: \"%s\"", argv[0]);
+               for (int i = 1; i < argc; i++) {
+                       infov("\t arg[%d]: %s", i, argv[i]);
+               }
+       }
+
+       child = (struct child *)calloc(1, sizeof(*child));
+       if (!child) {
+               err_print("No memory left :-(");
+               return -ERR_SYSTEM;
+       }
+
+       ret = posix_spawnattr_init(&attr);
+       if (ret != 0) {
+               err_print("posix_spawnattr_init");
+               ret = -ERR_SPAWN_ATTR;
+               goto out_err;
+       }
+
+       if (pa->flags & PA_SHOULD_VERIFY)
+               persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_VERIFY;
+
+       if (pa->flags & PA_OVERRIDE)
+               persona_flags |= POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE;
+
+       ret = posix_spawnattr_set_persona_np(&attr, pa->kinfo.persona_id, persona_flags);
+       if (ret != 0) {
+               err_print("posix_spawnattr_set_persona_np failed!");
+               ret = -ERR_SPAWN_ATTR;
+               goto out_err;
+       }
+
+       if (pa->flags & PA_HAS_UID) {
+               ret = posix_spawnattr_set_persona_uid_np(&attr, pa->override_uid);
+               if (ret != 0) {
+                       err_print("posix_spawnattr_set_persona_uid_np failed!");
+                       ret = -ERR_SPAWN_ATTR;
+                       goto out_err;
+               }
+       }
+
+       if (pa->flags & PA_HAS_GID) {
+               ret = posix_spawnattr_set_persona_gid_np(&attr, pa->kinfo.persona_gid);
+               if (ret != 0) {
+                       err_print("posix_spawnattr_set_persona_gid_np failed!");
+                       ret = -ERR_SPAWN_ATTR;
+                       goto out_err;
+               }
+       }
+
+       ret = posix_spawn(&child->pid, argv[0], NULL, &attr, argv, environ);
+       if (ret != 0) {
+               err_print("posix_spawn (ret=%d)", ret);
+               ret = -ERR_SPAWN;
+               goto out_err;
+       }
+
+       infov("\tspawned child PID: %d", child->pid);
+
+       /* link the processes onto the global children list */
+       pthread_mutex_lock(&g_child_mtx);
+       TAILQ_INSERT_TAIL(&g_children, child, sibling);
+       ++g_nchildren;
+       pthread_mutex_unlock(&g_child_mtx);
+
+       posix_spawnattr_destroy(&attr);
+       return child->pid;
+
+out_err:
+       posix_spawnattr_destroy(&attr);
+       free(child);
+       return (pid_t)ret;
+}
+
+
+static int child_should_exit = 0;
+
+static void child_sighandler(int sig)
+{
+       (void)sig;
+       dbg("PID: %d received sig %d", getpid(), sig);
+       child_should_exit = 1;
+}
+
+static int child_main_loop(int argc, char **argv)
+{
+       char ch;
+       sigset_t sigset;
+       int err = 0;
+       uid_t persona_id = 0;
+       struct kpersona_info kinfo;
+       int rval = 0;
+
+       while ((ch = getopt(argc, argv, "vhER")) != -1) {
+               switch (ch) {
+               case 'v':
+                       g.verbose = 1;
+                       break;
+               case 'E':
+                       child_should_exit = 1;
+                       break;
+               case 'R':
+                       rval = 1;
+                       break;
+               case 'h':
+               case '?':
+               case ':':
+               default:
+                       err("Invalid child process invocation.");
+               }
+       }
+
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGINT);
+       sigaddset(&sigset, SIGHUP);
+       sigaddset(&sigset, SIGTERM);
+       sigaddset(&sigset, SIGABRT);
+       sigaddset(&sigset, SIGCHLD);
+       sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+
+       signal(SIGINT, child_sighandler);
+       signal(SIGHUP, child_sighandler);
+       signal(SIGTERM, child_sighandler);
+       signal(SIGABRT, child_sighandler);
+       signal(SIGCHLD, child_sighandler);
+
+       err = kpersona_get(&persona_id);
+
+       info("Child: PID:%d", getpid());
+       info("Child: UID:%d, GID:%d", getuid(), getgid());
+       info("Child: login:%s", getlogin());
+       info("Child: Persona: %d (err:%d)", persona_id, err);
+
+       kinfo.persona_info_version = PERSONA_INFO_V1;
+       err = kpersona_info(persona_id, &kinfo);
+       if (err == 0)
+               dump_kpersona("Child: kpersona_info", &kinfo);
+       else
+               info("Child: ERROR grabbing kpersona_info: %d", errno);
+
+       if (child_should_exit)
+               return rval;
+
+       infov("Child Sleeping!");
+       while (!child_should_exit)
+               sleep(1);
+
+       infov("Child exiting!");
+       return rval;
+}
+
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ *
+ * Main Entry Point
+ *
+ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ */
+static void main_sighandler(int sig)
+{
+       dbg("PID: %d received sig %d", getpid(), sig);
+       if (sig == SIGCHLD) {
+               --g_nchildren;
+       }
+}
+
+static void usage_main(const char *progname, int verbose)
+{
+       const char *nm = basename((char *)progname);
+
+       printf("%s v%d.%d\n", PERSONA_TEST_NAME, PERSONA_TEST_VMAJOR, PERSONA_TEST_VMINOR);
+       printf("usage: %s [-I id] [-V] [-u uid] [-g gid] [-vw] progname [args...]\n", nm);
+       printf("       Spawn a new process into a new or existing persona.\n");
+       if (!verbose)
+               exit(1);
+
+       printf("\t%-10s\tID of the persona\n", "-I id");
+       printf("\t%-10s\tVerify persona parameters against existing persona (given by -I)\n", "-V");
+       printf("\t%-10s\tOverride/verify the user ID of the new process\n", "-u uid");
+       printf("\t%-10s\tOverride/verify the group ID of the new process\n", "-g gid");
+       printf("\t%-10s\tBe verbose\n", "-v");
+       printf("\t%-10s\tDo not wait for the child process\n", "-w");
+       printf("\n");
+
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+       char ch;
+       int ret;
+
+       pthread_mutex_init(&g_child_mtx, NULL);
+
+       /*
+        * Defaults
+        */
+       g.verbose = 0;
+       g.wait_for_children = 1;
+
+       if (argc > 1 && strcmp(argv[1], "child") == 0) {
+               optind = 2;
+               ret = child_main_loop(argc, argv);
+               if (ret != 1)
+                       exit(0);
+
+               /*
+                * If we get here, then the child wants us to continue running
+                * to potentially spawn yet another child process. This is
+                * helpful when testing inherited personas and verifying
+                * persona restrictions.
+                */
+       }
+
+       if (geteuid() != 0)
+               err("%s must be run as root", argv[0] ? basename(argv[0]) : PERSONA_TEST_NAME);
+
+       struct persona_args pa;
+       memset(&pa, 0, sizeof(pa));
+
+       pa.flags = PA_NONE;
+       pa.kinfo.persona_id = getuid();
+
+       /*
+        * Argument parse for default overrides:
+        */
+       while ((ch = getopt(argc, argv, "Vg:I:u:vwh")) != -1) {
+               switch (ch) {
+               case 'V':
+                       pa.flags |= PA_SHOULD_VERIFY;
+                       break;
+               case 'g':
+                       pa.kinfo.persona_gid = atoi(optarg);
+                       if (pa.kinfo.persona_gid <= 500)
+                               err("Invalid GID: %d", pa.kinfo.persona_gid);
+                       pa.flags |= PA_HAS_GID;
+                       pa.flags |= PA_OVERRIDE;
+                       break;
+               case 'I':
+                       pa.kinfo.persona_id = atoi(optarg);
+                       if (pa.kinfo.persona_id == 0)
+                               err("Invalid Persona ID: %s", optarg);
+                       pa.flags |= PA_HAS_ID;
+                       break;
+               case 'u':
+                       pa.override_uid = atoi(optarg);
+                       if (pa.override_uid <= 500)
+                               err("Invalid UID: %d", pa.override_uid);
+                       pa.flags |= PA_HAS_UID;
+                       pa.flags |= PA_OVERRIDE;
+                       break;
+               case 'v':
+                       g.verbose = 1;
+                       break;
+               case 'w':
+                       g.wait_for_children = 0;
+                       break;
+               case 'h':
+               case '?':
+                       usage_main(argv[0], 1);
+               case ':':
+               default:
+                       printf("Invalid option: '%c'\n", ch);
+                       usage_main(argv[0], 0);
+               }
+       }
+
+       if (pa.flags & PA_SHOULD_VERIFY)
+               pa.flags = ~PA_OVERRIDE;
+
+       if (optind >= argc) {
+               printf("No program given!\n");
+               usage_main(argv[0], 0);
+       }
+
+       argc -= optind;
+       for (int i = 0; i < argc; i++) {
+               argv[i] = argv[i + optind];
+       }
+
+       argv[argc] = NULL;
+
+       ret = spawn_child(argc, argv, &pa);
+       if (ret < 0)
+               return ret;
+
+       pid_t child_pid = (pid_t)ret;
+       int status = 0;
+       sigset_t sigset;
+       sigemptyset(&sigset);
+       sigaddset(&sigset, SIGCHLD);
+       sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+       signal(SIGCHLD, main_sighandler);
+
+       if (g.wait_for_children) {
+               infov("Waiting for child...");
+               waitpid(child_pid, &status, 0);
+               if (WIFEXITED(status)) {
+                       status = WEXITSTATUS(status);
+                       if (status != 0)
+                               errc(ERR_CHILD_FAIL,
+                                    "Child exited with status: %d", status);
+               }
+       }
+
+       info("Done.");
+       return 0;
+}
diff --git a/tools/tests/personas/persona_test.h b/tools/tests/personas/persona_test.h
new file mode 100644 (file)
index 0000000..e88d08a
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * persona_test.h
+ *
+ * Jeremy C. Andrus <jeremy_andrus@apple.com>
+ *
+ */
+#ifndef _PERSONA_TEST_H_
+#define _PERSONA_TEST_H_
+
+/* internal */
+#include <spawn_private.h>
+#include <sys/persona.h>
+#include <sys/spawn_internal.h>
+
+//#define DEBUG
+
+enum {
+       PA_NONE          = 0x0000,
+       PA_CREATE        = 0x0001,
+       PA_SHOULD_VERIFY = 0x0002,
+       PA_OVERRIDE      = 0x0004,
+       PA_HAS_ID        = 0x0100,
+       PA_HAS_TYPE      = 0x0200,
+       PA_HAS_UID       = 0x0400,
+       PA_HAS_GID       = 0x0800,
+       PA_HAS_GROUPS    = 0x1000,
+       PA_HAS_LOGIN     = 0x2000,
+};
+
+struct persona_args {
+       uint32_t flags;
+       uid_t  override_uid;
+       struct kpersona_info kinfo;
+};
+
+
+/*
+ * Error codes emitted on failure
+ */
+#define ERR_SYSTEM          -1
+#define ERR_SPAWN            30
+#define ERR_SPAWN_ATTR       31
+#define ERR_CHILD_FAIL       40
+#define ERR_ARGS             98
+#define ERR_SETUP            99
+
+#define err(fmt, ...) \
+       do { \
+               fflush(NULL); \
+               fprintf(stderr, "[%4d] [ERROR(%d:%s)] %s:%d: " fmt "\n", \
+                       getuid(), errno, strerror(errno), \
+                       __func__, __LINE__, ## __VA_ARGS__ ); \
+               fflush(stderr); \
+               exit(ERR_SYSTEM); \
+       } while (0)
+
+#define errc(code, fmt, ...) \
+       do { \
+               fflush(NULL); \
+               fprintf(stderr, "[%4d] [ERROR(%d)] %s:%d: " fmt "\n", \
+                       getuid(), code, \
+                       __func__, __LINE__, ## __VA_ARGS__ ); \
+               fflush(stderr); \
+               exit(code ? code : ERR_SYSTEM); \
+       } while (0)
+
+#define err_print(fmt, ...) \
+       do { \
+               fflush(NULL); \
+               fprintf(stderr, "[%4d] [ERROR(%d:%s)] %s:%d: " fmt "\n", \
+                       getuid(), errno, strerror(errno), \
+                       __func__, __LINE__, ## __VA_ARGS__ ); \
+               fflush(stderr); \
+       } while (0)
+
+
+#define err__start(fmt, ...) \
+       do { \
+               fprintf(stderr, "[%4d] [ERROR] " fmt, getuid(), ## __VA_ARGS__); \
+               fflush(stderr); \
+       } while (0)
+
+#define err__cont(fmt, ...) \
+       do { \
+               fprintf(stderr, fmt, ## __VA_ARGS__); \
+               fflush(stderr); \
+       } while (0)
+
+#define err__finish(fmt, ...) \
+       do { \
+               fprintf(stderr, fmt "\n", ## __VA_ARGS__); \
+               fflush(stderr); \
+       } while (0)
+
+
+#ifdef DEBUG
+#define dbg(fmt, ...) \
+       do { \
+               fprintf(stdout, "[%4d] [DEBUG] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+               fflush(NULL); \
+       } while (0)
+#define warn(fmt, ...) \
+       do { \
+               fprintf(stdout, "[%4d] [WARN ] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+               fflush(NULL); \
+       } while (0)
+#else
+#define dbg(...)
+#define warn(...)
+#endif
+
+#define info(fmt, ...) \
+       do { \
+               fprintf(stdout, "[%4d] [INFO ] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+               fflush(NULL); \
+       } while (0)
+
+#define info_start(fmt, ...) \
+       do { \
+               fprintf(stdout, "[%4d] [INFO ] " fmt, getuid(), ## __VA_ARGS__ ); \
+       } while (0)
+
+#define info_cont(fmt, ...) \
+       do { \
+               fprintf(stdout, fmt, ## __VA_ARGS__ ); \
+       } while (0)
+
+#define info_end() \
+       do { \
+               fprintf(stdout, "\n"); \
+               fflush(NULL); \
+       } while (0)
+
+#define infov(fmt, ...) \
+       if (g.verbose) { \
+               fprintf(stdout, "[%4d] [vINFO] " fmt "\n", getuid(), ## __VA_ARGS__ ); \
+               fflush(NULL); \
+       }
+
+#define ARRAY_SZ(a) \
+       (sizeof(a) / sizeof((a)[0]))
+
+
+static inline void _dump_kpersona(const char *msg, uint32_t flags, const struct kpersona_info *ki)
+{
+       if (msg)
+               info("%s", msg);
+       info("\t kpersona_info (v%d) {", ki->persona_info_version);
+       info("\t\t     %cid:  %d", flags & PA_HAS_ID ? '+' : '-', ki->persona_id);
+       info("\t\t     %ctype:  %d", flags & PA_HAS_TYPE ? '+' : '-', ki->persona_type);
+       info("\t\t    %cgid:  %d", flags & PA_HAS_GID ? '+' : '-', ki->persona_gid);
+
+       info_start("\t\t  ngroups:  %d", ki->persona_ngroups);
+       for (int i = 0; i < ki->persona_ngroups; i++) {
+               if (i == 0) info_cont(" {");
+               info_cont(" %d", ki->persona_groups[i]);
+       }
+       if (ki->persona_ngroups > 0)
+               info_cont(" }");
+       info_end();
+
+       info("\t\t  %cgmuid: %d (0x%x)", flags & PA_HAS_GROUPS ? '+' : '-',
+            (int)ki->persona_gmuid, ki->persona_gmuid);
+       info("\t\t  %clogin: \"%s\"", flags & PA_HAS_LOGIN ? '+' : '-', ki->persona_name);
+       info("\t }");
+}
+
+#define dump_kpersona(msg, ki) \
+       _dump_kpersona(msg, 0xffffffff, ki)
+
+static inline void dump_persona_args(const char *msg, const struct persona_args *pa)
+{
+       const struct kpersona_info *ki = &pa->kinfo;
+
+       if (msg)
+               info("%s", msg);
+       info("\t flags: 0x%x", pa->flags);
+       info("\t %cuid: %d", pa->flags & PA_HAS_UID ? '+' : '-', pa->override_uid);
+       _dump_kpersona(NULL, pa->flags, ki);
+}
+
+static int parse_groupspec(struct kpersona_info *kinfo, char *spec)
+{
+       int idx = 0;
+       int grp;
+       char *s, *e;
+
+       if (!spec)
+               return -1;
+       s = e = spec;
+       while (*s) {
+               int comma = 0;
+               e = s;
+               while (*e && *e != ',')
+                       e++;
+               if (*e)
+                       comma = 1;
+               *e = 0;
+               grp = atoi(s);
+               if (comma) {
+                       *e = ',';
+                       s = e + 1;
+               } else {
+                       s = e;
+               }
+               if (grp < 0)
+                       return -1;
+               kinfo->persona_groups[idx] = grp;
+               idx++;
+       }
+       kinfo->persona_ngroups = idx;
+
+       return 0;
+}
+
+#endif /* _PERSONA_TEST_H_ */