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
|
bool
|
||||||
default n
|
default n
|
||||||
depends on X86_MMU
|
depends on X86_MMU
|
||||||
|
select SET_GDT
|
||||||
|
select GDT_DYNAMIC
|
||||||
prompt "MMU-based stack overflow protection"
|
prompt "MMU-based stack overflow protection"
|
||||||
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
|
||||||
|
|
|
@ -88,14 +88,14 @@ SECTION_FUNC(TEXT_START, __start)
|
||||||
#ifdef CONFIG_SET_GDT
|
#ifdef CONFIG_SET_GDT
|
||||||
/* If we set our own GDT, update the segment registers as well.
|
/* 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, %ds /* set DS */
|
||||||
movw %ax, %es /* set ES */
|
movw %ax, %es /* set ES */
|
||||||
movw %ax, %fs /* set FS */
|
movw %ax, %fs /* set FS */
|
||||||
movw %ax, %gs /* set GS */
|
movw %ax, %gs /* set GS */
|
||||||
movw %ax, %ss /* set SS */
|
movw %ax, %ss /* set SS */
|
||||||
|
|
||||||
ljmp $0x08, $__csSet /* set CS = 0x08 */
|
ljmp $CODE_SEG, $__csSet /* set CS = 0x08 */
|
||||||
|
|
||||||
__csSet:
|
__csSet:
|
||||||
#endif /* CONFIG_SET_GDT */
|
#endif /* CONFIG_SET_GDT */
|
||||||
|
@ -262,7 +262,10 @@ __csSet:
|
||||||
|
|
||||||
#endif /* CONFIG_X86_MMU */
|
#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 */
|
/* Jump to C portion of kernel initialization and never return */
|
||||||
|
|
||||||
jmp _Cstart
|
jmp _Cstart
|
||||||
|
|
|
@ -130,7 +130,8 @@ extern void (*_kernel_oops_handler)(void);
|
||||||
NANO_CPU_INT_REGISTER(_kernel_oops_handler, NANO_SOFT_IRQ,
|
NANO_CPU_INT_REGISTER(_kernel_oops_handler, NANO_SOFT_IRQ,
|
||||||
CONFIG_X86_KERNEL_OOPS_VECTOR / 16,
|
CONFIG_X86_KERNEL_OOPS_VECTOR / 16,
|
||||||
CONFIG_X86_KERNEL_OOPS_VECTOR, 0);
|
CONFIG_X86_KERNEL_OOPS_VECTOR, 0);
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define a default ESF for use with _NanoFatalErrorHandler() in the event
|
* Define a default ESF for use with _NanoFatalErrorHandler() in the event
|
||||||
* the caller does not have a NANO_ESF to pass
|
* the caller does not have a NANO_ESF to pass
|
||||||
|
@ -149,7 +150,6 @@ const NANO_ESF _default_esf = {
|
||||||
0xdeaddead, /* CS */
|
0xdeaddead, /* CS */
|
||||||
0xdeaddead, /* EFLAGS */
|
0xdeaddead, /* EFLAGS */
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_X86_KERNEL_OOPS */
|
|
||||||
|
|
||||||
#if CONFIG_EXCEPTION_DEBUG
|
#if CONFIG_EXCEPTION_DEBUG
|
||||||
|
|
||||||
|
@ -192,7 +192,9 @@ 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);
|
||||||
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_INVALID_TSS);
|
||||||
EXC_FUNC_CODE(IV_SEGMENT_NOT_PRESENT);
|
EXC_FUNC_CODE(IV_SEGMENT_NOT_PRESENT);
|
||||||
EXC_FUNC_CODE(IV_STACK_FAULT);
|
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);
|
_EXCEPTION_CONNECT_CODE(page_fault_handler, IV_PAGE_FAULT);
|
||||||
#endif /* CONFIG_EXCEPTION_DEBUG */
|
#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<kernel.h>
|
||||||
#include<mmustructs.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 */
|
/* Ref to _x86_mmu_buffer_validate documentation for details */
|
||||||
#define USER_PERM_BIT_POS ((u32_t)0x1)
|
#define USER_PERM_BIT_POS ((u32_t)0x1)
|
||||||
#define GET_RW_PERM(flags) (flags & BUFF_WRITEABLE)
|
#define GET_RW_PERM(flags) (flags & BUFF_WRITEABLE)
|
||||||
|
|
|
@ -39,6 +39,13 @@
|
||||||
#include <misc/dlist.h>
|
#include <misc/dlist.h>
|
||||||
#endif
|
#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? */
|
/* increase to 16 bytes (or more?) to support SSE/SSE2 instructions? */
|
||||||
|
|
||||||
#define STACK_ALIGN_SIZE 4
|
#define STACK_ALIGN_SIZE 4
|
||||||
|
|
|
@ -576,10 +576,18 @@ extern FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason,
|
||||||
[reason] "i" (reason_p)); \
|
[reason] "i" (reason_p)); \
|
||||||
CODE_UNREACHABLE; \
|
CODE_UNREACHABLE; \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
/** Dummy ESF for fatal errors that would otherwise not have an ESF */
|
/** Dummy ESF for fatal errors that would otherwise not have an ESF */
|
||||||
extern const NANO_ESF _default_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 */
|
#endif /* !_ASMLANGUAGE */
|
||||||
|
|
||||||
|
|
|
@ -152,13 +152,18 @@ SECTIONS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_GDT_DYNAMIC
|
#ifdef CONFIG_GDT_DYNAMIC
|
||||||
|
KEEP(*(.tss))
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
_gdt = .;
|
_gdt = .;
|
||||||
#ifdef LINKER_PASS2
|
#ifdef LINKER_PASS2
|
||||||
KEEP(*(gdt_ram_data))
|
KEEP(*(gdt_ram_data))
|
||||||
#else /* LINKER_PASS2 */
|
#else /* LINKER_PASS2 */
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86_STACK_PROTECTION
|
||||||
|
#define GDT_NUM_ENTRIES 5
|
||||||
|
#else /* CONFIG_X86_STACK_PROTECTION */
|
||||||
#define GDT_NUM_ENTRIES 3
|
#define GDT_NUM_ENTRIES 3
|
||||||
|
#endif /* CONFIG_X86_STACK_PROTECTION */
|
||||||
|
|
||||||
. += GDT_NUM_ENTRIES * 8;
|
. += GDT_NUM_ENTRIES * 8;
|
||||||
#endif /* LINKER_PASS2 */
|
#endif /* LINKER_PASS2 */
|
||||||
|
|
|
@ -11,6 +11,16 @@ import os
|
||||||
from elftools.elf.elffile import ELFFile
|
from elftools.elf.elffile import ELFFile
|
||||||
from elftools.elf.sections import SymbolTableSection
|
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"
|
gdt_pd_fmt = "<HIH"
|
||||||
|
|
||||||
FLAGS_GRAN = 1 << 7 # page granularity
|
FLAGS_GRAN = 1 << 7 # page granularity
|
||||||
|
@ -65,6 +75,23 @@ def create_code_data_entry(base, limit, dpl, flags, access):
|
||||||
access, flags, base_hi)
|
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):
|
def get_symbols(obj):
|
||||||
for section in obj.iter_sections():
|
for section in obj.iter_sections():
|
||||||
if isinstance(section, SymbolTableSection):
|
if isinstance(section, SymbolTableSection):
|
||||||
|
@ -95,7 +122,17 @@ def main():
|
||||||
kernel = ELFFile(fp)
|
kernel = ELFFile(fp)
|
||||||
syms = get_symbols(kernel)
|
syms = get_symbols(kernel)
|
||||||
|
|
||||||
num_entries = 3
|
# 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"]
|
gdt_base = syms["_gdt"]
|
||||||
|
|
||||||
|
@ -112,6 +149,17 @@ def main():
|
||||||
fp.write(create_code_data_entry(0, 0xFFFFF, 0,
|
fp.write(create_code_data_entry(0, 0xFFFFF, 0,
|
||||||
FLAGS_GRAN, ACCESS_RW))
|
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__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue