zephyr/kernel/userspace.c

197 lines
4.7 KiB
C
Raw Normal View History

kernel: introduce object validation mechanism All system calls made from userspace which involve pointers to kernel objects (including device drivers) will need to have those pointers validated; userspace should never be able to crash the kernel by passing it garbage. The actual validation with _k_object_validate() will be in the system call receiver code, which doesn't exist yet. - CONFIG_USERSPACE introduced. We are somewhat far away from having an end-to-end implementation, but at least need a Kconfig symbol to guard the incoming code with. Formal documentation doesn't exist yet either, but will appear later down the road once the implementation is mostly finalized. - In the memory region for RAM, the data section has been moved last, past bss and noinit. This ensures that inserting generated tables with addresses of kernel objects does not change the addresses of those objects (which would make the table invalid) - The DWARF debug information in the generated ELF binary is parsed to fetch the locations of all kernel objects and pass this to gperf to create a perfect hash table of their memory addresses. - The generated gperf code doesn't know that we are exclusively working with memory addresses and uses memory inefficently. A post-processing script process_gperf.py adjusts the generated code before it is compiled to work with pointer values directly and not strings containing them. - _k_object_init() calls inserted into the init functions for the set of kernel object types we are going to support so far Issue: ZEP-2187 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-08-22 22:15:23 +02:00
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <string.h>
#include <misc/printk.h>
#include <kernel_structs.h>
#include <sys_io.h>
#include <ksched.h>
kernel: introduce object validation mechanism All system calls made from userspace which involve pointers to kernel objects (including device drivers) will need to have those pointers validated; userspace should never be able to crash the kernel by passing it garbage. The actual validation with _k_object_validate() will be in the system call receiver code, which doesn't exist yet. - CONFIG_USERSPACE introduced. We are somewhat far away from having an end-to-end implementation, but at least need a Kconfig symbol to guard the incoming code with. Formal documentation doesn't exist yet either, but will appear later down the road once the implementation is mostly finalized. - In the memory region for RAM, the data section has been moved last, past bss and noinit. This ensures that inserting generated tables with addresses of kernel objects does not change the addresses of those objects (which would make the table invalid) - The DWARF debug information in the generated ELF binary is parsed to fetch the locations of all kernel objects and pass this to gperf to create a perfect hash table of their memory addresses. - The generated gperf code doesn't know that we are exclusively working with memory addresses and uses memory inefficently. A post-processing script process_gperf.py adjusts the generated code before it is compiled to work with pointer values directly and not strings containing them. - _k_object_init() calls inserted into the init functions for the set of kernel object types we are going to support so far Issue: ZEP-2187 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-08-22 22:15:23 +02:00
/**
* Kernel object validation function
*
* Retrieve metadata for a kernel object. This function is implemented in
* the gperf script footer, see gen_kobject_list.py
*
* @param obj Address of kernel object to get metadata
* @return Kernel object's metadata, or NULL if the parameter wasn't the
* memory address of a kernel object
*/
extern struct _k_object *_k_object_find(void *obj);
const char *otype_to_str(enum k_objects otype)
{
/* -fdata-sections doesn't work right except in very very recent
* GCC and these literal strings would appear in the binary even if
* otype_to_str was omitted by the linker
*/
#ifdef CONFIG_PRINTK
switch (otype) {
case K_OBJ_ALERT:
return "k_alert";
case K_OBJ_DELAYED_WORK:
return "k_delayed_work";
case K_OBJ_MEM_SLAB:
return "k_mem_slab";
case K_OBJ_MSGQ:
return "k_msgq";
case K_OBJ_MUTEX:
return "k_mutex";
case K_OBJ_PIPE:
return "k_pipe";
case K_OBJ_SEM:
return "k_sem";
case K_OBJ_STACK:
return "k_stack";
case K_OBJ_THREAD:
return "k_thread";
case K_OBJ_TIMER:
return "k_timer";
case K_OBJ_WORK:
return "k_work";
case K_OBJ_WORK_Q:
return "k_work_q";
default:
return "?";
}
#else
ARG_UNUSED(otype);
return NULL;
#endif
}
/* Stub functions, to be filled in forthcoming patch sets */
static void set_thread_perms(struct _k_object *ko, struct k_thread *thread)
{
if (thread->base.perm_index < 8 * CONFIG_MAX_THREAD_BYTES) {
sys_bitfield_set_bit((mem_addr_t)&ko->perms,
thread->base.perm_index);
}
kernel: introduce object validation mechanism All system calls made from userspace which involve pointers to kernel objects (including device drivers) will need to have those pointers validated; userspace should never be able to crash the kernel by passing it garbage. The actual validation with _k_object_validate() will be in the system call receiver code, which doesn't exist yet. - CONFIG_USERSPACE introduced. We are somewhat far away from having an end-to-end implementation, but at least need a Kconfig symbol to guard the incoming code with. Formal documentation doesn't exist yet either, but will appear later down the road once the implementation is mostly finalized. - In the memory region for RAM, the data section has been moved last, past bss and noinit. This ensures that inserting generated tables with addresses of kernel objects does not change the addresses of those objects (which would make the table invalid) - The DWARF debug information in the generated ELF binary is parsed to fetch the locations of all kernel objects and pass this to gperf to create a perfect hash table of their memory addresses. - The generated gperf code doesn't know that we are exclusively working with memory addresses and uses memory inefficently. A post-processing script process_gperf.py adjusts the generated code before it is compiled to work with pointer values directly and not strings containing them. - _k_object_init() calls inserted into the init functions for the set of kernel object types we are going to support so far Issue: ZEP-2187 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-08-22 22:15:23 +02:00
}
static int test_thread_perms(struct _k_object *ko)
{
if (_current->base.perm_index < 8 * CONFIG_MAX_THREAD_BYTES) {
return sys_bitfield_test_bit((mem_addr_t)&ko->perms,
_current->base.perm_index);
}
return 0;
kernel: introduce object validation mechanism All system calls made from userspace which involve pointers to kernel objects (including device drivers) will need to have those pointers validated; userspace should never be able to crash the kernel by passing it garbage. The actual validation with _k_object_validate() will be in the system call receiver code, which doesn't exist yet. - CONFIG_USERSPACE introduced. We are somewhat far away from having an end-to-end implementation, but at least need a Kconfig symbol to guard the incoming code with. Formal documentation doesn't exist yet either, but will appear later down the road once the implementation is mostly finalized. - In the memory region for RAM, the data section has been moved last, past bss and noinit. This ensures that inserting generated tables with addresses of kernel objects does not change the addresses of those objects (which would make the table invalid) - The DWARF debug information in the generated ELF binary is parsed to fetch the locations of all kernel objects and pass this to gperf to create a perfect hash table of their memory addresses. - The generated gperf code doesn't know that we are exclusively working with memory addresses and uses memory inefficently. A post-processing script process_gperf.py adjusts the generated code before it is compiled to work with pointer values directly and not strings containing them. - _k_object_init() calls inserted into the init functions for the set of kernel object types we are going to support so far Issue: ZEP-2187 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-08-22 22:15:23 +02:00
}
kernel: introduce object validation mechanism All system calls made from userspace which involve pointers to kernel objects (including device drivers) will need to have those pointers validated; userspace should never be able to crash the kernel by passing it garbage. The actual validation with _k_object_validate() will be in the system call receiver code, which doesn't exist yet. - CONFIG_USERSPACE introduced. We are somewhat far away from having an end-to-end implementation, but at least need a Kconfig symbol to guard the incoming code with. Formal documentation doesn't exist yet either, but will appear later down the road once the implementation is mostly finalized. - In the memory region for RAM, the data section has been moved last, past bss and noinit. This ensures that inserting generated tables with addresses of kernel objects does not change the addresses of those objects (which would make the table invalid) - The DWARF debug information in the generated ELF binary is parsed to fetch the locations of all kernel objects and pass this to gperf to create a perfect hash table of their memory addresses. - The generated gperf code doesn't know that we are exclusively working with memory addresses and uses memory inefficently. A post-processing script process_gperf.py adjusts the generated code before it is compiled to work with pointer values directly and not strings containing them. - _k_object_init() calls inserted into the init functions for the set of kernel object types we are going to support so far Issue: ZEP-2187 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-08-22 22:15:23 +02:00
void k_object_grant_access(void *object, struct k_thread *thread)
{
struct _k_object *ko = _k_object_find(object);
if (!ko) {
if (_is_thread_user()) {
printk("granting access to non-existent kernel object %p\n",
object);
k_oops();
} else {
/* Supervisor threads may at times instantiate objects
* that ignore rules on where they can live. Such
* objects won't ever be usable from userspace, but
* we shouldn't explode.
*/
return;
}
}
/* userspace can't grant access to objects unless it already has
* access to that object
*/
if (_is_thread_user() && !test_thread_perms(ko)) {
printk("insufficient permissions in current thread %p\n",
_current);
printk("Cannot grant access to %s %p for thread %p\n",
otype_to_str(ko->type), object, thread);
k_oops();
}
set_thread_perms(ko, thread);
}
int _k_object_validate(void *obj, enum k_objects otype, int init)
{
struct _k_object *ko;
ko = _k_object_find(obj);
if (!ko || ko->type != otype) {
printk("%p is not a %s\n", obj, otype_to_str(otype));
return -EBADF;
}
/* Uninitialized objects are not owned by anyone. However if an
* object is initialized, and the caller is from userspace, then
* we need to assert that the user thread has sufficient permissions
* to re-initialize.
*/
if (ko->flags & K_OBJ_FLAG_INITIALIZED && _is_thread_user() &&
!test_thread_perms(ko)) {
printk("thread %p does not have permission on %s %p\n",
_current, otype_to_str(otype), obj);
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)) {
printk("%p used before initialization\n", obj);
return -EINVAL;
}
return 0;
}
void _k_object_init(void *object)
{
struct _k_object *ko;
/* By the time we get here, if the caller was from userspace, all the
* necessary checks have been done in _k_object_validate(), which takes
* place before the object is initialized.
*
* This function runs after the object has been initialized and
* finalizes it
*/
ko = _k_object_find(object);
if (!ko) {
/* Supervisor threads can ignore rules about kernel objects
* and may declare them on stacks, etc. Such objects will never
* be usable from userspace, but we shouldn't explode.
*/
return;
}
memset(ko->perms, 0, CONFIG_MAX_THREAD_BYTES);
set_thread_perms(ko, _current);
ko->flags |= K_OBJ_FLAG_INITIALIZED;
}
u32_t _k_syscall_entry(u32_t arg1, u32_t arg2, u32_t arg3, u32_t arg4,
u32_t arg5, u32_t call_id)
{
/* A real implementation will figure out what function to call
* based on call_id, validate arguments, perform any other runtime
* checks needed, and call into the appropriate kernel function.
*/
__ASSERT(0, "system calls are unimplemented");
return 0;
}