]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/x86_64/copyio.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / osfmk / x86_64 / copyio.c
index 557fae0ecbbb76c00f779ee148c69874241c2781..9569ed36c333baef09187c49bcc97049ea6de272 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -31,6 +31,7 @@
 #include <i386/param.h>
 #include <i386/misc_protos.h>
 #include <i386/cpu_data.h>
+#include <i386/machine_cpu.h>
 #include <i386/machine_routines.h>
 #include <i386/cpuid.h>
 #include <i386/vmx.h>
@@ -43,6 +44,7 @@
 #include <sys/kdebug.h>
 
 #include <kern/copyout_shim.h>
+#include <kern/zalloc_internal.h>
 
 #undef copyin
 #undef copyout
@@ -79,10 +81,10 @@ const int copysize_limit_panic = (64 * MB);
  */
 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);
-
-/* On by default, optionally disabled by boot-arg */
-extern boolean_t copyio_zalloc_check;
+extern int _copyin_atomic32(const char *src, uint32_t *dst);
+extern int _copyin_atomic64(const char *src, uint64_t *dst);
+extern int _copyout_atomic32(const uint32_t *u32, char *src);
+extern int _copyout_atomic64(const uint64_t *u64, char *src);
 
 /*
  * Types of copies:
@@ -92,7 +94,10 @@ extern boolean_t copyio_zalloc_check;
 #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 */
+#define COPYINATOMIC32  5       /* from user virtual to kernel virtual */
+#define COPYINATOMIC64  6       /* from user virtual to kernel virtual */
+#define COPYOUTATOMIC32 7       /* from user virtual to kernel virtual */
+#define COPYOUTATOMIC64 8       /* from user virtual to kernel virtual */
 
 #if ENABLE_SMAPLOG
 typedef struct {
@@ -194,9 +199,15 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
                if (__improbable((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);
                }
-               if (__probable(copyio_zalloc_check)) {
-                       kernel_buf_size = zone_element_size(kernel_addr, NULL);
-                       if (__improbable(kernel_buf_size && kernel_buf_size < nbytes)) {
+               if (__probable(!zalloc_disable_copyio_check)) {
+                       zone_t src_zone = NULL;
+                       kernel_buf_size = zone_element_size(kernel_addr, &src_zone);
+                       /*
+                        * Size of elements in the permanent zone is not saved as a part of the
+                        * zone's info
+                        */
+                       if (__improbable(src_zone && !src_zone->permanent &&
+                           kernel_buf_size < nbytes)) {
                                panic("copyio: kernel buffer %p has size %lu < nbytes %lu", kernel_addr, kernel_buf_size, nbytes);
                        }
                }
@@ -210,11 +221,27 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
                goto out;
        }
 
+       if (copy_type >= COPYINATOMIC32 && copy_type <= COPYOUTATOMIC64) {
+               if (__improbable(pmap == kernel_pmap)) {
+                       error = EFAULT;
+                       goto out;
+               }
+       }
+
 #if KASAN
-       if (copy_type == COPYIN || copy_type == COPYINSTR || copy_type == COPYINWORD) {
+       switch (copy_type) {
+       case COPYIN:
+       case COPYINSTR:
+       case COPYINATOMIC32:
+       case COPYINATOMIC64:
                __asan_storeN((uptr)kernel_addr, nbytes);
-       } else if (copy_type == COPYOUT) {
+               break;
+       case COPYOUT:
+       case COPYOUTATOMIC32:
+       case COPYOUTATOMIC64:
                __asan_loadN((uptr)kernel_addr, nbytes);
+               kasan_check_uninitialized((vm_address_t)kernel_addr, nbytes);
+               break;
        }
 #endif
 
@@ -288,10 +315,24 @@ copyio(int copy_type, user_addr_t user_addr, char *kernel_addr,
                    nbytes);
                break;
 
-       case COPYINWORD:
-               error = _copyin_word((const void *) user_addr,
-                   (void *) kernel_addr,
-                   nbytes);
+       case COPYINATOMIC32:
+               error = _copyin_atomic32((const void *) user_addr,
+                   (void *) kernel_addr);
+               break;
+
+       case COPYINATOMIC64:
+               error = _copyin_atomic64((const void *) user_addr,
+                   (void *) kernel_addr);
+               break;
+
+       case COPYOUTATOMIC32:
+               error = _copyout_atomic32((const void *) kernel_addr,
+                   (void *) user_addr);
+               break;
+
+       case COPYOUTATOMIC64:
+               error = _copyout_atomic64((const void *) kernel_addr,
+                   (void *) user_addr);
                break;
 
        case COPYINSTR:
@@ -395,23 +436,63 @@ copyin(const user_addr_t user_addr, void *kernel_addr, vm_size_t nbytes)
 }
 
 /*
- * copyin_word
- * Read an aligned value from userspace as a single memory transaction.
- * This function supports userspace synchronization features
+ * copy{in,out}_atomic{32,64}
+ * Read or store an aligned value from userspace as a single memory transaction.
+ * These functions support userspace synchronization features
  */
 int
-copyin_word(const user_addr_t user_addr, uint64_t *kernel_addr, vm_size_t nbytes)
+copyin_atomic32(const user_addr_t user_addr, uint32_t *kernel_addr)
 {
-       /* Verify sizes */
-       if ((nbytes != 4) && (nbytes != 8)) {
+       /* Test alignment */
+       if (user_addr & 3) {
                return EINVAL;
        }
+       return copyio(COPYINATOMIC32, user_addr, (char *)(uintptr_t)kernel_addr, 4, NULL, 0);
+}
 
+int
+copyin_atomic32_wait_if_equals(const user_addr_t user_addr, uint32_t value)
+{
+       uint32_t u32;
+       int result = copyin_atomic32(user_addr, &u32);
+       if (__improbable(result)) {
+               return result;
+       }
+       if (u32 != value) {
+               return ESTALE;
+       }
+       cpu_pause();
+       return 0;
+}
+
+int
+copyin_atomic64(const user_addr_t user_addr, uint64_t *kernel_addr)
+{
+       /* Test alignment */
+       if (user_addr & 7) {
+               return EINVAL;
+       }
+       return copyio(COPYINATOMIC64, user_addr, (char *)(uintptr_t)kernel_addr, 8, NULL, 0);
+}
+
+int
+copyout_atomic32(uint32_t value, user_addr_t user_addr)
+{
+       /* Test alignment */
+       if (user_addr & 3) {
+               return EINVAL;
+       }
+       return copyio(COPYOUTATOMIC32, user_addr, (char *)&value, 4, NULL, 0);
+}
+
+int
+copyout_atomic64(uint64_t value, user_addr_t user_addr)
+{
        /* Test alignment */
-       if (user_addr & (nbytes - 1)) {
+       if (user_addr & 7) {
                return EINVAL;
        }
-       return copyio(COPYINWORD, user_addr, (char *)(uintptr_t)kernel_addr, nbytes, NULL, 0);
+       return copyio(COPYOUTATOMIC64, user_addr, (char *)&value, 8, NULL, 0);
 }
 
 int