add guard pages to stack; dynamically allocate user stacks; zero non-preserved registers before returning from syscalls

This commit is contained in:
Benji Dial 2026-01-02 22:39:57 -05:00
parent 5821f51f02
commit 085ae6ba53
7 changed files with 113 additions and 31 deletions

View file

@ -76,7 +76,23 @@ isr_exception_common:
mov rax, cr3 mov rax, cr3
push rax push rax
mov rdi, rsp 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: isr_irq_common:
push rax push rax

View file

@ -22,6 +22,8 @@
// https://osdev.wiki/wiki/Task_State_Segment // https://osdev.wiki/wiki/Task_State_Segment
#include "interrupts.h" #include "interrupts.h"
#include "process.h"
#include "paging.h"
#include "debug.h" #include "debug.h"
//interrupts.asm depends on layout //interrupts.asm depends on layout
@ -63,7 +65,7 @@ const char *exception_names[32] = {
"hypervisor injection exception", "vmm communication exception", "security exception", 0 "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) assert(parameter->exception_number < 32 && exception_names[parameter->exception_number] != 0)
@ -101,10 +103,24 @@ const char *exception_names[32] = {
} }
//referenced in interrupts.asm //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) {
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); unhandled_exception(parameter);
} }

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/paging.c /* 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 * 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 * 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 //defined in paging.asm
void invlpg(void *address); void invlpg(void *address);
void map_in_kernel_page_table( void map_in_kernel_page_table(uint64_t physical_base, void *virtual_base, int writable, int executable) {
uint64_t physical_base, void *virtual_base,
int writable, int executable) {
uint64_t virtual_base_u64 = (uint64_t)virtual_base; uint64_t virtual_base_u64 = (uint64_t)virtual_base;
assert(virtual_base_u64 >= 0xffffffffc0000000); 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) { void unmap_kernel_page(void *virtual_base) {
uint64_t virtual_base_u64 = (uint64_t)virtual_base; uint64_t virtual_base_u64 = (uint64_t)virtual_base;

View file

@ -1,5 +1,5 @@
/* Calcite, src/kernel/paging.h /* 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 * 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 * 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( void map_in_kernel_page_table(
uint64_t physical_base, void *virtual_base, int writable, int executable); 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. //unmaps one page. base should be page-aligned and within kernel range.
void unmap_kernel_page(void *virtual_base); void unmap_kernel_page(void *virtual_base);

View file

@ -69,11 +69,8 @@ void create_process(struct process *process_out) {
} }
void map_page_for_process( static void get_page_table_entry(
struct process *process, uint64_t physical_base, struct process *process, void *virtual_base, int *p1i_out, int *p2i_out, int *p3i_out) {
void *virtual_base, int writable, int executable, int owned) {
assert(physical_base % 4096 == 0)
uint64_t vma = (uint64_t)virtual_base; uint64_t vma = (uint64_t)virtual_base;
assert(vma % 4096 == 0) assert(vma % 4096 == 0)
@ -112,6 +109,21 @@ void map_page_for_process(
process->owned_pages_bitmaps[p3i][p2i][i] = 0; 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) assert(process->p1_virtual_bases[p3i][p2i][p1i] == 0)
process->p1_virtual_bases[p3i][p2i][p1i] = 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( void unmap_page_for_process(
struct process *process, void *virtual_base) { struct process *process, void *virtual_base) {
@ -532,7 +555,8 @@ void destroy_thread(struct thread *thread) {
--thread->process->n_threads; --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); unmap_and_free_kernel_page(p);
if (thread->process->n_threads == 0) if (thread->process->n_threads == 0)
@ -545,22 +569,25 @@ void destroy_thread(struct thread *thread) {
} }
#define INITIAL_STACK_SIZE (16 << 20) #define MAX_STACK_SIZE ((16 << 20) - 4096)
#define INITIAL_SYSCALL_STACK_SIZE 16384 #define INITIAL_STACK_SIZE 16384
#define SYSCALL_STACK_SIZE 12288
void create_thread(struct process *process, struct thread *thread_out) { 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 = 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(); uint64_t pma = take_free_physical_page();
map_page_for_process( map_page_for_process(
process, pma, process, pma,
stack_bottom_vma + i * 4096, stack_bottom_vma + i,
1, 0, 1); 1, 0, 1);
void *kvma = find_free_kernel_region(4096); 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(); uint64_t pma = take_free_physical_page();
map_in_kernel_page_table( 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->process = process;
thread_out->stack_bottom = stack_bottom_vma; 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_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; ++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) { enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out) {
assert(running_thread != 0) assert(running_thread != 0)

View file

@ -115,6 +115,9 @@ void map_page_for_process(
struct process *process, uint64_t physical_base, struct process *process, uint64_t physical_base,
void *virtual_base, int writable, int executable, int owned); 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. //virtual base must be page-aligned, in bottom p3, and not zero.
//also frees physical page if owned by process. //also frees physical page if owned by process.
void unmap_page_for_process( void unmap_page_for_process(

View file

@ -1,5 +1,5 @@
; Calcite, src/kernel/syscalls.asm ; 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 ; 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 ; it under the terms of the GNU General Public License as published by
@ -44,6 +44,13 @@ syscall_entry:
mov rcx, rax mov rcx, rax
call syscall_entry_c 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 r11
pop rcx pop rcx
pop rsp pop rsp