x86: implement improved double-fault handler
We now create a special IA hardware task for handling double faults. This has a known good stack so that if the kernel tries to push stack data onto an unmapped page, we don't triple-fault and reset the system. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
08c291306e
commit
bc666ae7f7
8 changed files with 124 additions and 15 deletions
|
@ -82,6 +82,8 @@ config X86_STACK_PROTECTION
|
|||
bool
|
||||
default n
|
||||
depends on X86_MMU
|
||||
select SET_GDT
|
||||
select GDT_DYNAMIC
|
||||
prompt "MMU-based stack overflow protection"
|
||||
help
|
||||
This option leverages the MMU to cause a system fatal error if the
|
||||
|
|
|
@ -88,14 +88,14 @@ SECTION_FUNC(TEXT_START, __start)
|
|||
#ifdef CONFIG_SET_GDT
|
||||
/* If we set our own GDT, update the segment registers as well.
|
||||
*/
|
||||
movw $0x10, %ax /* data segment selector (entry = 3) */
|
||||
movw $DATA_SEG, %ax /* data segment selector (entry = 3) */
|
||||
movw %ax, %ds /* set DS */
|
||||
movw %ax, %es /* set ES */
|
||||
movw %ax, %fs /* set FS */
|
||||
movw %ax, %gs /* set GS */
|
||||
movw %ax, %ss /* set SS */
|
||||
|
||||
ljmp $0x08, $__csSet /* set CS = 0x08 */
|
||||
ljmp $CODE_SEG, $__csSet /* set CS = 0x08 */
|
||||
|
||||
__csSet:
|
||||
#endif /* CONFIG_SET_GDT */
|
||||
|
@ -262,7 +262,10 @@ __csSet:
|
|||
|
||||
#endif /* CONFIG_X86_MMU */
|
||||
|
||||
|
||||
#ifdef CONFIG_X86_STACK_PROTECTION
|
||||
mov $MAIN_TSS, %ax
|
||||
ltr %ax
|
||||
#endif
|
||||
/* Jump to C portion of kernel initialization and never return */
|
||||
|
||||
jmp _Cstart
|
||||
|
|
|
@ -130,7 +130,8 @@ extern void (*_kernel_oops_handler)(void);
|
|||
NANO_CPU_INT_REGISTER(_kernel_oops_handler, NANO_SOFT_IRQ,
|
||||
CONFIG_X86_KERNEL_OOPS_VECTOR / 16,
|
||||
CONFIG_X86_KERNEL_OOPS_VECTOR, 0);
|
||||
#else
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define a default ESF for use with _NanoFatalErrorHandler() in the event
|
||||
* the caller does not have a NANO_ESF to pass
|
||||
|
@ -149,7 +150,6 @@ const NANO_ESF _default_esf = {
|
|||
0xdeaddead, /* CS */
|
||||
0xdeaddead, /* EFLAGS */
|
||||
};
|
||||
#endif /* CONFIG_X86_KERNEL_OOPS */
|
||||
|
||||
#if CONFIG_EXCEPTION_DEBUG
|
||||
|
||||
|
@ -192,7 +192,9 @@ EXC_FUNC_NOCODE(IV_OVERFLOW);
|
|||
EXC_FUNC_NOCODE(IV_BOUND_RANGE);
|
||||
EXC_FUNC_NOCODE(IV_INVALID_OPCODE);
|
||||
EXC_FUNC_NOCODE(IV_DEVICE_NOT_AVAILABLE);
|
||||
EXC_FUNC_CODE(IV_DOUBLE_FAULT);
|
||||
#ifndef CONFIG_X86_STACK_PROTECTION
|
||||
EXC_FUNC_NOCODE(IV_DOUBLE_FAULT);
|
||||
#endif
|
||||
EXC_FUNC_CODE(IV_INVALID_TSS);
|
||||
EXC_FUNC_CODE(IV_SEGMENT_NOT_PRESENT);
|
||||
EXC_FUNC_CODE(IV_STACK_FAULT);
|
||||
|
@ -230,3 +232,43 @@ FUNC_NORETURN void page_fault_handler(const NANO_ESF *pEsf)
|
|||
_EXCEPTION_CONNECT_CODE(page_fault_handler, IV_PAGE_FAULT);
|
||||
#endif /* CONFIG_EXCEPTION_DEBUG */
|
||||
|
||||
#ifdef CONFIG_X86_STACK_PROTECTION
|
||||
void df_handler(void)
|
||||
{
|
||||
printk("DOUBLE FAULT! Likely kernel stack overflow\n");
|
||||
|
||||
/* TODO: do forensic analysis to figure out the faulting context
|
||||
* since this exception type pushes undefined CS:EIP information
|
||||
*/
|
||||
|
||||
/* Double faults are unrecoverable, time to freak out */
|
||||
_NanoFatalErrorHandler(_NANO_ERR_KERNEL_PANIC, &_default_esf);
|
||||
}
|
||||
|
||||
_GENERIC_SECTION(.tss)
|
||||
struct task_state_segment _main_tss = {
|
||||
.ss0 = DATA_SEG
|
||||
};
|
||||
|
||||
char __noinit df_stack[512];
|
||||
|
||||
/* Special TSS for handling double-faults with a known good stack */
|
||||
_GENERIC_SECTION(.tss)
|
||||
struct task_state_segment _df_tss = {
|
||||
.esp = (u32_t)(df_stack + sizeof(df_stack)),
|
||||
.cs = CODE_SEG,
|
||||
.ds = DATA_SEG,
|
||||
.es = DATA_SEG,
|
||||
.fs = DATA_SEG,
|
||||
.gs = DATA_SEG,
|
||||
.ss = DATA_SEG,
|
||||
.eip = (u32_t)df_handler,
|
||||
.cr3 = (u32_t)&__mmu_tables_start
|
||||
};
|
||||
|
||||
/* Configure a task gate descriptor in the IDT for the double fault
|
||||
* exception
|
||||
*/
|
||||
_X86_IDT_TSS_REGISTER(DF_TSS, -1, -1, IV_DOUBLE_FAULT, 0);
|
||||
|
||||
#endif /* CONFIG_X86_STACK_PROTECTION */
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
#include<kernel.h>
|
||||
#include<mmustructs.h>
|
||||
|
||||
/* Linker variable. It needed to access the start of the Page directory */
|
||||
extern u32_t __mmu_tables_start;
|
||||
|
||||
#define X86_MMU_PD ((struct x86_mmu_page_directory *)\
|
||||
(void *)&__mmu_tables_start)
|
||||
|
||||
/* Ref to _x86_mmu_buffer_validate documentation for details */
|
||||
#define USER_PERM_BIT_POS ((u32_t)0x1)
|
||||
#define GET_RW_PERM(flags) (flags & BUFF_WRITEABLE)
|
||||
|
|
|
@ -39,6 +39,13 @@
|
|||
#include <misc/dlist.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* GDT layout */
|
||||
#define CODE_SEG 0x08
|
||||
#define DATA_SEG 0x10
|
||||
#define MAIN_TSS 0x18
|
||||
#define DF_TSS 0x20
|
||||
|
||||
/* increase to 16 bytes (or more?) to support SSE/SSE2 instructions? */
|
||||
|
||||
#define STACK_ALIGN_SIZE 4
|
||||
|
|
|
@ -576,10 +576,18 @@ extern FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason,
|
|||
[reason] "i" (reason_p)); \
|
||||
CODE_UNREACHABLE; \
|
||||
} while (0)
|
||||
#else
|
||||
#endif
|
||||
|
||||
/** Dummy ESF for fatal errors that would otherwise not have an ESF */
|
||||
extern const NANO_ESF _default_esf;
|
||||
#endif /* CONFIG_X86_KERNEL_OOPS */
|
||||
|
||||
#ifdef CONFIG_X86_MMU
|
||||
/* Linker variable. It needed to access the start of the Page directory */
|
||||
extern u32_t __mmu_tables_start;
|
||||
|
||||
#define X86_MMU_PD ((struct x86_mmu_page_directory *)\
|
||||
(void *)&__mmu_tables_start)
|
||||
#endif /* CONFIG_X86_MMU */
|
||||
|
||||
#endif /* !_ASMLANGUAGE */
|
||||
|
||||
|
|
|
@ -152,13 +152,18 @@ SECTIONS
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_GDT_DYNAMIC
|
||||
KEEP(*(.tss))
|
||||
. = ALIGN(8);
|
||||
_gdt = .;
|
||||
#ifdef LINKER_PASS2
|
||||
KEEP(*(gdt_ram_data))
|
||||
#else /* LINKER_PASS2 */
|
||||
|
||||
#ifdef CONFIG_X86_STACK_PROTECTION
|
||||
#define GDT_NUM_ENTRIES 5
|
||||
#else /* CONFIG_X86_STACK_PROTECTION */
|
||||
#define GDT_NUM_ENTRIES 3
|
||||
#endif /* CONFIG_X86_STACK_PROTECTION */
|
||||
|
||||
. += GDT_NUM_ENTRIES * 8;
|
||||
#endif /* LINKER_PASS2 */
|
||||
|
|
|
@ -11,6 +11,16 @@ import os
|
|||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.sections import SymbolTableSection
|
||||
|
||||
def debug(text):
|
||||
if not args.verbose:
|
||||
return
|
||||
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
|
||||
|
||||
def error(text):
|
||||
sys.stderr.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
gdt_pd_fmt = "<HIH"
|
||||
|
||||
FLAGS_GRAN = 1 << 7 # page granularity
|
||||
|
@ -65,6 +75,23 @@ def create_code_data_entry(base, limit, dpl, flags, access):
|
|||
access, flags, base_hi)
|
||||
|
||||
|
||||
def create_tss_entry(base, limit, dpl):
|
||||
present = 1
|
||||
|
||||
base_lo, base_mid, base_hi, limit_lo, limit_hi, = chop_base_limit(base,
|
||||
limit)
|
||||
|
||||
type_code = 0x9 # non-busy 32-bit TSS descriptor
|
||||
gran = 0
|
||||
|
||||
flags = (gran << 7) | limit_hi
|
||||
type_byte = ((present << 7) | (dpl << 5) | type_code)
|
||||
|
||||
|
||||
return struct.pack(gdt_ent_fmt, limit_lo, base_lo, base_mid,
|
||||
type_byte, flags, base_hi)
|
||||
|
||||
|
||||
def get_symbols(obj):
|
||||
for section in obj.iter_sections():
|
||||
if isinstance(section, SymbolTableSection):
|
||||
|
@ -95,6 +122,16 @@ def main():
|
|||
kernel = ELFFile(fp)
|
||||
syms = get_symbols(kernel)
|
||||
|
||||
# NOTE: use-cases are extremely limited; we always have a basic flat
|
||||
# code/data segments. If we are doing stack protection, we are going to
|
||||
# have two TSS to manage the main task and the special task for double
|
||||
# fault exception handling
|
||||
|
||||
if "CONFIG_X86_STACK_PROTECTION" in syms:
|
||||
stackprot = True
|
||||
num_entries = 5
|
||||
else:
|
||||
stackprot = False
|
||||
num_entries = 3
|
||||
|
||||
gdt_base = syms["_gdt"]
|
||||
|
@ -112,6 +149,17 @@ def main():
|
|||
fp.write(create_code_data_entry(0, 0xFFFFF, 0,
|
||||
FLAGS_GRAN, ACCESS_RW))
|
||||
|
||||
if stackprot:
|
||||
main_tss = syms["_main_tss"]
|
||||
df_tss = syms["_df_tss"]
|
||||
|
||||
# Selector 0x18: main TSS
|
||||
fp.write(create_tss_entry(main_tss, 0x67, 0))
|
||||
|
||||
# Selector 0x20: double-fault TSS
|
||||
fp.write(create_tss_entry(df_tss, 0x67, 0))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue