x86: de-couple user mode and HW stack protection
This is intended for memory-constrained systems and will save 4K per thread, since we will no longer reserve room for or activate a kernel stack guard page. If CONFIG_USERSPACE is enabled, stack overflows will still be caught in some situations: 1) User mode threads overflowing stack, since it crashes into the kernel stack page 2) Supervisor mode threads overflowing stack, since the kernel stack page is marked non-present for non-user threads Stack overflows will not be caught: 1) When handling a system call 2) When the interrupt stack overflows Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
b3227fa614
commit
2a8684f60c
5 changed files with 50 additions and 15 deletions
16
arch/Kconfig
16
arch/Kconfig
|
@ -42,18 +42,30 @@ config HW_STACK_PROTECTION
|
||||||
bool "Hardware Stack Protection"
|
bool "Hardware Stack Protection"
|
||||||
depends on ARCH_HAS_STACK_PROTECTION
|
depends on ARCH_HAS_STACK_PROTECTION
|
||||||
help
|
help
|
||||||
Select this option to enable hardware stack protection.
|
Select this option to enable hardware-based platform features to
|
||||||
|
catch stack overflows when the system is running in privileged
|
||||||
|
mode. If CONFIG_USERSPACE is not enabled, the system is always
|
||||||
|
running in privileged mode.
|
||||||
|
|
||||||
|
Note that this does not necessarily prevent corruptuon and assertions
|
||||||
|
about the overall system state when a fault is triggered cannot be
|
||||||
|
made.
|
||||||
|
|
||||||
config USERSPACE
|
config USERSPACE
|
||||||
bool "User mode threads (EXPERIMENTAL)"
|
bool "User mode threads (EXPERIMENTAL)"
|
||||||
depends on ARCH_HAS_USERSPACE
|
depends on ARCH_HAS_USERSPACE
|
||||||
select HW_STACK_PROTECTION
|
|
||||||
help
|
help
|
||||||
When enabled, threads may be created or dropped down to user mode,
|
When enabled, threads may be created or dropped down to user mode,
|
||||||
which has significantly restricted permissions and must interact
|
which has significantly restricted permissions and must interact
|
||||||
with the kernel via system calls. See Zephyr documentation for more
|
with the kernel via system calls. See Zephyr documentation for more
|
||||||
details about this feature.
|
details about this feature.
|
||||||
|
|
||||||
|
If a user thread overflows its stack, this will be caught and the
|
||||||
|
kernel itself will be shielded from harm. Enabling this option
|
||||||
|
may or may not catch stack overflows when the system is in
|
||||||
|
privileged mode or handling a system call; to ensure these are always
|
||||||
|
caught, enable CONFIG_HW_STACK_PROTECTION.
|
||||||
|
|
||||||
This feature is under heavy development and APIs related to it are
|
This feature is under heavy development and APIs related to it are
|
||||||
subject to change, even if declared non-private.
|
subject to change, even if declared non-private.
|
||||||
|
|
||||||
|
|
|
@ -96,11 +96,21 @@ config X86_PAE_MODE
|
||||||
Note: Do not enable in RAM constrained devices.
|
Note: Do not enable in RAM constrained devices.
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
config X86_ENABLE_TSS
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
This hidden option enables defining a Task State Segment (TSS) for
|
||||||
|
kernel execution. This is needed to handle double-faults or
|
||||||
|
do privilege elevation. It also defines a special TSS and handler
|
||||||
|
for correctly handling double-fault exceptions, instead of just
|
||||||
|
letting the system triple-fault and reset.
|
||||||
|
|
||||||
config X86_STACK_PROTECTION
|
config X86_STACK_PROTECTION
|
||||||
bool
|
bool
|
||||||
default y if HW_STACK_PROTECTION
|
default y if HW_STACK_PROTECTION
|
||||||
select SET_GDT
|
select SET_GDT
|
||||||
select GDT_DYNAMIC
|
select GDT_DYNAMIC
|
||||||
|
select X86_ENABLE_TSS
|
||||||
help
|
help
|
||||||
This option leverages the MMU to cause a system fatal error if the
|
This option leverages the MMU to cause a system fatal error if the
|
||||||
bounds of the current process stack are overflowed. This is done
|
bounds of the current process stack are overflowed. This is done
|
||||||
|
@ -110,6 +120,9 @@ config X86_USERSPACE
|
||||||
bool
|
bool
|
||||||
default y if USERSPACE
|
default y if USERSPACE
|
||||||
select THREAD_STACK_INFO
|
select THREAD_STACK_INFO
|
||||||
|
select SET_GDT
|
||||||
|
select GDT_DYNAMIC
|
||||||
|
select X86_ENABLE_TSS
|
||||||
help
|
help
|
||||||
This option enables APIs to drop a thread's privileges down to ring 3,
|
This option enables APIs to drop a thread's privileges down to ring 3,
|
||||||
supporting user-level threads that are protected from each other and
|
supporting user-level threads that are protected from each other and
|
||||||
|
|
|
@ -375,7 +375,7 @@ __csSet:
|
||||||
|
|
||||||
#endif /* CONFIG_X86_MMU */
|
#endif /* CONFIG_X86_MMU */
|
||||||
|
|
||||||
#ifdef CONFIG_X86_STACK_PROTECTION
|
#if defined(CONFIG_X86_ENABLE_TSS)
|
||||||
mov $MAIN_TSS, %ax
|
mov $MAIN_TSS, %ax
|
||||||
ltr %ax
|
ltr %ax
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -65,7 +65,8 @@ FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#if defined(CONFIG_STACK_CANARIES) || defined(CONFIG_STACK_SENTINEL) || \
|
#if defined(CONFIG_STACK_CANARIES) || defined(CONFIG_STACK_SENTINEL) || \
|
||||||
defined(CONFIG_X86_STACK_PROTECTION)
|
defined(CONFIG_HW_STACK_PROTECTION) || \
|
||||||
|
defined(CONFIG_USERSPACE)
|
||||||
case _NANO_ERR_STACK_CHK_FAIL:
|
case _NANO_ERR_STACK_CHK_FAIL:
|
||||||
printk("***** Stack Check Fail! *****\n");
|
printk("***** Stack Check Fail! *****\n");
|
||||||
break;
|
break;
|
||||||
|
@ -211,7 +212,7 @@ EXC_FUNC_NOCODE(IV_OVERFLOW);
|
||||||
EXC_FUNC_NOCODE(IV_BOUND_RANGE);
|
EXC_FUNC_NOCODE(IV_BOUND_RANGE);
|
||||||
EXC_FUNC_NOCODE(IV_INVALID_OPCODE);
|
EXC_FUNC_NOCODE(IV_INVALID_OPCODE);
|
||||||
EXC_FUNC_NOCODE(IV_DEVICE_NOT_AVAILABLE);
|
EXC_FUNC_NOCODE(IV_DEVICE_NOT_AVAILABLE);
|
||||||
#ifndef CONFIG_X86_STACK_PROTECTION
|
#ifndef CONFIG_X86_ENABLE_TSS
|
||||||
EXC_FUNC_NOCODE(IV_DOUBLE_FAULT);
|
EXC_FUNC_NOCODE(IV_DOUBLE_FAULT);
|
||||||
#endif
|
#endif
|
||||||
EXC_FUNC_CODE(IV_INVALID_TSS);
|
EXC_FUNC_CODE(IV_INVALID_TSS);
|
||||||
|
@ -294,7 +295,7 @@ FUNC_NORETURN void page_fault_handler(const NANO_ESF *pEsf)
|
||||||
_EXCEPTION_CONNECT_CODE(page_fault_handler, IV_PAGE_FAULT);
|
_EXCEPTION_CONNECT_CODE(page_fault_handler, IV_PAGE_FAULT);
|
||||||
#endif /* CONFIG_EXCEPTION_DEBUG */
|
#endif /* CONFIG_EXCEPTION_DEBUG */
|
||||||
|
|
||||||
#ifdef CONFIG_X86_STACK_PROTECTION
|
#ifdef CONFIG_X86_ENABLE_TSS
|
||||||
static __noinit volatile NANO_ESF _df_esf;
|
static __noinit volatile NANO_ESF _df_esf;
|
||||||
|
|
||||||
/* Very tiny stack; just enough for the bogus error code pushed by the CPU
|
/* Very tiny stack; just enough for the bogus error code pushed by the CPU
|
||||||
|
@ -396,4 +397,4 @@ static FUNC_NORETURN __used void _df_handler_top(void)
|
||||||
*/
|
*/
|
||||||
_X86_IDT_TSS_REGISTER(DF_TSS, -1, -1, IV_DOUBLE_FAULT, 0);
|
_X86_IDT_TSS_REGISTER(DF_TSS, -1, -1, IV_DOUBLE_FAULT, 0);
|
||||||
|
|
||||||
#endif /* CONFIG_X86_STACK_PROTECTION */
|
#endif /* CONFIG_X86_ENABLE_TSS */
|
||||||
|
|
|
@ -552,10 +552,11 @@ extern FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason,
|
||||||
const NANO_ESF * pEsf);
|
const NANO_ESF * pEsf);
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_STACK_PROTECTION
|
#ifdef CONFIG_X86_ENABLE_TSS
|
||||||
extern struct task_state_segment _main_tss;
|
extern struct task_state_segment _main_tss;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_X86_USERSPACE
|
#ifdef CONFIG_USERSPACE
|
||||||
/* Syscall invocation macros. x86-specific machine constraints used to ensure
|
/* Syscall invocation macros. x86-specific machine constraints used to ensure
|
||||||
* args land in the proper registers, see implementation of
|
* args land in the proper registers, see implementation of
|
||||||
* _x86_syscall_entry_stub in userspace.S
|
* _x86_syscall_entry_stub in userspace.S
|
||||||
|
@ -688,8 +689,12 @@ static inline int _arch_is_user_context(void)
|
||||||
|
|
||||||
return cs == USER_CODE_SEG;
|
return cs == USER_CODE_SEG;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
|
||||||
/* With userspace enabled, stacks are arranged as follows:
|
|
||||||
|
#if defined(CONFIG_HW_STACK_PROTECTION) && defined(CONFIG_USERSPACE)
|
||||||
|
/* With both hardware stack protection and userspace enabled, stacks are
|
||||||
|
* arranged as follows:
|
||||||
*
|
*
|
||||||
* High memory addresses
|
* High memory addresses
|
||||||
* +---------------+
|
* +---------------+
|
||||||
|
@ -711,14 +716,18 @@ static inline int _arch_is_user_context(void)
|
||||||
* All context switches will save/restore the esp0 field in the TSS.
|
* All context switches will save/restore the esp0 field in the TSS.
|
||||||
*/
|
*/
|
||||||
#define _STACK_GUARD_SIZE (MMU_PAGE_SIZE * 2)
|
#define _STACK_GUARD_SIZE (MMU_PAGE_SIZE * 2)
|
||||||
#else /* !CONFIG_X86_USERSPACE */
|
|
||||||
#define _STACK_GUARD_SIZE MMU_PAGE_SIZE
|
|
||||||
#endif /* CONFIG_X86_USERSPACE */
|
|
||||||
#define _STACK_BASE_ALIGN MMU_PAGE_SIZE
|
#define _STACK_BASE_ALIGN MMU_PAGE_SIZE
|
||||||
#else /* !CONFIG_X86_STACK_PROTECTION */
|
#elif defined(CONFIG_HW_STACK_PROTECTION) || defined(CONFIG_USERSPACE)
|
||||||
|
/* If only one of HW stack protection or userspace is enabled, then the
|
||||||
|
* stack will be preceded by one page which is a guard page or a kernel mode
|
||||||
|
* stack, respectively.
|
||||||
|
*/
|
||||||
|
#define _STACK_GUARD_SIZE MMU_PAGE_SIZE
|
||||||
|
#define _STACK_BASE_ALIGN MMU_PAGE_SIZE
|
||||||
|
#else /* Neither feature */
|
||||||
#define _STACK_GUARD_SIZE 0
|
#define _STACK_GUARD_SIZE 0
|
||||||
#define _STACK_BASE_ALIGN STACK_ALIGN
|
#define _STACK_BASE_ALIGN STACK_ALIGN
|
||||||
#endif /* CONFIG_X86_STACK_PROTECTION */
|
#endif
|
||||||
|
|
||||||
#define _ARCH_THREAD_STACK_DEFINE(sym, size) \
|
#define _ARCH_THREAD_STACK_DEFINE(sym, size) \
|
||||||
struct _k_thread_stack_element __kernel_noinit \
|
struct _k_thread_stack_element __kernel_noinit \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue