/*
- * 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;
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);
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,
* 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.
* 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) {
}
}
+#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;
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
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) {
}
}
+#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
/* 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
}
}
sysctl_unlocked_node_lock = lck_mtx_alloc_init(sysctl_lock_group, NULL);
sysctl_register_set("__sysctl_set");
+ sysctl_load_devicetree_entries();
}
/*
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 */
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';
* 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;
*oid++ = oidp->oid_number;
(*len)++;
- if (!i) {
+ if (i == '\0') {
return 0;
}
{
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;
}
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;
*/
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;
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);
}
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;
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;
}
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;
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;
}