diff --git a/kernel/include/syscall_handler.h b/kernel/include/syscall_handler.h index 236d4aa6f56..8065c60d8e5 100644 --- a/kernel/include/syscall_handler.h +++ b/kernel/include/syscall_handler.h @@ -17,6 +17,12 @@ extern const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT]; +enum _obj_init_check { + _OBJ_INIT_TRUE = 0, + _OBJ_INIT_FALSE = -1, + _OBJ_INIT_ANY = 1 +}; + /** * Ensure a system object is a valid object of the expected type * @@ -31,14 +37,15 @@ extern const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT]; * @param ko Kernel object metadata pointer, or NULL * @param otype Expected type of the kernel object, or K_OBJ_ANY if type * doesn't matter - * @param init If true, this is for an init function and we will not error - * out if the object is not initialized + * @param init Indicate whether the object needs to already be in initialized + * or uninitialized state, or that we don't care * @return 0 If the object is valid * -EBADF if not a valid object of the specified type * -EPERM If the caller does not have permissions * -EINVAL Object is not initialized */ -int _k_object_validate(struct _k_object *ko, enum k_objects otype, int init); +int _k_object_validate(struct _k_object *ko, enum k_objects otype, + enum _obj_init_check init); /** * Dump out error information on failed _k_object_validate() call @@ -101,6 +108,9 @@ extern void _thread_perms_clear(struct _k_object *ko, struct k_thread *thread); /* * Revoke access to all objects for the provided thread * + * NOTE: Unlike _thread_perms_clear(), this function will not clear + * permissions on public objects. + * * @param thread Thread object to revoke access */ extern void _thread_perms_all_clear(struct k_thread *thread); @@ -226,54 +236,71 @@ void _k_object_uninit(void *obj); #define _SYSCALL_MEMORY_ARRAY_WRITE(ptr, nmemb, size) \ _SYSCALL_MEMORY_ARRAY(ptr, nmemb, size, 1) -static inline int _obj_validation_check(void *obj, enum k_objects otype, - int init) +static inline int _obj_validation_check(struct _k_object *ko, + void *obj, + enum k_objects otype, + enum _obj_init_check init) { - struct _k_object *ko; int ret; - ko = _k_object_find(obj); ret = _k_object_validate(ko, otype, init); #ifdef CONFIG_PRINTK if (ret) { _dump_object_error(ret, obj, ko, otype); } +#else + ARG_UNUSED(obj); #endif return ret; } #define _SYSCALL_IS_OBJ(ptr, type, init) \ - _SYSCALL_VERIFY_MSG(!_obj_validation_check((void *)ptr, type, init), \ - "object %p access denied", (void *)(ptr)) + _SYSCALL_VERIFY_MSG( \ + !_obj_validation_check(_k_object_find((void *)ptr), (void *)ptr, \ + type, init), "access denied") /** * @brief Runtime check kernel object pointer for non-init functions * * Calls _k_object_validate and triggers a kernel oops if the check files. - * For use in system call handlers which are not init functions; a check - * enforcing that an object is initialized* will not occur. + * For use in system call handlers which are not init functions; a fatal + * error will occur if the object is not intiailized. * * @param ptr Untrusted kernel object pointer * @param type Expected kernel object type */ #define _SYSCALL_OBJ(ptr, type) \ - _SYSCALL_IS_OBJ(ptr, type, 0) + _SYSCALL_IS_OBJ(ptr, type, _OBJ_INIT_TRUE) /** * @brief Runtime check kernel object pointer for non-init functions * - * See description of _SYSCALL_IS_OBJ. For use in system call handlers which - * are not init functions; a check enforcing that an object is initialized - * will not occur. + * See description of _SYSCALL_IS_OBJ. No initialization checks are done. + * Intended for init functions where objects may be re-initialized at will. * * @param ptr Untrusted kernel object pointer * @param type Expected kernel object type */ #define _SYSCALL_OBJ_INIT(ptr, type) \ - _SYSCALL_IS_OBJ(ptr, type, 1) + _SYSCALL_IS_OBJ(ptr, type, _OBJ_INIT_ANY) + +/** + * @brief Runtime check kernel object pointer for non-init functions + * + * See description of _SYSCALL_IS_OBJ. Triggers a fatal error if the object is + * initialized. Intended for init functions where objects, once initialized, + * can only be re-used when their initialization state expires due to some + * other mechanism. + * + * @param ptr Untrusted kernel object pointer + * @param type Expected kernel object type + */ + +#define _SYSCALL_OBJ_NEVER_INIT(ptr, type) \ + _SYSCALL_IS_OBJ(ptr, type, _OBJ_INIT_FALSE) /* * Handler definition macros diff --git a/kernel/userspace.c b/kernel/userspace.c index 524790b50dd..67659292437 100644 --- a/kernel/userspace.c +++ b/kernel/userspace.c @@ -192,6 +192,8 @@ void _dump_object_error(int retval, void *obj, struct _k_object *ko, case -EINVAL: printk("%p used before initialization\n", obj); break; + case -EADDRINUSE: + printk("%p %s in use\n", obj, otype_to_str(otype)); } } @@ -222,24 +224,31 @@ void k_object_access_all_grant(void *object) } } -int _k_object_validate(struct _k_object *ko, enum k_objects otype, int init) +int _k_object_validate(struct _k_object *ko, enum k_objects otype, + enum _obj_init_check init) { - if (!ko || (otype != K_OBJ_ANY && ko->type != otype)) { + if (unlikely(!ko || (otype != K_OBJ_ANY && ko->type != otype))) { return -EBADF; } /* Manipulation of any kernel objects by a user thread requires that * thread be granted access first, even for uninitialized objects */ - if (!thread_perms_test(ko)) { + if (unlikely(!thread_perms_test(ko))) { return -EPERM; } - /* If we are not initializing an object, and the object is not - * initialized, we should freak out - */ - if (!init && !(ko->flags & K_OBJ_FLAG_INITIALIZED)) { - return -EINVAL; + /* Initialization state checks. _OBJ_INIT_ANY, we don't care */ + if (likely(init == _OBJ_INIT_TRUE)) { + /* Object MUST be intialized */ + if (unlikely(!(ko->flags & K_OBJ_FLAG_INITIALIZED))) { + return -EINVAL; + } + } else if (init < _OBJ_INIT_TRUE) { /* _OBJ_INIT_FALSE case */ + /* Object MUST NOT be initialized */ + if (unlikely(ko->flags & K_OBJ_FLAG_INITIALIZED)) { + return -EADDRINUSE; + } } return 0; diff --git a/kernel/userspace_handler.c b/kernel/userspace_handler.c index 8840e8b7ba1..3f2d9d048de 100644 --- a/kernel/userspace_handler.c +++ b/kernel/userspace_handler.c @@ -17,7 +17,7 @@ static struct _k_object *validate_any_object(void *obj) /* This can be any kernel object and it doesn't have to be * initialized */ - ret = _k_object_validate(ko, K_OBJ_ANY, 1); + ret = _k_object_validate(ko, K_OBJ_ANY, _OBJ_INIT_ANY); if (ret) { #ifdef CONFIG_PRINTK _dump_object_error(ret, obj, ko, K_OBJ_ANY); diff --git a/tests/kernel/mem_protect/obj_validation/src/main.c b/tests/kernel/mem_protect/obj_validation/src/main.c index 75fb5fffb2f..f91096c97a0 100644 --- a/tests/kernel/mem_protect/obj_validation/src/main.c +++ b/tests/kernel/mem_protect/obj_validation/src/main.c @@ -26,7 +26,8 @@ static int test_object(struct k_sem *sem, int retval) */ ret = _k_object_validate(_k_object_find(sem), K_OBJ_SEM, 0); } else { - ret = _obj_validation_check(sem, K_OBJ_SEM, 0); + ret = _obj_validation_check(_k_object_find(sem), sem, + K_OBJ_SEM, 0); } if (ret != retval) {