X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0a7de7458d150b5d4dffc935ba399be265ef0a1a..f427ee49d309d8fc33ebf3042c3a775f2f530ded:/bsd/kern/kern_newsysctl.c diff --git a/bsd/kern/kern_newsysctl.c b/bsd/kern/kern_newsysctl.c index 746752d34..b6765a202 100644 --- a/bsd/kern/kern_newsysctl.c +++ b/bsd/kern/kern_newsysctl.c @@ -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@ * @@ -84,6 +84,10 @@ #include #endif +#if defined(HAS_APPLE_PAC) +#include +#include +#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; }