]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/x86_64/copyio.c
xnu-3789.60.24.tar.gz
[apple/xnu.git] / osfmk / x86_64 / copyio.c
index 6411418061d88e562eae15aeee01d98e3d2bc7a5..5ce1c3b739b71d188eaa90b90dd5c4806a858812 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2016 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
 static int copyio(int, user_addr_t, char *, vm_size_t, vm_size_t *, int);
 static int copyio_phys(addr64_t, addr64_t, vm_size_t, int);
 
 static int copyio(int, user_addr_t, char *, vm_size_t, vm_size_t *, int);
 static int copyio_phys(addr64_t, addr64_t, vm_size_t, int);
 
+/*
+ * Copy sizes bigger than this value will cause a kernel panic.
+ *
+ * Yes, this is an arbitrary fixed limit, but it's almost certainly
+ * a programming error to be copying more than this amount between
+ * user and wired kernel memory in a single invocation on this
+ * platform.
+ */
+#define COPYSIZELIMIT_PANIC     (64*MB)
+
 /*
  * The copy engine has the following characteristics
  *   - copyio() handles copies to/from user or kernel space
 /*
  * The copy engine has the following characteristics
  *   - copyio() handles copies to/from user or kernel space
@@ -63,6 +73,7 @@ static int copyio_phys(addr64_t, addr64_t, vm_size_t, int);
  */
 extern int _bcopy(const void *, void *, vm_size_t);
 extern int _bcopystr(const void *, void *, vm_size_t, vm_size_t *);
  */
 extern int _bcopy(const void *, void *, vm_size_t);
 extern int _bcopystr(const void *, void *, vm_size_t, vm_size_t *);
+extern int _copyin_word(const char *src, uint64_t *dst, vm_size_t len);
 
 
 /*
 
 
 /*
@@ -73,42 +84,103 @@ extern int _bcopystr(const void *, void *, vm_size_t, vm_size_t *);
 #define COPYINSTR      2       /* string variant of copyout */
 #define COPYINPHYS     3       /* from user virtual to kernel physical */
 #define COPYOUTPHYS    4       /* from kernel physical to user virtual */
 #define COPYINSTR      2       /* string variant of copyout */
 #define COPYINPHYS     3       /* from user virtual to kernel physical */
 #define COPYOUTPHYS    4       /* from kernel physical to user virtual */
+#define COPYINWORD     5       /* from user virtual to kernel virtual */
+
+#if ENABLE_SMAPLOG
+typedef struct {
+       uint64_t        timestamp;
+       thread_t        thread;
+       uintptr_t       cr4;
+       uint8_t         cpuid;
+       uint8_t         smap_state;
+       uint8_t         copyio_active;
+} smaplog_entry_t;
+
+#define SMAPLOG_BUFFER_SIZE (50)
+static smaplog_entry_t smaplog_cbuf[SMAPLOG_BUFFER_SIZE];
+static uint32_t                smaplog_head = 0;
+
+static void
+smaplog_add_entry(boolean_t enabling)
+{
+       uint32_t index = 0;
+       thread_t thread = current_thread();
+
+       do {
+               index = smaplog_head;
+       } while (!OSCompareAndSwap(index, (index + 1) % SMAPLOG_BUFFER_SIZE, &smaplog_head));
+
+       assert(index < SMAPLOG_BUFFER_SIZE);
+       assert(smaplog_head < SMAPLOG_BUFFER_SIZE);
+       assert(thread);
+
+       smaplog_cbuf[index].timestamp = mach_absolute_time();
+       smaplog_cbuf[index].thread = thread;
+       smaplog_cbuf[index].cpuid = cpu_number();
+       smaplog_cbuf[index].cr4 = get_cr4();
+       smaplog_cbuf[index].smap_state = enabling;
+       smaplog_cbuf[index].copyio_active = (thread->machine.specFlags & CopyIOActive) ? 1 : 0;
+}
+#endif /* ENABLE_SMAPLOG */
+
+extern boolean_t pmap_smap_enabled;
+static inline void user_access_enable(void) {
+       if (pmap_smap_enabled) {
+               stac();
+#if ENABLE_SMAPLOG
+               smaplog_add_entry(TRUE);
+#endif
+       }
+}
+static inline void user_access_disable(void) {
+       if (pmap_smap_enabled) {
+               clac();
+#if ENABLE_SMAPLOG
+               smaplog_add_entry(FALSE);
+#endif
+       }
+}
 
 
+#if COPYIO_TRACE_ENABLED
+#define COPYIO_TRACE(x, a, b, c, d, e) KERNEL_DEBUG_CONSTANT(x, a, b, c, d, e)
+#else
+#define COPYIO_TRACE(x, a, b, c, d, e) do { } while(0)
+#endif
 
 static int
 copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
        vm_size_t nbytes, vm_size_t *lencopied, int use_kernel_map)
 {
 
 static int
 copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
        vm_size_t nbytes, vm_size_t *lencopied, int use_kernel_map)
 {
-        thread_t       thread;
+        thread_t       thread = current_thread();
        pmap_t          pmap;
        vm_size_t       bytes_copied;
        int             error = 0;
        boolean_t       istate = FALSE;
        boolean_t       recursive_CopyIOActive;
        pmap_t          pmap;
        vm_size_t       bytes_copied;
        int             error = 0;
        boolean_t       istate = FALSE;
        boolean_t       recursive_CopyIOActive;
-#if KDEBUG
+#if    COPYIO_TRACE_ENABLED
        int             debug_type = 0xeff70010;
        debug_type += (copy_type << 2);
 #endif
        int             debug_type = 0xeff70010;
        debug_type += (copy_type << 2);
 #endif
+       boolean_t nopagezero = thread->map->pmap->pagezero_accessible;
 
 
-       thread = current_thread();
+       assert(nbytes < COPYSIZELIMIT_PANIC);
 
 
-       KERNEL_DEBUG(debug_type | DBG_FUNC_START,
-                    (unsigned)(user_addr >> 32), (unsigned)user_addr,
-                    nbytes, thread->machine.copyio_state, 0);
+       COPYIO_TRACE(debug_type | DBG_FUNC_START,
+           user_addr, kernel_addr, nbytes, use_kernel_map, 0);
 
 
-       if (nbytes == 0)
+       if (__improbable(nbytes == 0))
                goto out;
 
         pmap = thread->map->pmap;
 
                goto out;
 
         pmap = thread->map->pmap;
 
-       if ((copy_type != COPYINPHYS) && (copy_type != COPYOUTPHYS) && ((vm_offset_t)kernel_addr < VM_MIN_KERNEL_AND_KEXT_ADDRESS)) {
+       if (__improbable((copy_type != COPYINPHYS) && (copy_type != COPYOUTPHYS) && ((vm_offset_t)kernel_addr < VM_MIN_KERNEL_AND_KEXT_ADDRESS))) {
                panic("Invalid copy parameter, copy type: %d, kernel address: %p", copy_type, kernel_addr);
        }
 
        /* Sanity and security check for addresses to/from a user */
 
                panic("Invalid copy parameter, copy type: %d, kernel address: %p", copy_type, kernel_addr);
        }
 
        /* Sanity and security check for addresses to/from a user */
 
-       if (((pmap != kernel_pmap) && (use_kernel_map == 0)) &&
-           ((nbytes && (user_addr+nbytes <= user_addr)) || ((user_addr + nbytes) > vm_map_max(thread->map)))) {
+       if (__improbable(((pmap != kernel_pmap) && (use_kernel_map == 0)) &&
+               ((nbytes && (user_addr+nbytes <= user_addr)) || ((user_addr + nbytes) > vm_map_max(thread->map))))) {
                error = EFAULT;
                goto out;
        }
                error = EFAULT;
                goto out;
        }
@@ -122,13 +194,24 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
         * we will later restore the correct cr3.
         */
        recursive_CopyIOActive = thread->machine.specFlags & CopyIOActive;
         * we will later restore the correct cr3.
         */
        recursive_CopyIOActive = thread->machine.specFlags & CopyIOActive;
-       thread->machine.specFlags |= CopyIOActive;
-       if (no_shared_cr3) {
+
+       boolean_t pdswitch = no_shared_cr3 || nopagezero;
+
+       if (__improbable(pdswitch)) {
                istate = ml_set_interrupts_enabled(FALSE);
                istate = ml_set_interrupts_enabled(FALSE);
-               if (get_cr3_base() != pmap->pm_cr3)
+               if (nopagezero && pmap_pcid_ncpus) {
+                       pmap_pcid_activate(pmap, cpu_number(), TRUE, TRUE);
+               } else if (get_cr3_base() != pmap->pm_cr3) {
                        set_cr3_raw(pmap->pm_cr3);
                        set_cr3_raw(pmap->pm_cr3);
+               }
+               thread->machine.specFlags |= CopyIOActive;
+       } else {
+               thread->machine.specFlags |= CopyIOActive;
        }
 
        }
 
+       user_access_enable();
+
+#if DEVELOPMENT || DEBUG       
        /*
         * Ensure that we're running on the target thread's cr3.
         */
        /*
         * Ensure that we're running on the target thread's cr3.
         */
@@ -138,11 +221,14 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
                        copy_type, (void *)user_addr, kernel_addr, nbytes, lencopied, use_kernel_map,
                        (void *) get_cr3_raw(), (void *) pmap->pm_cr3);
        }
                        copy_type, (void *)user_addr, kernel_addr, nbytes, lencopied, use_kernel_map,
                        (void *) get_cr3_raw(), (void *) pmap->pm_cr3);
        }
-       if (no_shared_cr3)
+#endif
+
+       if (__improbable(pdswitch)) {
                (void) ml_set_interrupts_enabled(istate);
                (void) ml_set_interrupts_enabled(istate);
+       }
 
 
-       KERNEL_DEBUG(0xeff70044 | DBG_FUNC_NONE, (unsigned)user_addr,
-                    (unsigned)kernel_addr, nbytes, 0, 0);
+       COPYIO_TRACE(0xeff70044 | DBG_FUNC_NONE, user_addr,
+                    kernel_addr, nbytes, 0, 0);
 
         switch (copy_type) {
 
 
         switch (copy_type) {
 
@@ -170,6 +256,12 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
                                nbytes);
                break;
 
                                nbytes);
                break;
 
+       case COPYINWORD:
+               error = _copyin_word((const void *) user_addr,
+                               (void *) kernel_addr,
+                               nbytes);
+               break;
+
        case COPYINSTR:
                error = _bcopystr((const void *) user_addr,
                                kernel_addr,
        case COPYINSTR:
                error = _bcopystr((const void *) user_addr,
                                kernel_addr,
@@ -208,22 +300,30 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
                        error = ENAMETOOLONG;
                        break;
                }
                        error = ENAMETOOLONG;
                        break;
                }
-               break;
        }
 
        }
 
-       if (!recursive_CopyIOActive) {
-               thread->machine.specFlags &= ~CopyIOActive;
-       }
-       if (no_shared_cr3) {
+       user_access_disable();
+
+       if (__improbable(pdswitch)) {
                istate = ml_set_interrupts_enabled(FALSE);
                istate = ml_set_interrupts_enabled(FALSE);
-               if  (get_cr3_raw() != kernel_pmap->pm_cr3)
-                       set_cr3_raw(kernel_pmap->pm_cr3);
+               if  (!recursive_CopyIOActive && (get_cr3_raw() != kernel_pmap->pm_cr3)) {
+                       if (nopagezero && pmap_pcid_ncpus) {
+                               pmap_pcid_activate(pmap, cpu_number(), TRUE, FALSE);
+                       } else {
+                               set_cr3_raw(kernel_pmap->pm_cr3);
+                       }
+               }
+
+               if (!recursive_CopyIOActive) {
+                       thread->machine.specFlags &= ~CopyIOActive;
+               }
                (void) ml_set_interrupts_enabled(istate);
                (void) ml_set_interrupts_enabled(istate);
+       } else if (!recursive_CopyIOActive) {
+               thread->machine.specFlags &= ~CopyIOActive;
        }
 
 out:
        }
 
 out:
-       KERNEL_DEBUG(debug_type | DBG_FUNC_END, (unsigned)user_addr,
-                    (unsigned)kernel_addr, (unsigned)nbytes, error, 0);
+       COPYIO_TRACE(debug_type | DBG_FUNC_END, user_addr, kernel_addr, nbytes, error, 0);
 
        return (error);
 }
 
        return (error);
 }
@@ -260,6 +360,24 @@ copyin(const user_addr_t user_addr, char *kernel_addr, vm_size_t nbytes)
     return copyio(COPYIN, user_addr, kernel_addr, nbytes, NULL, 0);
 }
 
     return copyio(COPYIN, user_addr, kernel_addr, nbytes, NULL, 0);
 }
 
+/*
+ * copyin_word
+ * Read an aligned value from userspace as a single memory transaction.
+ * This function supports userspace synchronization features
+ */
+int
+copyin_word(const user_addr_t user_addr, uint64_t *kernel_addr, vm_size_t nbytes)
+{
+       /* Verify sizes */
+       if ((nbytes != 4) && (nbytes != 8))
+               return EINVAL;
+
+       /* Test alignment */
+       if (user_addr & (nbytes - 1))
+               return EINVAL;
+       return copyio(COPYINWORD, user_addr, (char *)(uintptr_t)kernel_addr, nbytes, NULL, 0);
+}
+
 int
 copyinstr(const user_addr_t user_addr,  char *kernel_addr, vm_size_t nbytes, vm_size_t *lencopied)
 {
 int
 copyinstr(const user_addr_t user_addr,  char *kernel_addr, vm_size_t nbytes, vm_size_t *lencopied)
 {