From 085ae6ba53341db9688fd67cecd62260b9030064 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Fri, 2 Jan 2026 22:39:57 -0500 Subject: [PATCH] add guard pages to stack; dynamically allocate user stacks; zero non-preserved registers before returning from syscalls --- src/kernel/interrupts.asm | 18 ++++++++++- src/kernel/interrupts.c | 24 +++++++++++--- src/kernel/paging.c | 18 ++++++++--- src/kernel/paging.h | 5 ++- src/kernel/process.c | 67 +++++++++++++++++++++++++++------------ src/kernel/process.h | 3 ++ src/kernel/syscalls.asm | 9 +++++- 7 files changed, 113 insertions(+), 31 deletions(-) diff --git a/src/kernel/interrupts.asm b/src/kernel/interrupts.asm index 1fd0f10..ded890a 100644 --- a/src/kernel/interrupts.asm +++ b/src/kernel/interrupts.asm @@ -76,7 +76,23 @@ isr_exception_common: mov rax, cr3 push rax mov rdi, rsp - jmp isr_exception_c + + call isr_exception_c + + add rsp, 48 + pop r11 + pop r10 + pop r9 + pop r8 + add rsp, 8 + pop rsi + pop rdi + pop rdx + pop rcx + add rsp, 8 + pop rax + add rsp, 16 + iretq isr_irq_common: push rax diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index f1d64a4..99521da 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -22,6 +22,8 @@ // https://osdev.wiki/wiki/Task_State_Segment #include "interrupts.h" +#include "process.h" +#include "paging.h" #include "debug.h" //interrupts.asm depends on layout @@ -63,7 +65,7 @@ const char *exception_names[32] = { "hypervisor injection exception", "vmm communication exception", "security exception", 0 }; -[[noreturn]] static void unhandled_exception(struct exception_parameter *parameter) { +[[noreturn]] static void unhandled_exception(const struct exception_parameter *parameter) { assert(parameter->exception_number < 32 && exception_names[parameter->exception_number] != 0) @@ -101,11 +103,25 @@ const char *exception_names[32] = { } //referenced in interrupts.asm -void isr_exception_c(struct exception_parameter *parameter) { +void isr_exception_c(const struct exception_parameter *parameter) { - //eventually some exceptions will be handled in here + if (running_thread != 0 && + parameter->exception_number == 0x0e && + (parameter->error_code & 0x1) == 0x0 && + parameter->cr2 >= (uint64_t)running_thread->stack_bottom + 4096 && + parameter->cr2 < (uint64_t)running_thread->stack_top) { - unhandled_exception(parameter); + void *vma = (void *)(parameter->cr2 & 0xfffffffffffff000); + unmap_page_for_process(running_thread->process, vma); + uint64_t pma = take_free_physical_page(); + map_page_for_process( + running_thread->process, pma, + vma, 1, 0, 1); + + } + + else + unhandled_exception(parameter); } diff --git a/src/kernel/paging.c b/src/kernel/paging.c index 66c16c7..e16deeb 100644 --- a/src/kernel/paging.c +++ b/src/kernel/paging.c @@ -1,5 +1,5 @@ /* Calcite, src/kernel/paging.c - * Copyright 2025 Benji Dial + * Copyright 2025-2026 Benji Dial * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -104,9 +104,7 @@ void mark_physical_memory_free(uint64_t base, uint64_t length) { //defined in paging.asm void invlpg(void *address); -void map_in_kernel_page_table( - uint64_t physical_base, void *virtual_base, - int writable, int executable) { +void map_in_kernel_page_table(uint64_t physical_base, void *virtual_base, int writable, int executable) { uint64_t virtual_base_u64 = (uint64_t)virtual_base; assert(virtual_base_u64 >= 0xffffffffc0000000); @@ -122,6 +120,18 @@ void map_in_kernel_page_table( } +void create_kernel_guard_page(void *virtual_base) { + + uint64_t virtual_base_u64 = (uint64_t)virtual_base; + assert(virtual_base_u64 >= 0xffffffffc0000000); + + uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12; + assert(kernel_p1s[p1s_index] == 0); + + kernel_p1s[p1s_index] = 0x2; + +} + void unmap_kernel_page(void *virtual_base) { uint64_t virtual_base_u64 = (uint64_t)virtual_base; diff --git a/src/kernel/paging.h b/src/kernel/paging.h index eaee047..60f0ca9 100644 --- a/src/kernel/paging.h +++ b/src/kernel/paging.h @@ -1,5 +1,5 @@ /* Calcite, src/kernel/paging.h - * Copyright 2025 Benji Dial + * Copyright 2025-2026 Benji Dial * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,9 @@ void mark_physical_memory_free(uint64_t base, uint64_t length); void map_in_kernel_page_table( uint64_t physical_base, void *virtual_base, int writable, int executable); +//virtual base should be page-aligned and within kernel range. +void create_kernel_guard_page(void *virtual_base); + //unmaps one page. base should be page-aligned and within kernel range. void unmap_kernel_page(void *virtual_base); diff --git a/src/kernel/process.c b/src/kernel/process.c index b3096d6..eedaa73 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -69,11 +69,8 @@ void create_process(struct process *process_out) { } -void map_page_for_process( - struct process *process, uint64_t physical_base, - void *virtual_base, int writable, int executable, int owned) { - - assert(physical_base % 4096 == 0) +static void get_page_table_entry( + struct process *process, void *virtual_base, int *p1i_out, int *p2i_out, int *p3i_out) { uint64_t vma = (uint64_t)virtual_base; assert(vma % 4096 == 0) @@ -112,6 +109,21 @@ void map_page_for_process( process->owned_pages_bitmaps[p3i][p2i][i] = 0; } + *p1i_out = p1i; + *p2i_out = p2i; + *p3i_out = p3i; + +} + +void map_page_for_process( + struct process *process, uint64_t physical_base, + void *virtual_base, int writable, int executable, int owned) { + + assert(physical_base % 4096 == 0) + + int p1i, p2i, p3i; + get_page_table_entry(process, virtual_base, &p1i, &p2i, &p3i); + assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0) process->p1_virtual_bases[p3i][p2i][p1i] = @@ -123,6 +135,17 @@ void map_page_for_process( } +void create_guard_page_for_process(struct process *process, void *virtual_base) { + + int p1i, p2i, p3i; + get_page_table_entry(process, virtual_base, &p1i, &p2i, &p3i); + + assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0) + + process->p1_virtual_bases[p3i][p2i][p1i] = 0x6; + +} + void unmap_page_for_process( struct process *process, void *virtual_base) { @@ -532,7 +555,8 @@ void destroy_thread(struct thread *thread) { --thread->process->n_threads; - for (void *p = thread->syscall_stack_bottom; p < thread->syscall_stack_top; p += 4096) + unmap_kernel_page(thread->syscall_stack_bottom); + for (void *p = thread->syscall_stack_bottom + 4096; p < thread->syscall_stack_top; p += 4096) unmap_and_free_kernel_page(p); if (thread->process->n_threads == 0) @@ -545,22 +569,25 @@ void destroy_thread(struct thread *thread) { } -#define INITIAL_STACK_SIZE (16 << 20) -#define INITIAL_SYSCALL_STACK_SIZE 16384 +#define MAX_STACK_SIZE ((16 << 20) - 4096) +#define INITIAL_STACK_SIZE 16384 + +#define SYSCALL_STACK_SIZE 12288 void create_thread(struct process *process, struct thread *thread_out) { - //TODO: allocate stack as needed on page faults, have guard pages, etc. - void *stack_bottom_vma = - find_free_process_region(process, INITIAL_STACK_SIZE / 4096); + find_free_process_region(process, MAX_STACK_SIZE + 4096); - for (int i = 0; i < INITIAL_STACK_SIZE / 4096; ++i) { + for (int i = 0; i < MAX_STACK_SIZE - INITIAL_STACK_SIZE + 4096; i += 4096) + create_guard_page_for_process(process, stack_bottom_vma + i); + + for (int i = MAX_STACK_SIZE - INITIAL_STACK_SIZE + 4096; i < MAX_STACK_SIZE + 4096; i += 4096) { uint64_t pma = take_free_physical_page(); map_page_for_process( process, pma, - stack_bottom_vma + i * 4096, + stack_bottom_vma + i, 1, 0, 1); void *kvma = find_free_kernel_region(4096); @@ -570,19 +597,21 @@ void create_thread(struct process *process, struct thread *thread_out) { } - void *syscall_stack_bottom = find_free_kernel_region(INITIAL_SYSCALL_STACK_SIZE); + void *syscall_stack_bottom = find_free_kernel_region(SYSCALL_STACK_SIZE + 4096); - for (int i = 0; i < INITIAL_SYSCALL_STACK_SIZE / 4096; ++i) { + create_kernel_guard_page(syscall_stack_bottom); + + for (int i = 4096; i < 4096 + SYSCALL_STACK_SIZE; i += 4096) { uint64_t pma = take_free_physical_page(); map_in_kernel_page_table( - pma, syscall_stack_bottom + i * 4096, 1, 0); + pma, syscall_stack_bottom + i, 1, 0); } thread_out->process = process; thread_out->stack_bottom = stack_bottom_vma; - thread_out->stack_top = stack_bottom_vma + INITIAL_STACK_SIZE; + thread_out->stack_top = stack_bottom_vma + 4096 + MAX_STACK_SIZE; thread_out->syscall_stack_bottom = syscall_stack_bottom; - thread_out->syscall_stack_top = syscall_stack_bottom + INITIAL_SYSCALL_STACK_SIZE; + thread_out->syscall_stack_top = syscall_stack_bottom + 4096 + SYSCALL_STACK_SIZE; ++process->n_threads; @@ -640,8 +669,6 @@ void syscall_map_framebuffer(struct framebuffer_info *info_out) { } -#define INITIAL_FILE_HANDLE_COUNT 128 - enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out) { assert(running_thread != 0) diff --git a/src/kernel/process.h b/src/kernel/process.h index 8915003..bc9ba50 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -115,6 +115,9 @@ void map_page_for_process( struct process *process, uint64_t physical_base, void *virtual_base, int writable, int executable, int owned); +//physical and virtual bases must be page-aligned. +void create_guard_page_for_process(struct process *process, void *virtual_base); + //virtual base must be page-aligned, in bottom p3, and not zero. //also frees physical page if owned by process. void unmap_page_for_process( diff --git a/src/kernel/syscalls.asm b/src/kernel/syscalls.asm index 27b59b7..42fb580 100644 --- a/src/kernel/syscalls.asm +++ b/src/kernel/syscalls.asm @@ -1,5 +1,5 @@ ; Calcite, src/kernel/syscalls.asm - ; Copyright 2025 Benji Dial + ; Copyright 2025-2026 Benji Dial ; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by @@ -44,6 +44,13 @@ syscall_entry: mov rcx, rax call syscall_entry_c + xor rdi, rdi + xor rsi, rsi + xor rdx, rdx + xor r8, r8 + xor r9, r9 + xor r10, r10 + pop r11 pop rcx pop rsp