diff --git a/kernel/include/cpu/CPU.h b/kernel/include/cpu/CPU.h index 3b8359b3..81b0ef83 100644 --- a/kernel/include/cpu/CPU.h +++ b/kernel/include/cpu/CPU.h @@ -10,4 +10,5 @@ namespace CPU uint64_t get_feature_bitmask(); uint64_t get_initial_apic_id(); bool has_feature(CPU::Features); + bool has_nx(); } \ No newline at end of file diff --git a/kernel/include/memory/MemoryManager.h b/kernel/include/memory/MemoryManager.h index 240c9a21..072c004c 100644 --- a/kernel/include/memory/MemoryManager.h +++ b/kernel/include/memory/MemoryManager.h @@ -13,6 +13,8 @@ namespace MemoryManager { void init(); + void protect_kernel_sections(); + void* get_mapping(void* physicalAddress, int flags = MAP_READ_WRITE); void release_mapping(void* mapping); diff --git a/kernel/include/memory/Paging.h b/kernel/include/memory/Paging.h index 37530b37..38998b5d 100644 --- a/kernel/include/memory/Paging.h +++ b/kernel/include/memory/Paging.h @@ -17,13 +17,17 @@ struct PageDirectoryEntry bool larger_pages : 1; bool ignore1 : 1; uint8_t available : 3; - uint64_t address : 52; + uint64_t address : 48; + uint8_t available2 : 3; + bool no_execute : 1; void set_address(uint64_t addr); uint64_t get_address(); -}; +} __attribute__((packed)); struct PageTable { PageDirectoryEntry entries[512]; -} __attribute__((aligned(PAGE_SIZE))); \ No newline at end of file +} __attribute__((aligned(PAGE_SIZE))); + +static_assert(sizeof(PageDirectoryEntry) == 8UL); \ No newline at end of file diff --git a/kernel/include/memory/VMM.h b/kernel/include/memory/VMM.h index ad736def..b2b56a77 100644 --- a/kernel/include/memory/VMM.h +++ b/kernel/include/memory/VMM.h @@ -33,6 +33,7 @@ namespace VMM PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr); void propagate_read_write(PageTable* root, uint64_t vaddr); + void propagate_no_execute(PageTable* root, uint64_t vaddr); void propagate_user(PageTable* root, uint64_t vaddr); void flush_tlb(uint64_t addr); diff --git a/kernel/moon.ld b/kernel/moon.ld index 68fe520f..e2dd402a 100644 --- a/kernel/moon.ld +++ b/kernel/moon.ld @@ -16,14 +16,19 @@ SECTIONS kernel_start = .; .text : { KEEP(*(.text.boot)) *(.text .text.*) /* code */ + . = ALIGN(0x1000); + start_of_kernel_rodata = .; *(.rodata .rodata.*) /* data */ + end_of_kernel_rodata = .; + . = ALIGN(0x1000); + start_of_kernel_data = .; *(.data .data.*) } :boot .bss (NOLOAD) : { /* bss */ - . = ALIGN(16); *(.bss .bss.*) *(COMMON) } :boot + end_of_kernel_data = .; kernel_end = .; /DISCARD/ : { *(.eh_frame) *(.comment) } diff --git a/kernel/src/cpu/CPU.cpp b/kernel/src/cpu/CPU.cpp index 173951f4..75c717f5 100644 --- a/kernel/src/cpu/CPU.cpp +++ b/kernel/src/cpu/CPU.cpp @@ -66,6 +66,14 @@ uint64_t CPU::get_feature_bitmask() return bitmask; } +bool CPU::has_nx() +{ + unsigned int unused; + unsigned int edx; + __get_cpuid(0x80000001, &unused, &unused, &unused, &edx); + return edx & (1 << 20); +} + static bool _has_feature(int feature) { return (CPU::get_feature_bitmask() & (uint64_t)(1 << feature)) > 0; diff --git a/kernel/src/init/Init.cpp b/kernel/src/init/Init.cpp index 59146d9b..4b886699 100644 --- a/kernel/src/init/Init.cpp +++ b/kernel/src/init/Init.cpp @@ -18,6 +18,7 @@ #include "std/assert.h" #include "std/string.h" #include "utils/Time.h" +#include "misc/MSR.h" extern BOOTBOOT bootboot; extern "C" char environment[4096]; @@ -39,6 +40,40 @@ void Init::disable_smp() extern "C" void asm_enable_sse(); extern void clock_init(); +extern void panic_prepare_keyboard_triple_fault(); + +#define NO_EXECUTE_ENABLED (1 << 11) + +static void check_and_enable_nx() +{ + if(!CPU::has_nx()) { + kerrorln("This machine does not support the NX feature, which is required to continue booting."); + kerrorln("On most cases, this means your machine is too old and not supported."); + + kinfoln("Press any key to restart and select an OS that is suitable for your CPU."); + + panic_prepare_keyboard_triple_fault(); + + while(1) halt(); + } + + kdbgln("nx supported"); + + MSR efer(IA32_EFER_MSR); + + uint64_t value = efer.read(); + + if(value & NO_EXECUTE_ENABLED) + { + kdbgln("nx already enabled"); + return; + } + + kdbgln("nx not enabled, enabling it"); + + efer.write(value | NO_EXECUTE_ENABLED); +} + void Init::early_init() { Interrupts::disable(); @@ -48,8 +83,12 @@ void Init::early_init() framebuffer0.init((void*)&fb, bootboot.fb_type, bootboot.fb_scanline, bootboot.fb_width, bootboot.fb_height); + check_and_enable_nx(); + MemoryManager::init(); + MemoryManager::protect_kernel_sections(); + if (strstr(environment, "quiet=1")) { KernelLog::toggle_log_level(LogLevel::DEBUG); diff --git a/kernel/src/memory/MemoryManager.cpp b/kernel/src/memory/MemoryManager.cpp index 1e3dec5b..61381a43 100644 --- a/kernel/src/memory/MemoryManager.cpp +++ b/kernel/src/memory/MemoryManager.cpp @@ -8,6 +8,7 @@ #include "memory/PMM.h" #include "memory/VMM.h" #include "std/assert.h" +#include "misc/utils.h" void MemoryManager::init() { @@ -17,6 +18,17 @@ void MemoryManager::init() PMM::map_bitmap_to_virtual(); } +extern char start_of_kernel_rodata[1]; +extern char end_of_kernel_rodata[1]; +extern char start_of_kernel_data[1]; +extern char end_of_kernel_data[1]; + +void MemoryManager::protect_kernel_sections() +{ + protect(start_of_kernel_rodata, Utilities::get_blocks_from_size(PAGE_SIZE, end_of_kernel_rodata - start_of_kernel_rodata), 0); + protect(start_of_kernel_data, Utilities::get_blocks_from_size(PAGE_SIZE, end_of_kernel_data - start_of_kernel_data), MAP_READ_WRITE); +} + void* MemoryManager::get_mapping(void* physicalAddress, int flags) { uint64_t virtualAddress = KernelHeap::request_virtual_page(); diff --git a/kernel/src/memory/VMM.cpp b/kernel/src/memory/VMM.cpp index 9f7a5fca..3e2be67d 100644 --- a/kernel/src/memory/VMM.cpp +++ b/kernel/src/memory/VMM.cpp @@ -13,6 +13,8 @@ static PageTable* kernel_pml4; static PageTable* current_pml4; static AddressSpace* user_address_space; +// FIXME: Switch to recursive paging instead of naively assuming the physical address space is identity mapped. + void VMM::switch_back_to_kernel_address_space() { if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; } @@ -85,6 +87,9 @@ void VMM::remap(uint64_t vaddr, int flags) if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr); else pde->read_write = false; + if(flags & Execute) pde->no_execute = false; + else + pde->no_execute = true; flush_tlb(vaddr); } @@ -104,6 +109,7 @@ uint64_t VMM::get_flags(uint64_t vaddr) uint64_t flags = 0; if (pde->user) flags |= User; if (pde->read_write) flags |= ReadWrite; + if(!pde->no_execute) flags |= Execute; return flags; } @@ -130,6 +136,9 @@ void VMM::map(uint64_t vaddr, uint64_t paddr, int flags) if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr); else pde->read_write = false; + if(flags & Execute) pde->no_execute = false; + else + pde->no_execute = true; if (will_flush_tlb) flush_tlb(vaddr); } @@ -246,6 +255,34 @@ void VMM::propagate_user(PageTable* root, uint64_t vaddr) pde->user = true; } +void VMM::propagate_no_execute(PageTable* root, uint64_t vaddr) +{ + uint64_t page_index; + PageDirectoryEntry* pde; + PageTable* pt = root; + + uint64_t indexes[3]; + + decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]); + + for (int i = 0; i < 3; i++) + { + pde = &pt->entries[indexes[i]]; + if (!pde->present) return; + else + { + pde->no_execute = true; + if (pde->larger_pages) return; + pt = (PageTable*)pde->get_address(); + } + } + + pde = &pt->entries[page_index]; + if (!pde->present) return; + else + pde->no_execute = true; +} + void VMM::flush_tlb(uint64_t addr) { asm volatile("invlpg (%0)" : : "r"(addr) : "memory"); diff --git a/kernel/src/panic/Panic.cpp b/kernel/src/panic/Panic.cpp index eac29e7c..f9e1d862 100644 --- a/kernel/src/panic/Panic.cpp +++ b/kernel/src/panic/Panic.cpp @@ -16,7 +16,7 @@ static bool g_is_in_panic = false; static bool g_is_in_double_panic = false; -static void panic_prepare_keyboard_triple_fault() +void panic_prepare_keyboard_triple_fault() { PIC::enable_master(0b11111101); // enable keyboard only PIC::enable_slave(0b11111111); diff --git a/kernel/src/sys/elf/ELFLoader.cpp b/kernel/src/sys/elf/ELFLoader.cpp index 02cda298..3b8be7dc 100644 --- a/kernel/src/sys/elf/ELFLoader.cpp +++ b/kernel/src/sys/elf/ELFLoader.cpp @@ -105,7 +105,11 @@ ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node) VMM::apply_address_space(); VMM::switch_to_previous_user_address_space(); - MemoryManager::protect(buffer, pages, phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER); + int new_flags = MAP_USER; + if(phdr.p_flags & 2) new_flags |= MAP_READ_WRITE; + if(phdr.p_flags & 1) new_flags |= MAP_EXEC; + + MemoryManager::protect(buffer, pages, new_flags); image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) + (image->section_count + 1) * sizeof(ELFSection)); diff --git a/kernel/src/sys/mem.cpp b/kernel/src/sys/mem.cpp index aa51f14a..1e70549e 100644 --- a/kernel/src/sys/mem.cpp +++ b/kernel/src/sys/mem.cpp @@ -10,27 +10,35 @@ #include "thread/Scheduler.h" #include -#define MAP_READ 1 -#define MAP_WRITE 2 -#define MAP_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_NONE 0 +#define PROT_EXEC 4 #define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno) static const char* format_prot(int prot) { - static char prot_string[3]; - prot_string[2] = 0; - prot_string[0] = ((prot & MAP_READ) > 0) ? 'r' : '-'; - prot_string[1] = ((prot & MAP_WRITE) > 0) ? 'w' : '-'; + static char prot_string[4]; + prot_string[3] = 0; + prot_string[0] = ((prot & PROT_READ) > 0) ? 'r' : '-'; + prot_string[1] = ((prot & PROT_WRITE) > 0) ? 'w' : '-'; + prot_string[2] = ((prot & PROT_WRITE) > 0) ? 'x' : '-'; return prot_string; } static int mman_flags_from_prot(int prot) { - prot &= 0b11; - if (prot == MAP_NONE) return 0; - if ((prot & MAP_WRITE) > 0) return MAP_USER | MAP_READ_WRITE; - return MAP_USER; + prot &= 0b111; + int flags = MAP_USER; + if (prot == PROT_NONE) return 0; + if ((prot & PROT_WRITE) > 0) { + flags |= MAP_READ_WRITE; + } + if ((prot & PROT_EXEC) > 0) { + flags |= MAP_EXEC; + } + return flags; } void sys_mmap(Context* context, void* address, size_t size, int prot)