kernel: fix errno access for user mode

The errno "variable" is required to be thread-specific.
It gets defined to a macro which dereferences a pointer
returned by a kernel function.

In user mode, we cannot simply read/write the thread struct.
We do not have thread-local storage mechanism, so for now
use the lowest address of the thread stack to store this
value, since this is guaranteed to be read/writable by
a user thread.

The downside of this approach is potential stack corruption
if the stack pointer goes down this far but does not exceed
the location, since a fault won't be generated in this case.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2018-07-19 11:09:33 -07:00 committed by Andrew Boie
commit 7f4d006959
6 changed files with 69 additions and 5 deletions

View file

@ -538,9 +538,17 @@ struct k_thread {
#endif #endif
#ifdef CONFIG_ERRNO #ifdef CONFIG_ERRNO
#ifdef CONFIG_USERSPACE
/* Set to the lowest area in the thread stack since this needs to
* be directly read/writable by user mode. Not ideal, but best we
* can do until we have thread-local storage
*/
int *errno_location;
#else
/** per-thread errno variable */ /** per-thread errno variable */
int errno_var; int errno_var;
#endif #endif
#endif
#if defined(CONFIG_THREAD_STACK_INFO) #if defined(CONFIG_THREAD_STACK_INFO)
/** Stack Info */ /** Stack Info */

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MISC_ERRNO_PRIVATE_H_
#define _MISC_ERRNO_PRIVATE_H_
#include <toolchain.h>
#ifdef __cplusplus
extern "C" {
#endif
/* NOTE: located here to avoid include dependency loops between errno.h
* and kernel.h
*/
/**
* return a pointer to a memory location containing errno
*
* errno is thread-specific, and can't just be a global. This pointer
* is guaranteed to be read/writable from user mode.
*
* @return Memory location of errno data for current thread
*/
__syscall int *z_errno(void);
#ifdef __cplusplus
}
#endif
#include <syscalls/errno_private.h>
#endif /* _MISC_ERRNO_PRIVATE_H */

View file

@ -13,6 +13,7 @@
*/ */
#include <kernel_structs.h> #include <kernel_structs.h>
#include <syscall_handler.h>
/* /*
* Define _k_neg_eagain for use in assembly files as errno.h is * Define _k_neg_eagain for use in assembly files as errno.h is
@ -22,8 +23,20 @@
const int _k_neg_eagain = -EAGAIN; const int _k_neg_eagain = -EAGAIN;
#ifdef CONFIG_ERRNO #ifdef CONFIG_ERRNO
int *__errno(void) #ifdef CONFIG_USERSPACE
int *_impl_z_errno(void)
{
/* Initialized to the lowest address in the stack so the thread can
* directly read/write it
*/
return _current->errno_location;
}
Z_SYSCALL_HANDLER0_SIMPLE(z_errno);
#else
int *_impl_z_errno(void)
{ {
return &_current->errno_var; return &_current->errno_var;
} }
#endif #endif /* CONFIG_USERSPACE */
#endif /* CONFIG_ERRNO */

View file

@ -312,6 +312,7 @@ void _setup_new_thread(struct k_thread *new_thread,
_k_object_init(new_thread); _k_object_init(new_thread);
_k_object_init(stack); _k_object_init(stack);
new_thread->stack_obj = stack; new_thread->stack_obj = stack;
new_thread->errno_location = (int *)K_THREAD_STACK_BUFFER(stack);
/* Any given thread has access to itself */ /* Any given thread has access to itself */
k_object_access_grant(new_thread, new_thread); k_object_access_grant(new_thread, new_thread);

View file

@ -17,13 +17,13 @@
#ifndef __INCerrnoh #ifndef __INCerrnoh
#define __INCerrnoh #define __INCerrnoh
#include <misc/errno_private.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#define errno (*z_errno())
extern int *__errno(void);
#define errno (*__errno())
/* /*
* POSIX Error codes * POSIX Error codes

View file

@ -11,6 +11,7 @@
#include <linker/linker-defs.h> #include <linker/linker-defs.h>
#include <misc/util.h> #include <misc/util.h>
#include <kernel_internal.h> #include <kernel_internal.h>
#include <misc/errno_private.h>
#define USED_RAM_END_ADDR POINTER_TO_UINT(&_end) #define USED_RAM_END_ADDR POINTER_TO_UINT(&_end)
@ -178,3 +179,8 @@ void z_newlib_get_heap_bounds(void **base, size_t *size)
*base = heap_base; *base = heap_base;
*size = MAX_HEAP_SIZE; *size = MAX_HEAP_SIZE;
} }
int *__errno(void)
{
return z_errno();
}