From bc666ae7f7e873e90fdf853f43e1c522f01c2a87 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 14 Jul 2017 16:35:17 -0700 Subject: [PATCH] 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 --- arch/x86/Kconfig | 2 ++ arch/x86/core/crt0.S | 9 ++++-- arch/x86/core/fatal.c | 48 +++++++++++++++++++++++++-- arch/x86/core/x86_mmu.c | 6 ---- arch/x86/include/kernel_arch_data.h | 7 ++++ include/arch/x86/arch.h | 12 +++++-- include/arch/x86/linker.ld | 5 +++ scripts/gen_gdt.py | 50 ++++++++++++++++++++++++++++- 8 files changed, 124 insertions(+), 15 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f63ff7cfdb5..46c464e8ca0 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -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 diff --git a/arch/x86/core/crt0.S b/arch/x86/core/crt0.S index 48764fb40d6..208866401a3 100644 --- a/arch/x86/core/crt0.S +++ b/arch/x86/core/crt0.S @@ -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 diff --git a/arch/x86/core/fatal.c b/arch/x86/core/fatal.c index 099f7428e4f..6c03867ee56 100644 --- a/arch/x86/core/fatal.c +++ b/arch/x86/core/fatal.c @@ -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 */ diff --git a/arch/x86/core/x86_mmu.c b/arch/x86/core/x86_mmu.c index c167f2da381..5a540265bde 100644 --- a/arch/x86/core/x86_mmu.c +++ b/arch/x86/core/x86_mmu.c @@ -7,12 +7,6 @@ #include #include -/* 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) diff --git a/arch/x86/include/kernel_arch_data.h b/arch/x86/include/kernel_arch_data.h index 814907ea18c..175f19f78d3 100644 --- a/arch/x86/include/kernel_arch_data.h +++ b/arch/x86/include/kernel_arch_data.h @@ -39,6 +39,13 @@ #include #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 diff --git a/include/arch/x86/arch.h b/include/arch/x86/arch.h index 7477f781b5b..a9bd6261b32 100644 --- a/include/arch/x86/arch.h +++ b/include/arch/x86/arch.h @@ -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 */ diff --git a/include/arch/x86/linker.ld b/include/arch/x86/linker.ld index 103455e7c16..57337514505 100644 --- a/include/arch/x86/linker.ld +++ b/include/arch/x86/linker.ld @@ -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 */ diff --git a/scripts/gen_gdt.py b/scripts/gen_gdt.py index 983465ba055..8af8e7aa72b 100755 --- a/scripts/gen_gdt.py +++ b/scripts/gen_gdt.py @@ -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 = "