]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_newsysctl.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_newsysctl.c
index 746752d34dac2baa5e40fd1a6472d2226e8b5d6f..b6765a202db9313f25400dbcf343d5d595055c66 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
 #include <security/mac_framework.h>
 #endif
 
+#if defined(HAS_APPLE_PAC)
+#include <os/hash.h>
+#include <ptrauth.h>
+#endif /* defined(HAS_APPLE_PAC) */
 
 lck_grp_t * sysctl_lock_group = NULL;
 lck_rw_t * sysctl_geometry_lock = NULL;
@@ -112,7 +116,7 @@ STATIC int sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp,
     struct sysctl_oid **oidpp);
 STATIC int sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l);
 STATIC int sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l);
-STATIC int name2oid(char *name, int *oid, u_int *len);
+STATIC int name2oid(char *name, int *oid, size_t *len);
 STATIC int sysctl_sysctl_name2oid(struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req);
 STATIC int sysctl_sysctl_next(struct sysctl_oid *oidp, void *arg1, int arg2,
     struct sysctl_req *req);
@@ -122,9 +126,9 @@ STATIC int sysctl_new_user(struct sysctl_req *req, void *p, size_t l);
 
 STATIC void sysctl_create_user_req(struct sysctl_req *req, struct proc *p, user_addr_t oldp,
     size_t oldlen, user_addr_t newp, size_t newlen);
-STATIC int sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestring, size_t namestringlen, int *name, u_int namelen, struct sysctl_req *req);
+STATIC int sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestring, size_t namestringlen, int *name, size_t namelen, struct sysctl_req *req);
 
-int     kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen);
+int     kernel_sysctl(struct proc *p, int *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen);
 int     kernel_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
 int     userland_sysctl(boolean_t string_is_canonical,
     char *namestring, size_t namestringlen,
@@ -155,6 +159,7 @@ sysctl_register_oid(struct sysctl_oid *new_oidp)
         * structure was changed for a necessary reason).
         */
        if (!(new_oidp->oid_kind & CTLFLAG_OID2)) {
+#if __x86_64__
                /*
                 * XXX: M_TEMP is perhaps not the most apropriate zone, as it
                 * XXX: will subject us to use-after-free by other consumers.
@@ -170,7 +175,10 @@ sysctl_register_oid(struct sysctl_oid *new_oidp)
                 * Note:        We may want to set the oid_descr to the
                 *              oid_name (or "") at some future date.
                 */
-               memcpy(oidp, new_oidp, offsetof(struct sysctl_oid, oid_descr));
+               *oidp = *new_oidp;
+#else
+               panic("Old style sysctl without a version number isn't supported");
+#endif
        } else {
                /* It's a later version; handle the versions we know about */
                switch (new_oidp->oid_version) {
@@ -209,13 +217,30 @@ sysctl_register_oid(struct sysctl_oid *new_oidp)
                }
        }
 
+#if defined(HAS_APPLE_PAC)
+       if (oidp->oid_handler) {
+               /*
+                * Sign oid_handler address-discriminated upon installation to make it
+                * harder to replace with an arbitrary function pointer.  Blend with
+                * a hash of oid_arg1 for robustness against memory corruption.
+                */
+               oidp->oid_handler = ptrauth_auth_and_resign(oidp->oid_handler,
+                   ptrauth_key_function_pointer,
+                   ptrauth_function_pointer_type_discriminator(typeof(oidp->oid_handler)),
+                   ptrauth_key_function_pointer,
+                   ptrauth_blend_discriminator(&oidp->oid_handler,
+                   os_hash_kernel_pointer(oidp->oid_arg1)));
+       }
+#endif /* defined(HAS_APPLE_PAC) */
 
        /*
         * Insert the oid into the parent's list in order.
         */
        q = NULL;
        SLIST_FOREACH(p, parent, oid_link) {
-               if (oidp->oid_number < p->oid_number) {
+               if (oidp->oid_number == p->oid_number) {
+                       panic("attempting to register a sysctl at previously registered slot : %d", oidp->oid_number);
+               } else if (oidp->oid_number < p->oid_number) {
                        break;
                }
                q = p;
@@ -234,12 +259,17 @@ void
 sysctl_unregister_oid(struct sysctl_oid *oidp)
 {
        struct sysctl_oid *removed_oidp = NULL; /* OID removed from tree */
+#if __x86_64__
        struct sysctl_oid *old_oidp = NULL;     /* OID compatibility copy */
+#else
+       struct sysctl_oid *const old_oidp = NULL;
+#endif
 
        /* Get the write lock to modify the geometry */
        lck_rw_lock_exclusive(sysctl_geometry_lock);
 
        if (!(oidp->oid_kind & CTLFLAG_OID2)) {
+#if __x86_64__
                /*
                 * We're using a copy so we can get the new fields in an
                 * old structure, so we have to iterate to compare the
@@ -255,6 +285,9 @@ sysctl_unregister_oid(struct sysctl_oid *oidp)
                        SLIST_REMOVE(old_oidp->oid_parent, old_oidp, sysctl_oid, oid_link);
                        removed_oidp = old_oidp;
                }
+#else
+               panic("Old style sysctl without a version number isn't supported");
+#endif
        } else {
                /* It's a later version; handle the versions we know about */
                switch (oidp->oid_version) {
@@ -269,6 +302,20 @@ sysctl_unregister_oid(struct sysctl_oid *oidp)
                }
        }
 
+#if defined(HAS_APPLE_PAC)
+       if (removed_oidp && removed_oidp->oid_handler && old_oidp == NULL) {
+               /*
+                * Revert address-discriminated signing performed by
+                * sysctl_register_oid() (in case this oid is registered again).
+                */
+               removed_oidp->oid_handler = ptrauth_auth_and_resign(removed_oidp->oid_handler,
+                   ptrauth_key_function_pointer,
+                   ptrauth_blend_discriminator(&removed_oidp->oid_handler,
+                   os_hash_kernel_pointer(removed_oidp->oid_arg1)),
+                   ptrauth_key_function_pointer,
+                   ptrauth_function_pointer_type_discriminator(typeof(removed_oidp->oid_handler)));
+       }
+#endif /* defined(HAS_APPLE_PAC) */
 
        /*
         * We've removed it from the list at this point, but we don't want
@@ -285,9 +332,11 @@ sysctl_unregister_oid(struct sysctl_oid *oidp)
        /* Release the write lock */
        lck_rw_unlock_exclusive(sysctl_geometry_lock);
 
-       /* If it was allocated, free it after dropping the lock */
        if (old_oidp != NULL) {
+#if __x86_64__
+               /* If it was allocated, free it after dropping the lock */
                FREE(old_oidp, M_TEMP);
+#endif
        }
 }
 
@@ -349,6 +398,7 @@ sysctl_early_init(void)
        sysctl_unlocked_node_lock = lck_mtx_alloc_init(sysctl_lock_group, NULL);
 
        sysctl_register_set("__sysctl_set");
+       sysctl_load_devicetree_entries();
 }
 
 /*
@@ -430,26 +480,27 @@ int
 sysctl_io_string(struct sysctl_req *req, char *pValue, size_t valueSize, int trunc, int *changed)
 {
        int error;
+       size_t len = strlen(pValue) + 1;
 
        if (changed) {
                *changed = 0;
        }
 
-       if (trunc && req->oldptr && req->oldlen && (req->oldlen < strlen(pValue) + 1)) {
+       if (trunc && req->oldptr && req->oldlen && (req->oldlen < len)) {
                /* If trunc != 0, if you give it a too small (but larger than
                 * 0 bytes) buffer, instead of returning ENOMEM, it truncates the
                 * returned string to the buffer size.  This preserves the semantics
                 * of some library routines implemented via sysctl, which truncate
                 * their returned data, rather than simply returning an error. The
-                * returned string is always NUL terminated. */
+                * returned string is always nul (ascii '\0') terminated. */
                error = SYSCTL_OUT(req, pValue, req->oldlen - 1);
                if (!error) {
-                       char c = 0;
+                       char c = '\0';
                        error = SYSCTL_OUT(req, &c, 1);
                }
        } else {
                /* Copy string out */
-               error = SYSCTL_OUT(req, pValue, strlen(pValue) + 1);
+               error = SYSCTL_OUT(req, pValue, len);
        }
 
        /* error or no new value */
@@ -467,7 +518,7 @@ sysctl_io_string(struct sysctl_req *req, char *pValue, size_t valueSize, int tru
                return EINVAL;
        }
 
-       /* copy the string in and force NUL termination */
+       /* copy the string in and force nul termination */
        error = SYSCTL_IN(req, pValue, req->newlen);
        pValue[req->newlen] = '\0';
 
@@ -913,9 +964,9 @@ SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_next, "
  * Locks:      Assumes sysctl_geometry_lock is held prior to calling
  */
 STATIC int
-name2oid(char *name, int *oid, u_int *len)
+name2oid(char *name, int *oid, size_t *len)
 {
-       int i;
+       char i;
        struct sysctl_oid *oidp;
        struct sysctl_oid_list *lsp = &sysctl__children;
        char *p;
@@ -949,7 +1000,7 @@ name2oid(char *name, int *oid, u_int *len)
                *oid++ = oidp->oid_number;
                (*len)++;
 
-               if (!i) {
+               if (i == '\0') {
                        return 0;
                }
 
@@ -1021,7 +1072,7 @@ sysctl_sysctl_name2oid(__unused struct sysctl_oid *oidp, __unused void *arg1,
 {
        char *p;
        int error, oid[CTL_MAXNAME] = {};
-       u_int len = 0;          /* set by name2oid() */
+       size_t len = 0;          /* set by name2oid() */
 
        if (req->newlen < 1) {
                return ENOENT;
@@ -1312,7 +1363,7 @@ sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l)
 }
 
 int
-kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen)
+kernel_sysctl(struct proc *p, int *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen)
 {
        int error = 0;
        struct sysctl_req req;
@@ -1403,7 +1454,7 @@ sysctl_new_user(struct sysctl_req *req, void *p, size_t l)
  */
 
 int
-sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestring, size_t namestringlen, int *name, u_int namelen, struct sysctl_req *req)
+sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestring, size_t namestringlen, int *name, size_t namelen, struct sysctl_req *req)
 {
        u_int indx;
        int i;
@@ -1589,9 +1640,20 @@ found:
                lck_mtx_lock(sysctl_unlocked_node_lock);
        }
 
+#if defined(HAS_APPLE_PAC)
+       /*
+        * oid_handler is signed address-discriminated by sysctl_register_oid().
+        */
+       oid_handler = ptrauth_auth_and_resign(oid_handler,
+           ptrauth_key_function_pointer,
+           ptrauth_blend_discriminator(&oid->oid_handler,
+           os_hash_kernel_pointer(oid->oid_arg1)),
+           ptrauth_key_function_pointer,
+           ptrauth_function_pointer_type_discriminator(typeof(oid_handler)));
+#endif /* defined(HAS_APPLE_PAC) */
 
        if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
-               i = oid_handler(oid, name + indx, namelen - indx, req);
+               i = oid_handler(oid, name + indx, (int)(namelen - indx), req);
        } else {
                i = oid_handler(oid, oid->oid_arg1, oid->oid_arg2, req);
        }
@@ -1656,7 +1718,7 @@ sysctl_create_user_req(struct sysctl_req *req, struct proc *p, user_addr_t oldp,
 int
 sysctl(proc_t p, struct sysctl_args *uap, __unused int32_t *retval)
 {
-       int error;
+       int error, new_error;
        size_t oldlen = 0, newlen;
        int name[CTL_MAXNAME];
        struct sysctl_req req;
@@ -1721,16 +1783,25 @@ sysctl(proc_t p, struct sysctl_args *uap, __unused int32_t *retval)
 
 err:
        if (uap->oldlenp != USER_ADDR_NULL) {
-               error = suulong(uap->oldlenp, oldlen);
+               /*
+                * Only overwrite the old error value on a new error
+                */
+               new_error = suulong(uap->oldlenp, oldlen);
+
+               if (new_error) {
+                       error = new_error;
+               }
        }
 
        return error;
 }
 
+// sysctlbyname is also exported as KPI to kexts
+// and the syscall name cannot conflict with it
 int
-sysctlbyname(proc_t p, struct sysctlbyname_args *uap, __unused int32_t *retval)
+sys_sysctlbyname(proc_t p, struct sysctlbyname_args *uap, __unused int32_t *retval)
 {
-       int error;
+       int error, new_error;
        size_t oldlen = 0, newlen;
        char *name;
        size_t namelen = 0;
@@ -1788,7 +1859,14 @@ sysctlbyname(proc_t p, struct sysctlbyname_args *uap, __unused int32_t *retval)
        }
 
        if (uap->oldlenp != USER_ADDR_NULL) {
-               error = suulong(uap->oldlenp, oldlen);
+               /*
+                * Only overwrite the old error value on a new error
+                */
+               new_error = suulong(uap->oldlenp, oldlen);
+
+               if (new_error) {
+                       error = new_error;
+               }
        }
 
        return error;
@@ -1858,10 +1936,13 @@ kernel_sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, s
        oidlen = sizeof(oid);
        error = kernel_sysctl(current_proc(), name2mib_oid, 2, oid, &oidlen, __DECONST(void *, name), strlen(name));
        oidlen /= sizeof(int);
+       if (oidlen > UINT_MAX) {
+               error = EDOM;
+       }
 
        /* now use the OID */
        if (error == 0) {
-               error = kernel_sysctl(current_proc(), oid, oidlen, oldp, oldlenp, newp, newlen);
+               error = kernel_sysctl(current_proc(), oid, (u_int)oidlen, oldp, oldlenp, newp, newlen);
        }
        return error;
 }