X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3e170ce000f1506b7b5d2c5c7faec85ceabb573d..eb6b6ca394357805f2bdba989abae309f718b4d8:/bsd/kern/kern_newsysctl.c?ds=sidebyside diff --git a/bsd/kern/kern_newsysctl.c b/bsd/kern/kern_newsysctl.c index fdd86a948..07cd0e082 100644 --- a/bsd/kern/kern_newsysctl.c +++ b/bsd/kern/kern_newsysctl.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2013 Apple Inc. All rights reserved. + * Copyright (c) 2000-2019 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 @@ -11,10 +11,10 @@ * 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@ * * @@ -78,6 +78,15 @@ #include #include +#include + +#if CONFIG_MACF +#include +#endif + +#if defined(HAS_APPLE_PAC) +#include +#endif /* defined(HAS_APPLE_PAC) */ lck_grp_t * sysctl_lock_group = NULL; lck_rw_t * sysctl_geometry_lock = NULL; @@ -90,7 +99,7 @@ lck_mtx_t * sysctl_unlocked_node_lock = NULL; #undef STATIC #endif #if 0 -#define STATIC +#define STATIC #else #define STATIC static #endif @@ -98,32 +107,32 @@ lck_mtx_t * sysctl_unlocked_node_lock = NULL; /* forward declarations of static functions */ STATIC void sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i); STATIC int sysctl_sysctl_debug(struct sysctl_oid *oidp, void *arg1, - int arg2, struct sysctl_req *req); + int arg2, struct sysctl_req *req); STATIC int sysctl_sysctl_name(struct sysctl_oid *oidp, void *arg1, - int arg2, struct sysctl_req *req); -STATIC int sysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, - int *name, u_int namelen, int *next, int *len, int level, - struct sysctl_oid **oidpp); + int arg2, struct sysctl_req *req); +STATIC int sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, + int *name, u_int namelen, int *next, int *len, int level, + 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, u_int *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); + struct sysctl_req *req); STATIC int sysctl_sysctl_oidfmt(struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req); STATIC int sysctl_old_user(struct sysctl_req *req, const void *p, size_t l); 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); + 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); -int kernel_sysctl(struct proc *p, int *name, u_int 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, - int *name, u_int namelen, struct sysctl_req *req, - size_t *retval); +int kernel_sysctl(struct proc *p, int *name, u_int 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, + int *name, u_int namelen, struct sysctl_req *req, + size_t *retval); struct sysctl_oid_list sysctl__children; /* root list */ @@ -154,9 +163,9 @@ sysctl_register_oid(struct sysctl_oid *new_oidp) * XXX: will subject us to use-after-free by other consumers. */ MALLOC(oidp, struct sysctl_oid *, sizeof(*oidp), M_TEMP, M_WAITOK | M_ZERO); - if (oidp == NULL) - return; /* reject: no memory */ - + if (oidp == NULL) { + return; /* reject: no memory */ + } /* * Copy the structure only through the oid_fmt field, which * is the last field in a non-OID2 OID structure. @@ -173,23 +182,10 @@ sysctl_register_oid(struct sysctl_oid *new_oidp) oidp = new_oidp; break; default: - return; /* rejects unknown version */ + return; /* rejects unknown version */ } } - if(sysctl_geometry_lock == NULL) - { - /* - * Initialise the geometry lock for reading/modifying the - * sysctl tree. This is done here because IOKit registers - * some sysctl's before bsd_init() calls - * sysctl_register_fixed(). - */ - - sysctl_lock_group = lck_grp_alloc_init("sysctl", NULL); - sysctl_geometry_lock = lck_rw_alloc_init(sysctl_lock_group, NULL); - sysctl_unlocked_node_lock = lck_mtx_alloc_init(sysctl_lock_group, NULL); - } /* Get the write lock to modify the geometry */ lck_rw_lock_exclusive(sysctl_geometry_lock); @@ -202,31 +198,66 @@ sysctl_register_oid(struct sysctl_oid *new_oidp) /* First, find the highest oid in the parent list >OID_AUTO_START-1 */ n = OID_AUTO_START; SLIST_FOREACH(p, parent, oid_link) { - if (p->oid_number > n) + if (p->oid_number > n) { n = p->oid_number; + } } oidp->oid_number = n + 1; /* * Reflect the number in an llocated OID into the template * of the caller for sysctl_unregister_oid() compares. */ - if (oidp != new_oidp) + if (oidp != new_oidp) { new_oidp->oid_number = oidp->oid_number; + } + } + +#if defined(HAS_APPLE_PAC) + if (oidp->oid_handler) { + /* + * Dereference function-pointer-signed oid_handler to prevent an + * attacker with the ability to observe the result of the + * auth_and_resign below from trying all possible inputs until an auth + * succeeds. + */ + if (__builtin_expect(!*(uintptr_t*)ptrauth_auth_data((void*) + oidp->oid_handler, ptrauth_key_function_pointer, 0), 0)) { + /* + * This is necessary to force the dereference but will never + * actually be reached, dereferencing an invalidly signed pointer + * will trap before getting here (and the codegen is nicer than + * with a panic). + */ + __builtin_trap(); + } + /* + * Sign oid_handler address-discriminated upon installation to make it + * harder to replace with an arbitrary function pointer. + */ + oidp->oid_handler = ptrauth_auth_and_resign(oidp->oid_handler, + ptrauth_key_function_pointer, 0, ptrauth_key_function_pointer, + ptrauth_blend_discriminator(&oidp->oid_handler, + ptrauth_string_discriminator("oid_handler"))); } +#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; } - if (q) + if (q) { SLIST_INSERT_AFTER(q, oidp, oid_link); - else + } else { SLIST_INSERT_HEAD(parent, oidp, oid_link); + } /* Release the write lock */ lck_rw_unlock_exclusive(sysctl_geometry_lock); @@ -235,8 +266,8 @@ sysctl_register_oid(struct sysctl_oid *new_oidp) void sysctl_unregister_oid(struct sysctl_oid *oidp) { - struct sysctl_oid *removed_oidp = NULL; /* OID removed from tree */ - struct sysctl_oid *old_oidp = NULL; /* OID compatibility copy */ + struct sysctl_oid *removed_oidp = NULL; /* OID removed from tree */ + struct sysctl_oid *old_oidp = NULL; /* OID compatibility copy */ /* Get the write lock to modify the geometry */ lck_rw_lock_exclusive(sysctl_geometry_lock); @@ -249,9 +280,9 @@ sysctl_unregister_oid(struct sysctl_oid *oidp) * normally and free the memory. */ SLIST_FOREACH(old_oidp, oidp->oid_parent, oid_link) { - if (!memcmp(&oidp->oid_number, &old_oidp->oid_number, (offsetof(struct sysctl_oid, oid_descr)-offsetof(struct sysctl_oid, oid_number)))) { - break; - } + if (!memcmp(&oidp->oid_number, &old_oidp->oid_number, (offsetof(struct sysctl_oid, oid_descr) - offsetof(struct sysctl_oid, oid_number)))) { + break; + } } if (old_oidp != NULL) { SLIST_REMOVE(old_oidp->oid_parent, old_oidp, sysctl_oid, oid_link); @@ -266,10 +297,39 @@ sysctl_unregister_oid(struct sysctl_oid *oidp) removed_oidp = oidp; break; default: - /* XXX: Can't happen; probably tree coruption.*/ - break; /* rejects unknown version */ + /* XXX: Can't happen; probably tree coruption.*/ + break; /* rejects unknown version */ + } + } + +#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_function(removed_oidp->oid_handler, + ptrauth_key_function_pointer, + ptrauth_blend_discriminator(&removed_oidp->oid_handler, + ptrauth_string_discriminator("oid_handler"))); + /* + * Dereference the function-pointer-signed result to prevent an + * attacker with the ability to observe the result of the + * auth_and_resign above from trying all possible inputs until an auth + * succeeds. + */ + if (__builtin_expect(!*(uintptr_t*)ptrauth_auth_data((void*) + removed_oidp->oid_handler, ptrauth_key_function_pointer, 0), 0)) { + /* + * This is necessary to force the dereference but will never + * actually be reached, dereferencing an invalidly signed pointer + * will trap before getting here (and the codegen is nicer than + * with a panic). + */ + __builtin_trap(); } } +#endif /* defined(HAS_APPLE_PAC) */ /* * We've removed it from the list at this point, but we don't want @@ -279,7 +339,7 @@ sysctl_unregister_oid(struct sysctl_oid *oidp) * * Note: oidp could be NULL if it wasn't found. */ - while(removed_oidp && removed_oidp->oid_refcnt) { + while (removed_oidp && removed_oidp->oid_refcnt) { lck_rw_sleep(sysctl_geometry_lock, LCK_SLEEP_EXCLUSIVE, &removed_oidp->oid_refcnt, THREAD_UNINT); } @@ -303,7 +363,7 @@ sysctl_register_set(const char *set) LINKER_SET_FOREACH(oidpp, struct sysctl_oid **, set) { oidp = *oidpp; if (!(oidp->oid_kind & CTLFLAG_NOAUTO)) { - sysctl_register_oid(oidp); + sysctl_register_oid(oidp); } } } @@ -316,26 +376,41 @@ sysctl_unregister_set(const char *set) LINKER_SET_FOREACH(oidpp, struct sysctl_oid **, set) { oidp = *oidpp; if (!(oidp->oid_kind & CTLFLAG_NOAUTO)) { - sysctl_unregister_oid(oidp); + sysctl_unregister_oid(oidp); } } } - /* - * Register the kernel's oids on startup. + * Exported in BSDKernel.exports, kept for binary compatibility */ - +#if defined(__x86_64__) void -sysctl_register_all() +sysctl_register_fixed(void) { - sysctl_register_set("__sysctl_set"); } +#endif + +/* + * Register the kernel's oids on startup. + */ void -sysctl_register_fixed(void) +sysctl_early_init(void) { - sysctl_register_all(); + /* + * Initialize the geometry lock for reading/modifying the + * sysctl tree. This is done here because IOKit registers + * some sysctl's before bsd_init() would otherwise perform + * subsystem initialization. + */ + + sysctl_lock_group = lck_grp_alloc_init("sysctl", NULL); + sysctl_geometry_lock = lck_rw_alloc_init(sysctl_lock_group, NULL); + sysctl_unlocked_node_lock = lck_mtx_alloc_init(sysctl_lock_group, NULL); + + sysctl_register_set("__sysctl_set"); + sysctl_load_devicetree_entries(); } /* @@ -347,11 +422,14 @@ sysctl_register_fixed(void) */ int -sysctl_io_number(struct sysctl_req *req, long long bigValue, size_t valueSize, void *pValue, int *changed) { - int smallValue; - int error; +sysctl_io_number(struct sysctl_req *req, long long bigValue, size_t valueSize, void *pValue, int *changed) +{ + int smallValue; + int error; - if (changed) *changed = 0; + if (changed) { + *changed = 0; + } /* * Handle the various combinations of caller buffer size and @@ -363,45 +441,51 @@ sysctl_io_number(struct sysctl_req *req, long long bigValue, size_t valueSize, v /* 32 bit value expected or 32 bit buffer offered */ if (((valueSize == sizeof(int)) || ((req->oldlen == sizeof(int)) && (valueSize == sizeof(long long)))) - && (req->oldptr)) { + && (req->oldptr)) { smallValue = (int)bigValue; - if ((long long)smallValue != bigValue) - return(ERANGE); + if ((long long)smallValue != bigValue) { + return ERANGE; + } error = SYSCTL_OUT(req, &smallValue, sizeof(smallValue)); } else { /* any other case is either size-equal or a bug */ error = SYSCTL_OUT(req, &bigValue, valueSize); } /* error or nothing to set */ - if (error || !req->newptr) - return(error); + if (error || !req->newptr) { + return error; + } /* set request for constant */ - if (pValue == NULL) - return(EPERM); + if (pValue == NULL) { + return EPERM; + } /* set request needs to convert? */ if ((req->newlen == sizeof(int)) && (valueSize == sizeof(long long))) { /* new value is 32 bits, upconvert to 64 bits */ error = SYSCTL_IN(req, &smallValue, sizeof(smallValue)); - if (!error) + if (!error) { *(long long *)pValue = (long long)smallValue; + } } else if ((req->newlen == sizeof(long long)) && (valueSize == sizeof(int))) { /* new value is 64 bits, downconvert to 32 bits and range check */ error = SYSCTL_IN(req, &bigValue, sizeof(bigValue)); if (!error) { smallValue = (int)bigValue; - if ((long long)smallValue != bigValue) - return(ERANGE); + if ((long long)smallValue != bigValue) { + return ERANGE; + } *(int *)pValue = smallValue; } } else { /* sizes match, just copy in */ error = SYSCTL_IN(req, pValue, valueSize); } - if (!error && changed) + if (!error && changed) { *changed = 1; - return(error); + } + return error; } int @@ -409,18 +493,20 @@ sysctl_io_string(struct sysctl_req *req, char *pValue, size_t valueSize, int tru { int error; - if (changed) *changed = 0; + if (changed) { + *changed = 0; + } - if (trunc && req->oldptr && req->oldlen && (req->oldlenoldptr && req->oldlen && (req->oldlen < strlen(pValue) + 1)) { /* 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. */ - error = SYSCTL_OUT(req, pValue, req->oldlen-1); + * 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 { @@ -429,50 +515,59 @@ sysctl_io_string(struct sysctl_req *req, char *pValue, size_t valueSize, int tru } /* error or no new value */ - if (error || !req->newptr) - return(error); + if (error || !req->newptr) { + return error; + } /* attempt to set read-only value */ - if (valueSize == 0) - return(EPERM); + if (valueSize == 0) { + return EPERM; + } /* make sure there's room for the new string */ - if (req->newlen >= valueSize) - return(EINVAL); + if (req->newlen >= valueSize) { + 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'; - if (!error && changed) + if (!error && changed) { *changed = 1; - return(error); + } + return error; } -int sysctl_io_opaque(struct sysctl_req *req,void *pValue, size_t valueSize, int *changed) +int +sysctl_io_opaque(struct sysctl_req *req, void *pValue, size_t valueSize, int *changed) { int error; - if (changed) *changed = 0; + if (changed) { + *changed = 0; + } /* Copy blob out */ error = SYSCTL_OUT(req, pValue, valueSize); /* error or nothing to set */ - if (error || !req->newptr) - return(error); + if (error || !req->newptr) { + return error; + } error = SYSCTL_IN(req, pValue, valueSize); - if (!error && changed) + if (!error && changed) { *changed = 1; - return(error); + } + return error; } /* * "Staff-functions" * - * These functions implement a presently undocumented interface + * These functions implement a presently undocumented interface * used by the sysctl program to walk the tree, and get the type * so it can print the value. * This interface is under work and consideration, and should probably @@ -519,35 +614,35 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) struct sysctl_oid *oidp; SLIST_FOREACH(oidp, l, oid_link) { - - for (k=0; koid_number, oidp->oid_name); printf("%c%c%c", - oidp->oid_kind & CTLFLAG_LOCKED ? 'L':' ', - oidp->oid_kind & CTLFLAG_RD ? 'R':' ', - oidp->oid_kind & CTLFLAG_WR ? 'W':' '); + oidp->oid_kind & CTLFLAG_LOCKED ? 'L':' ', + oidp->oid_kind & CTLFLAG_RD ? 'R':' ', + oidp->oid_kind & CTLFLAG_WR ? 'W':' '); - if (oidp->oid_handler) + if (oidp->oid_handler) { printf(" *Handler"); + } switch (oidp->oid_kind & CTLTYPE) { - case CTLTYPE_NODE: - printf(" Node\n"); - if (!oidp->oid_handler) { - sysctl_sysctl_debug_dump_node( - oidp->oid_arg1, i+2); - } - break; - case CTLTYPE_INT: printf(" Int\n"); break; - case CTLTYPE_STRING: printf(" String\n"); break; - case CTLTYPE_QUAD: printf(" Quad\n"); break; - case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break; - default: printf("\n"); + case CTLTYPE_NODE: + printf(" Node\n"); + if (!oidp->oid_handler) { + sysctl_sysctl_debug_dump_node( + oidp->oid_arg1, i + 2); + } + break; + case CTLTYPE_INT: printf(" Int\n"); break; + case CTLTYPE_STRING: printf(" String\n"); break; + case CTLTYPE_QUAD: printf(" Quad\n"); break; + case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break; + default: printf("\n"); } - } } @@ -570,7 +665,7 @@ sysctl_sysctl_debug_dump_node(struct sysctl_oid_list *l, int i) */ STATIC int sysctl_sysctl_debug(__unused struct sysctl_oid *oidp, __unused void *arg1, - __unused int arg2, __unused struct sysctl_req *req) + __unused int arg2, __unused struct sysctl_req *req) { lck_rw_lock_shared(sysctl_geometry_lock); sysctl_sysctl_debug_dump_node(&sysctl__children, 0); @@ -578,8 +673,8 @@ sysctl_sysctl_debug(__unused struct sysctl_oid *oidp, __unused void *arg1, return ENOENT; } -SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD | CTLFLAG_LOCKED, - 0, 0, sysctl_sysctl_debug, "-", ""); +SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_LOCKED, + 0, 0, sysctl_sysctl_debug, "-", ""); /* * sysctl_sysctl_name @@ -629,26 +724,28 @@ SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD | CTLFLAG_LOCKED, */ STATIC int sysctl_sysctl_name(__unused struct sysctl_oid *oidp, void *arg1, int arg2, - struct sysctl_req *req) + struct sysctl_req *req) { int *name = (int *) arg1; u_int namelen = arg2; int error = 0; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children, *lsp2; - char tempbuf[10]; + char tempbuf[10] = {}; lck_rw_lock_shared(sysctl_geometry_lock); while (namelen) { if (!lsp) { - snprintf(tempbuf,sizeof(tempbuf),"%d",*name); - if (req->oldidx) + snprintf(tempbuf, sizeof(tempbuf), "%d", *name); + if (req->oldidx) { error = SYSCTL_OUT(req, ".", 1); - if (!error) + } + if (!error) { error = SYSCTL_OUT(req, tempbuf, strlen(tempbuf)); + } if (error) { lck_rw_done(sysctl_geometry_lock); - return (error); + return error; } namelen--; name++; @@ -656,27 +753,32 @@ sysctl_sysctl_name(__unused struct sysctl_oid *oidp, void *arg1, int arg2, } lsp2 = 0; SLIST_FOREACH(oid, lsp, oid_link) { - if (oid->oid_number != *name) + if (oid->oid_number != *name) { continue; + } - if (req->oldidx) + if (req->oldidx) { error = SYSCTL_OUT(req, ".", 1); - if (!error) + } + if (!error) { error = SYSCTL_OUT(req, oid->oid_name, - strlen(oid->oid_name)); + strlen(oid->oid_name)); + } if (error) { lck_rw_done(sysctl_geometry_lock); - return (error); + return error; } namelen--; name++; - if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oid->oid_kind & CTLTYPE) != CTLTYPE_NODE) { break; + } - if (oid->oid_handler) + if (oid->oid_handler) { break; + } lsp2 = (struct sysctl_oid_list *)oid->oid_arg1; break; @@ -684,7 +786,7 @@ sysctl_sysctl_name(__unused struct sysctl_oid *oidp, void *arg1, int arg2, lsp = lsp2; } lck_rw_done(sysctl_geometry_lock); - return (SYSCTL_OUT(req, "", 1)); + return SYSCTL_OUT(req, "", 1); } SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_name, ""); @@ -722,8 +824,8 @@ SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_name, " * we STRONGLY discourage these types of handlers */ STATIC int -sysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, int *name, u_int namelen, - int *next, int *len, int level, struct sysctl_oid **oidpp) +sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, + int *next, int *len, int level, struct sysctl_oid **oidpp) { struct sysctl_oid *oidp; @@ -733,48 +835,59 @@ sysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, int *name, u_int namelen, *oidpp = oidp; if (!namelen) { - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) { return 0; - if (oidp->oid_handler) + } + if (oidp->oid_handler) { /* We really should call the handler here...*/ return 0; + } lsp = (struct sysctl_oid_list *)oidp->oid_arg1; - if (!SLIST_FIRST(lsp)) + if (!SLIST_FIRST(lsp)) { /* This node had no children - skip it! */ continue; + } - if (!sysctl_sysctl_next_ls (lsp, 0, 0, next+1, - len, level+1, oidpp)) + if (!sysctl_sysctl_next_ls(lsp, 0, 0, next + 1, + len, level + 1, oidpp)) { return 0; + } goto next; } - if (oidp->oid_number < *name) + if (oidp->oid_number < *name) { continue; + } if (oidp->oid_number > *name) { - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) { return 0; - if (oidp->oid_handler) + } + if (oidp->oid_handler) { return 0; + } lsp = (struct sysctl_oid_list *)oidp->oid_arg1; - if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, - next+1, len, level+1, oidpp)) - return (0); + if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1, + next + 1, len, level + 1, oidpp)) { + return 0; + } goto next; } - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) { continue; + } - if (oidp->oid_handler) + if (oidp->oid_handler) { continue; + } lsp = (struct sysctl_oid_list *)oidp->oid_arg1; - if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, next+1, - len, level+1, oidpp)) - return (0); - next: + if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1, next + 1, + len, level + 1, oidpp)) { + return 0; + } +next: namelen = 1; *len = level; } @@ -821,22 +934,23 @@ sysctl_sysctl_next_ls (struct sysctl_oid_list *lsp, int *name, u_int namelen, */ STATIC int sysctl_sysctl_next(__unused struct sysctl_oid *oidp, void *arg1, int arg2, - struct sysctl_req *req) + struct sysctl_req *req) { int *name = (int *) arg1; u_int namelen = arg2; int i, j, error; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children; - int newoid[CTL_MAXNAME]; + int newoid[CTL_MAXNAME] = {}; lck_rw_lock_shared(sysctl_geometry_lock); - i = sysctl_sysctl_next_ls (lsp, name, namelen, newoid, &j, 1, &oid); + i = sysctl_sysctl_next_ls(lsp, name, namelen, newoid, &j, 1, &oid); lck_rw_done(sysctl_geometry_lock); - if (i) + if (i) { return ENOENT; - error = SYSCTL_OUT(req, newoid, j * sizeof (int)); - return (error); + } + error = SYSCTL_OUT(req, newoid, j * sizeof(int)); + return error; } SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_next, ""); @@ -861,27 +975,31 @@ 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, u_int *len) { int i; struct sysctl_oid *oidp; struct sysctl_oid_list *lsp = &sysctl__children; char *p; - if (!*name) + if (!*name) { return ENOENT; + } - p = name + strlen(name) - 1 ; - if (*p == '.') + p = name + strlen(name) - 1; + if (*p == '.') { *p = '\0'; + } *len = 0; - for (p = name; *p && *p != '.'; p++) + for (p = name; *p && *p != '.'; p++) { ; + } i = *p; - if (i == '.') + if (i == '.') { *p = '\0'; + } oidp = SLIST_FIRST(lsp); @@ -894,24 +1012,28 @@ name2oid (char *name, int *oid, u_int *len) (*len)++; if (!i) { - return (0); + return 0; } - if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) + if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) { break; + } - if (oidp->oid_handler) + if (oidp->oid_handler) { break; + } lsp = (struct sysctl_oid_list *)oidp->oid_arg1; oidp = SLIST_FIRST(lsp); *p = i; /* restore */ - name = p+1; - for (p = name; *p && *p != '.'; p++) - ; + name = p + 1; + for (p = name; *p && *p != '.'; p++) { + ; + } i = *p; - if (i == '.') + if (i == '.') { *p = '\0'; + } } return ENOENT; } @@ -957,28 +1079,31 @@ name2oid (char *name, int *oid, u_int *len) */ STATIC int sysctl_sysctl_name2oid(__unused struct sysctl_oid *oidp, __unused void *arg1, - __unused int arg2, struct sysctl_req *req) + __unused int arg2, struct sysctl_req *req) { char *p; - int error, oid[CTL_MAXNAME]; - u_int len = 0; /* set by name2oid() */ + int error, oid[CTL_MAXNAME] = {}; + u_int len = 0; /* set by name2oid() */ - if (req->newlen < 1) + if (req->newlen < 1) { return ENOENT; - if (req->newlen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ - return (ENAMETOOLONG); + } + if (req->newlen >= MAXPATHLEN) { /* XXX arbitrary, undocumented */ + return ENAMETOOLONG; + } - MALLOC(p, char *,req->newlen+1, M_TEMP, M_WAITOK); - if (!p) - return ENOMEM; + MALLOC(p, char *, req->newlen + 1, M_TEMP, M_WAITOK); + if (!p) { + return ENOMEM; + } error = SYSCTL_IN(req, p, req->newlen); if (error) { FREE(p, M_TEMP); - return (error); + return error; } - p [req->newlen] = '\0'; + p[req->newlen] = '\0'; /* * Note: We acquire and release the geometry lock here to @@ -990,15 +1115,16 @@ sysctl_sysctl_name2oid(__unused struct sysctl_oid *oidp, __unused void *arg1, FREE(p, M_TEMP); - if (error) - return (error); + if (error) { + return error; + } error = SYSCTL_OUT(req, oid, len * sizeof *oid); - return (error); + return error; } -SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY|CTLFLAG_KERN | CTLFLAG_LOCKED, 0, 0, - sysctl_sysctl_name2oid, "I", ""); +SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_KERN | CTLFLAG_LOCKED, 0, 0, + sysctl_sysctl_name2oid, "I", ""); /* * sysctl_sysctl_oidfmt @@ -1039,10 +1165,10 @@ SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY|CTLFLAG_KERN | CTLF */ STATIC int sysctl_sysctl_oidfmt(__unused struct sysctl_oid *oidp, void *arg1, int arg2, - struct sysctl_req *req) + struct sysctl_req *req) { int *name = (int *) arg1; - int error = ENOENT; /* default error: not found */ + int error = ENOENT; /* default error: not found */ u_int namelen = arg2; u_int indx; struct sysctl_oid *oid; @@ -1056,10 +1182,12 @@ sysctl_sysctl_oidfmt(__unused struct sysctl_oid *oidp, void *arg1, int arg2, if (oid->oid_number == name[indx]) { indx++; if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { - if (oid->oid_handler) + if (oid->oid_handler) { goto found; - if (indx == namelen) + } + if (indx == namelen) { goto found; + } lsp = (struct sysctl_oid_list *)oid->oid_arg1; oid = SLIST_FIRST(lsp); } else { @@ -1077,16 +1205,18 @@ sysctl_sysctl_oidfmt(__unused struct sysctl_oid *oidp, void *arg1, int arg2, goto err; found: - if (!oid->oid_fmt) + if (!oid->oid_fmt) { goto err; - error = SYSCTL_OUT(req, - &oid->oid_kind, sizeof(oid->oid_kind)); - if (!error) - error = SYSCTL_OUT(req, oid->oid_fmt, - strlen(oid->oid_fmt)+1); + } + error = SYSCTL_OUT(req, + &oid->oid_kind, sizeof(oid->oid_kind)); + if (!error) { + error = SYSCTL_OUT(req, oid->oid_fmt, + strlen(oid->oid_fmt) + 1); + } err: lck_rw_done(sysctl_geometry_lock); - return (error); + return error; } SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_oidfmt, ""); @@ -1105,7 +1235,7 @@ SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_sysctl_oidfm int sysctl_handle_int(__unused struct sysctl_oid *oidp, void *arg1, int arg2, - struct sysctl_req *req) + struct sysctl_req *req) { return sysctl_io_number(req, arg1? *(int*)arg1: arg2, sizeof(int), arg1, NULL); } @@ -1116,10 +1246,11 @@ sysctl_handle_int(__unused struct sysctl_oid *oidp, void *arg1, int arg2, int sysctl_handle_long(__unused struct sysctl_oid *oidp, void *arg1, - __unused int arg2, struct sysctl_req *req) + __unused int arg2, struct sysctl_req *req) { - if (!arg1) - return (EINVAL); + if (!arg1) { + return EINVAL; + } return sysctl_io_number(req, *(long*)arg1, sizeof(long), arg1, NULL); } @@ -1129,10 +1260,11 @@ sysctl_handle_long(__unused struct sysctl_oid *oidp, void *arg1, int sysctl_handle_quad(__unused struct sysctl_oid *oidp, void *arg1, - __unused int arg2, struct sysctl_req *req) + __unused int arg2, struct sysctl_req *req) { - if (!arg1) - return (EINVAL); + if (!arg1) { + return EINVAL; + } return sysctl_io_number(req, *(long long*)arg1, sizeof(long long), arg1, NULL); } @@ -1145,19 +1277,21 @@ sysctl_handle_quad(__unused struct sysctl_oid *oidp, void *arg1, */ int sysctl_handle_int2quad(__unused struct sysctl_oid *oidp, void *arg1, - __unused int arg2, struct sysctl_req *req) + __unused int arg2, struct sysctl_req *req) { int error = 0; long long val; int newval; - if (!arg1) - return (EINVAL); + if (!arg1) { + return EINVAL; + } val = (long long)*(int *)arg1; error = SYSCTL_OUT(req, &val, sizeof(long long)); - if (error || !req->newptr) - return (error); + if (error || !req->newptr) { + return error; + } error = SYSCTL_IN(req, &val, sizeof(long long)); if (!error) { @@ -1172,19 +1306,19 @@ sysctl_handle_int2quad(__unused struct sysctl_oid *oidp, void *arg1, *(int *)arg1 = newval; } } - return (error); + return error; } /* * Handle our generic '\0' terminated 'C' string. * Two cases: - * a variable string: point arg1 at it, arg2 is max length. - * a constant string: point arg1 at it, arg2 is zero. + * a variable string: point arg1 at it, arg2 is max length. + * a constant string: point arg1 at it, arg2 is zero. */ int sysctl_handle_string( __unused struct sysctl_oid *oidp, void *arg1, int arg2, - struct sysctl_req *req) + struct sysctl_req *req) { return sysctl_io_string(req, arg1, arg2, 0, NULL); } @@ -1196,7 +1330,7 @@ sysctl_handle_string( __unused struct sysctl_oid *oidp, void *arg1, int arg2, int sysctl_handle_opaque(__unused struct sysctl_oid *oidp, void *arg1, int arg2, - struct sysctl_req *req) + struct sysctl_req *req) { return sysctl_io_opaque(req, arg1, arg2, NULL); } @@ -1211,27 +1345,32 @@ sysctl_old_kernel(struct sysctl_req *req, const void *p, size_t l) if (req->oldptr) { i = l; - if (i > req->oldlen - req->oldidx) + if (i > req->oldlen - req->oldidx) { i = req->oldlen - req->oldidx; - if (i > 0) + } + if (i > 0) { bcopy((const void*)p, CAST_DOWN(char *, (req->oldptr + req->oldidx)), i); + } } req->oldidx += l; - if (req->oldptr && i != l) - return (ENOMEM); - return (0); + if (req->oldptr && i != l) { + return ENOMEM; + } + return 0; } STATIC int sysctl_new_kernel(struct sysctl_req *req, void *p, size_t l) { - if (!req->newptr) + if (!req->newptr) { return 0; - if (req->newlen - req->newidx < l) - return (EINVAL); + } + if (req->newlen - req->newidx < l) { + return EINVAL; + } bcopy(CAST_DOWN(char *, (req->newptr + req->newidx)), p, l); req->newidx += l; - return (0); + return 0; } int @@ -1245,10 +1384,12 @@ kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldle */ bzero(&req, sizeof req); req.p = p; - if (oldlenp) + if (oldlenp) { req.oldlen = *oldlenp; - if (old) + } + if (old) { req.oldptr = CAST_USER_ADDR_T(old); + } if (newlen) { req.newlen = newlen; req.newptr = CAST_USER_ADDR_T(new); @@ -1260,13 +1401,15 @@ kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldle /* make the request */ error = sysctl_root(TRUE, FALSE, NULL, 0, name, namelen, &req); - if (error && error != ENOMEM) - return (error); + if (error && error != ENOMEM) { + return error; + } - if (oldlenp) + if (oldlenp) { *oldlenp = req.oldidx; + } - return (error); + return error; } /* @@ -1279,20 +1422,25 @@ sysctl_old_user(struct sysctl_req *req, const void *p, size_t l) size_t i = 0; if (req->oldptr) { - if (req->oldlen - req->oldidx < l) - return (ENOMEM); + if (req->oldlen - req->oldidx < l) { + return ENOMEM; + } i = l; - if (i > req->oldlen - req->oldidx) + if (i > req->oldlen - req->oldidx) { i = req->oldlen - req->oldidx; - if (i > 0) + } + if (i > 0) { error = copyout((const void*)p, (req->oldptr + req->oldidx), i); + } } req->oldidx += l; - if (error) - return (error); - if (req->oldptr && i < l) - return (ENOMEM); - return (0); + if (error) { + return error; + } + if (req->oldptr && i < l) { + return ENOMEM; + } + return 0; } STATIC int @@ -1300,13 +1448,15 @@ sysctl_new_user(struct sysctl_req *req, void *p, size_t l) { int error; - if (!req->newptr) + if (!req->newptr) { return 0; - if (req->newlen - req->newidx < l) - return (EINVAL); + } + if (req->newlen - req->newidx < l) { + return EINVAL; + } error = copyin((req->newptr + req->newidx), p, l); req->newidx += l; - return (error); + return error; } /* @@ -1321,6 +1471,7 @@ sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestri int i; struct sysctl_oid *oid; struct sysctl_oid_list *lsp = &sysctl__children; + sysctl_handler_t oid_handler = NULL; int error; boolean_t unlocked_node_found = FALSE; boolean_t namestring_started = FALSE; @@ -1335,13 +1486,12 @@ sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestri goto err; } } - + oid = SLIST_FIRST(lsp); indx = 0; while (oid && indx < CTL_MAXNAME) { if (oid->oid_number == name[indx]) { - if (!from_kernel && !string_is_canonical) { if (namestring_started) { if (strlcat(namestring, ".", namestringlen) >= namestringlen) { @@ -1356,14 +1506,14 @@ sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestri } namestring_started = TRUE; } - + indx++; - if (!(oid->oid_kind & CTLFLAG_LOCKED)) - { + if (!(oid->oid_kind & CTLFLAG_LOCKED)) { unlocked_node_found = TRUE; } - if (oid->oid_kind & CTLFLAG_NOLOCK) + if (oid->oid_kind & CTLFLAG_NOLOCK) { req->lock = 0; + } /* * For SYSCTL_PROC() functions which are for sysctl's * which have parameters at the end of their OID @@ -1377,10 +1527,10 @@ sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestri * will become unsupported. */ if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { - if (oid->oid_handler) + if (oid->oid_handler) { goto found; - if (indx == namelen) - { + } + if (indx == namelen) { error = ENOENT; goto err; } @@ -1388,8 +1538,7 @@ sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestri lsp = (struct sysctl_oid_list *)oid->oid_arg1; oid = SLIST_FIRST(lsp); } else { - if (indx != namelen) - { + if (indx != namelen) { error = EISDIR; goto err; } @@ -1402,7 +1551,7 @@ sysctl_root(boolean_t from_kernel, boolean_t string_is_canonical, char *namestri error = ENOENT; goto err; found: - + /* * indx is the index of the first remaining OID name, * for sysctls that take them as arguments @@ -1410,17 +1559,17 @@ found: if (!from_kernel && !string_is_canonical && (indx < namelen)) { char tempbuf[10]; u_int indx2; - + for (indx2 = indx; indx2 < namelen; indx2++) { - snprintf(tempbuf, sizeof(tempbuf), "%d",name[indx2]); - + snprintf(tempbuf, sizeof(tempbuf), "%d", name[indx2]); + if (namestring_started) { if (strlcat(namestring, ".", namestringlen) >= namestringlen) { error = ENAMETOOLONG; goto err; } } - + if (strlcat(namestring, tempbuf, namestringlen) >= namestringlen) { error = ENAMETOOLONG; goto err; @@ -1428,10 +1577,10 @@ found: namestring_started = TRUE; } } - + /* If writing isn't allowed */ if (req->newptr && (!(oid->oid_kind & CTLFLAG_WR) || - ((oid->oid_kind & CTLFLAG_SECURE) && securelevel > 0))) { + ((oid->oid_kind & CTLFLAG_SECURE) && securelevel > 0))) { error = (EPERM); goto err; } @@ -1439,8 +1588,7 @@ found: /* * If we're inside the kernel, the OID must be marked as kernel-valid. */ - if (from_kernel && !(oid->oid_kind & CTLFLAG_KERN)) - { + if (from_kernel && !(oid->oid_kind & CTLFLAG_KERN)) { error = (EPERM); goto err; } @@ -1455,11 +1603,17 @@ found: */ if (!(oid->oid_kind & CTLFLAG_ANYBODY) && req->newptr && req->p && - (error = proc_suser(req->p))) + (error = proc_suser(req->p))) { goto err; + } - if (!oid->oid_handler) { - error = EINVAL; + /* + * sysctl_unregister_oid() may change the handler value, so grab it + * under the lock. + */ + oid_handler = oid->oid_handler; + if (!oid_handler) { + error = EINVAL; goto err; } @@ -1476,40 +1630,45 @@ found: #if CONFIG_MACF if (!from_kernel) { error = mac_system_check_sysctlbyname(kauth_cred_get(), - namestring, - name, - namelen, - req->oldptr, - req->oldlen, - req->newptr, - req->newlen); - if (error) + namestring, + name, + namelen, + req->oldptr, + req->oldlen, + req->newptr, + req->newlen); + if (error) { goto dropref; + } } #endif - + /* * ...however, we still have to grab the mutex for those calls which * may be into code whose reentrancy is protected by it. */ - if (unlocked_node_found) - { + if (unlocked_node_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_function(oid_handler, + ptrauth_key_function_pointer, + ptrauth_blend_discriminator(&oid->oid_handler, + ptrauth_string_discriminator("oid_handler"))); +#endif /* defined(HAS_APPLE_PAC) */ + if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) { - i = (oid->oid_handler) (oid, - name + indx, namelen - indx, - req); + i = oid_handler(oid, name + indx, namelen - indx, req); } else { - i = (oid->oid_handler) (oid, - oid->oid_arg1, oid->oid_arg2, - req); + i = oid_handler(oid, oid->oid_arg1, oid->oid_arg2, req); } error = i; - if (unlocked_node_found) - { + if (unlocked_node_found) { lck_mtx_unlock(sysctl_unlocked_node_lock); } @@ -1533,29 +1692,31 @@ dropref: * OIDs. */ lck_rw_lock_shared(sysctl_geometry_lock); - if (OSAddAtomic(-1, &oid->oid_refcnt) == 1) + if (OSAddAtomic(-1, &oid->oid_refcnt) == 1) { wakeup(&oid->oid_refcnt); + } err: lck_rw_done(sysctl_geometry_lock); - return (error); + return error; } -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) +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) { bzero(req, sizeof(*req)); - + req->p = p; - + req->oldlen = oldlen; req->oldptr = oldp; - + if (newlen) { req->newlen = newlen; req->newptr = newp; } - + req->oldfunc = sysctl_old_user; req->newfunc = sysctl_new_user; req->lock = 1; @@ -1566,40 +1727,44 @@ void sysctl_create_user_req(struct sysctl_req *req, struct proc *p, user_addr_t 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; char *namestring; size_t namestringlen = MAXPATHLEN; - + /* * all top-level sysctl names are non-terminal */ - if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) - return (EINVAL); + if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) { + return EINVAL; + } error = copyin(uap->name, &name[0], uap->namelen * sizeof(int)); - if (error) - return (error); - + if (error) { + return error; + } + AUDIT_ARG(ctlname, name, uap->namelen); - - if (uap->newlen > SIZE_T_MAX) - return (EINVAL); + + if (uap->newlen > SIZE_T_MAX) { + return EINVAL; + } newlen = (size_t)uap->newlen; - + if (uap->oldlenp != USER_ADDR_NULL) { - uint64_t oldlen64 = fuulong(uap->oldlenp); + uint64_t oldlen64 = fuulong(uap->oldlenp); /* * If more than 4G, clamp to 4G */ - if (oldlen64 > SIZE_T_MAX) + if (oldlen64 > SIZE_T_MAX) { oldlen = SIZE_T_MAX; - else + } else { oldlen = (size_t)oldlen64; + } } - + sysctl_create_user_req(&req, p, uap->old, oldlen, uap->new, newlen); /* Guess that longest length for the passed-in MIB, if we can be more aggressive than MAXPATHLEN */ @@ -1609,88 +1774,111 @@ sysctl(proc_t p, struct sysctl_args *uap, __unused int32_t *retval) } else if (name[0] == CTL_HW && name[1] < HW_MAXID) { namestringlen = 32; /* "hw.cachelinesize_compat" */ } - } + } MALLOC(namestring, char *, namestringlen, M_TEMP, M_WAITOK); if (!namestring) { - oldlen = 0; - goto err; + oldlen = 0; + goto err; } error = userland_sysctl(FALSE, namestring, namestringlen, name, uap->namelen, &req, &oldlen); - + FREE(namestring, M_TEMP); - - if ((error) && (error != ENOMEM)) - return (error); - + + if ((error) && (error != ENOMEM)) { + return error; + } + err: - if (uap->oldlenp != USER_ADDR_NULL) - error = suulong(uap->oldlenp, oldlen); - - return (error); + if (uap->oldlenp != USER_ADDR_NULL) { + /* + * 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; struct sysctl_req req; int oid[CTL_MAXNAME]; - if (uap->namelen >= MAXPATHLEN) /* XXX arbitrary, undocumented */ - return (ENAMETOOLONG); + if (uap->namelen >= MAXPATHLEN) { /* XXX arbitrary, undocumented */ + return ENAMETOOLONG; + } namelen = (size_t)uap->namelen; - - MALLOC(name, char *, namelen+1, M_TEMP, M_WAITOK); - if (!name) - return ENOMEM; + + MALLOC(name, char *, namelen + 1, M_TEMP, M_WAITOK); + if (!name) { + return ENOMEM; + } error = copyin(uap->name, name, namelen); if (error) { FREE(name, M_TEMP); - return (error); + return error; } name[namelen] = '\0'; /* XXX * AUDIT_ARG(ctlname, name, uap->namelen); */ - + if (uap->newlen > SIZE_T_MAX) { FREE(name, M_TEMP); - return (EINVAL); + return EINVAL; } newlen = (size_t)uap->newlen; - + if (uap->oldlenp != USER_ADDR_NULL) { - uint64_t oldlen64 = fuulong(uap->oldlenp); - + uint64_t oldlen64 = fuulong(uap->oldlenp); + /* * If more than 4G, clamp to 4G */ - if (oldlen64 > SIZE_T_MAX) + if (oldlen64 > SIZE_T_MAX) { oldlen = SIZE_T_MAX; - else + } else { oldlen = (size_t)oldlen64; + } } - + sysctl_create_user_req(&req, p, uap->old, oldlen, uap->new, newlen); - error = userland_sysctl(TRUE, name, namelen+1, oid, CTL_MAXNAME, &req, &oldlen); - + error = userland_sysctl(TRUE, name, namelen + 1, oid, CTL_MAXNAME, &req, &oldlen); + FREE(name, M_TEMP); - if ((error) && (error != ENOMEM)) - return (error); - - if (uap->oldlenp != USER_ADDR_NULL) - error = suulong(uap->oldlenp, oldlen); - - return (error); + if ((error) && (error != ENOMEM)) { + return error; + } + + if (uap->oldlenp != USER_ADDR_NULL) { + /* + * Only overwrite the old error value on a new error + */ + new_error = suulong(uap->oldlenp, oldlen); + + if (new_error) { + error = new_error; + } + } + + return error; } /* @@ -1699,32 +1887,35 @@ sysctlbyname(proc_t p, struct sysctlbyname_args *uap, __unused int32_t *retval) */ int userland_sysctl(boolean_t string_is_canonical, - char *namestring, size_t namestringlen, - int *name, u_int namelen, struct sysctl_req *req, - size_t *retval) + char *namestring, size_t namestringlen, + int *name, u_int namelen, struct sysctl_req *req, + size_t *retval) { int error = 0; struct sysctl_req req2; do { - /* if EAGAIN, reset output cursor */ - req2 = *req; - if (!string_is_canonical) - namestring[0] = '\0'; + /* if EAGAIN, reset output cursor */ + req2 = *req; + if (!string_is_canonical) { + namestring[0] = '\0'; + } - error = sysctl_root(FALSE, string_is_canonical, namestring, namestringlen, name, namelen, &req2); + error = sysctl_root(FALSE, string_is_canonical, namestring, namestringlen, name, namelen, &req2); } while (error == EAGAIN); - if (error && error != ENOMEM) - return (error); + if (error && error != ENOMEM) { + return error; + } if (retval) { - if (req2.oldptr && req2.oldidx > req2.oldlen) + if (req2.oldptr && req2.oldidx > req2.oldlen) { *retval = req2.oldlen; - else + } else { *retval = req2.oldidx; + } } - return (error); + return error; } /* @@ -1754,10 +1945,10 @@ 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); - + /* now use the OID */ - if (error == 0) + if (error == 0) { error = kernel_sysctl(current_proc(), oid, oidlen, oldp, oldlenp, newp, newlen); - return(error); + } + return error; } -