syscalls: fix execve crash and heap-related problems

This commit is contained in:
2025-12-10 23:47:21 +03:00
parent 9e68f71cd0
commit 3417fd7d13
4 changed files with 107 additions and 95 deletions
+31 -3
View File
@@ -53,6 +53,31 @@ static void kheap_bitmap_clear(uint32_t index) {
// Initialize kernel heap
void kheap_init() {
memset(kheap_bitmap, 0, sizeof(kheap_bitmap));
void* temp_phys = alloc_page();
if (!temp_phys) {
printf("CRITICAL: Failed to init heap - No physical memory\n");
while(1);
}
uint32_t heap_limit = 4 * 1024 * 1024;
for (uint32_t vaddr = KHEAP_START; vaddr < KHEAP_START + heap_limit; vaddr += 0x400000) {
// This call checks if the Page Table exists.
// If not, it allocates a new Page Table and inserts it into the Current PD.
map_page(temp_phys, (void*)vaddr, PAGE_PRESENT | PAGE_RW);
// We don't actually want the page mapped, we just wanted the Side Effect
// of creating the Page Table. So we unmap the page immediately.
// unmap_page clears the Entry, but DOES NOT free the Page Table itself.
unmap_page((void*)vaddr);
}
// Free the dummy page
free_page(temp_phys);
printf("Kernel Heap Initialized (Pre-allocated tables for 0x%X MB)\n", heap_limit / 1024 / 1024);
}
// Allocate contiguous virtual pages
@@ -88,7 +113,8 @@ void* kvalloc(size_t npages) {
for (uint32_t i = 0; i < npages; i++) {
void* phys = alloc_page();
if (!phys) {
// Out of physical memory! Unmap what we just did and fail.
debug_log("NO MEMORY FOR KVALLOC!!\n");
while(1){asm volatile("cli; hlt");}
kvfree((void*)vaddr, i);
return NULL;
}
@@ -117,15 +143,17 @@ void kvfree(void* addr, size_t npages) {
for (uint32_t i = 0; i < npages; i++) {
uint32_t current_vaddr = vaddr + i * PAGE_SIZE;
void* phys = get_physaddr((void*)current_vaddr);
if (phys) {
free_page(phys);
unmap_page((void*)current_vaddr);
} else {
printf("kvfree: no physical mapping for 0x%x\n", current_vaddr);
debug_log("kvfree: no phys mapping for %x\n", current_vaddr);
}
unmap_page((void*)current_vaddr);
kheap_bitmap_clear(start + i);
}
}
+5
View File
@@ -49,6 +49,7 @@ void init_allocator() {
}
void* alloc_page() {
asm volatile("cli");
int i, j;
// Проходим по всему битмапу
for (i = 0; i < BITMAP_SIZE; i++) {
@@ -64,6 +65,7 @@ void* alloc_page() {
//debug_log("allocating page %X\n", page_num * PAGE_SIZE);
page_bitmap[i] |= (1 << j); // Помечаем как занятый
asm volatile("sti");
return (void*)(page_num * PAGE_SIZE);
}
}
@@ -71,6 +73,7 @@ void* alloc_page() {
}
printf("no bitches? no free pages?\n");
asm volatile("sti");
return (void*)0; // Нет свободных страниц
}
@@ -86,5 +89,7 @@ void free_page(void* physaddr) {
// Вычисляем номер страницы
uint32_t page_num = addr / PAGE_SIZE;
asm volatile("cli");
clear_bit(page_num);
asm volatile("sti");
}
+26 -7
View File
@@ -348,8 +348,9 @@ int sys_execve(TrapFrame *tf) {
if (!kernel_argv) return -1;
for(int i = 0; i < argc; i++) {
kernel_argv[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE);
kernel_argv[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
strcpy(kernel_argv[i], argv[i]);
kernel_argv[i][EXECVE_MAX_ARGUMENT_SIZE] = '\0';
}
kernel_argv[argc] = NULL;
@@ -357,8 +358,9 @@ int sys_execve(TrapFrame *tf) {
if (!kernel_envp) return -1; // TODO: cleanup kernel_argv
for(int i = 0; i < envc; i++) {
kernel_envp[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE);
kernel_envp[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
strcpy(kernel_envp[i], envp[i]);
kernel_envp[i][EXECVE_MAX_ARGUMENT_SIZE] = '\0';
}
kernel_envp[envc] = NULL;
@@ -367,12 +369,22 @@ int sys_execve(TrapFrame *tf) {
if (err != 0) {
// TODO: Free kernel_argv/envp
debug_log("execve: failed to read file\n");
for(int i=0; i<argc; i++) free(kernel_argv[i]);
free(kernel_argv);
for(int i=0; i<envc; i++) free(kernel_envp[i]);
free(kernel_envp);
free(file_buffer);
return -1;
}
Elf32_Ehdr *elf_header = (Elf32_Ehdr*)file_buffer;
if (memcmp(elf_header->e_ident, "\x7F" "ELF", 4) != 0) {
// TODO: Free resources
for(int i=0; i<argc; i++) free(kernel_argv[i]);
free(kernel_argv);
for(int i=0; i<envc; i++) free(kernel_envp[i]);
free(kernel_envp);
free(file_buffer);
return -1;
}
@@ -423,7 +435,8 @@ int sys_execve(TrapFrame *tf) {
uint32_t user_esp = USER_STACK_TOP;
uint32_t envp_pointers[envc + 1];
//uint32_t envp_pointers[envc + 1];
uint32_t* envp_pointers = malloc((envc + 1) * sizeof(uint32_t));
for (int i = envc - 1; i >= 0; i--) {
size_t len = strlen(kernel_envp[i]) + 1;
user_esp -= len;
@@ -432,7 +445,8 @@ int sys_execve(TrapFrame *tf) {
}
envp_pointers[envc] = 0;
uint32_t argv_pointers[argc + 1];
//uint32_t argv_pointers[argc + 1];
uint32_t* argv_pointers = malloc((argc + 1) * sizeof(uint32_t));
for (int i = argc - 1; i >= 0; i--) {
size_t len = strlen(kernel_argv[i]) + 1;
user_esp -= len;
@@ -447,31 +461,36 @@ int sys_execve(TrapFrame *tf) {
user_esp -= (argc + 1) * sizeof(uint32_t);
memcpy((void*)user_esp, argv_pointers, (argc + 1) * sizeof(uint32_t));
free(argv_pointers);
free(envp_pointers);
user_esp -= sizeof(uint32_t);
*((uint32_t*)user_esp) = argc;
current->brk = (void*)(DivRoundUp(highest_vaddr, PAGE_SIZE) * PAGE_SIZE);
uint32_t entry_point = elf_header->e_entry;
free(file_buffer);
for(int i=0; i<argc; i++) free(kernel_argv[i]);
free(kernel_argv);
for(int i=0; i<envc; i++) free(kernel_envp[i]);
free(kernel_envp);
destroy_page_dir(old_page_dir_to_free);
tf->gs = SEG_UDATA | DPL_USER;
tf->fs = SEG_UDATA | DPL_USER;
tf->es = SEG_UDATA | DPL_USER;
tf->ds = SEG_UDATA | DPL_USER;
tf->eax = 0; tf->ecx = 0; tf->edx = 0; tf->ebx = 0;
tf->ebp = 0; tf->esi = 0; tf->edi = 0;
tf->eip = elf_header->e_entry;
tf->eip = entry_point;
tf->cs = SEG_UCODE | DPL_USER;
tf->eflags = FL_IF;
tf->usermode_esp = user_esp;
tf->usermode_ss = SEG_UDATA | DPL_USER;
destroy_page_dir(old_page_dir_to_free);
execve_return(tf);
return 0;
}
+33 -73
View File
@@ -119,78 +119,58 @@ void schedule() {
Process* prev = NULL;
p = queue;
while (p) {
if (p->state == Terminated) {
Process* next_proc = p->next;
if (p->state == Terminated && p != current) {
Process* to_free = p;
// Unlink from queue
if (prev) {
prev->next = next_proc;
prev->next = p->next;
p = p->next; // Move to next
} else {
queue = next_proc;
queue = p->next;
p = queue; // Move to head
}
// If we are terminating the currently running process, we must find a new one.
if (p == current) {
current = NULL;
// Safe to free resources now because we are NOT running on this stack
destroy_page_dir(to_free->pagedir);
free_page((void*)virt_to_phys(to_free->kstack));
free_page((void*)virt_to_phys(to_free));
// Continue loop without advancing prev (since we removed p)
continue;
}
// Free the terminated process's resources.
destroy_page_dir(p->pagedir);
free_page((void*)virt_to_phys(p->kstack));
free_page((void*)virt_to_phys(p));
p = next_proc;
} else {
prev = p;
p = p->next;
}
}
// If there are no processes left, we have a problem.
if (!queue) {
scheduler_unlock();
// Ideally, you would panic the kernel here.
// For now, we just return and hope an interrupt happens.
return;
}
Process* start_search = (current && current->next) ? current->next : queue;
if (!start_search) start_search = queue;
// Step 3: Find the next process to run using a round-robin algorithm.
Process* start_search = NULL;
if (current && current->state == Ready) {
// If the current process is still ready, start searching from the next one.
start_search = current->next;
} else {
// If the current process is not ready (e.g., it's Waiting or was Terminated),
// start the search from the beginning of the queue.
start_search = queue;
}
// Ensure start_search is not NULL (handles wrapping around).
if (!start_search) {
start_search = queue;
}
// Find the first available 'Ready' process.
Process* next = start_search;
if (next) { // Check if queue is empty
do {
if (next->state == Ready) {
// We found a process to run.
// If it's not the same one we're already running, switch to it.
if (next != current) {
tss.esp0 = next->kstack_top;
switchProcess(next); // This will update 'current' and switch contexts.
switchProcess(next);
}
// If next == current, we don't need to switch. Just continue execution.
scheduler_unlock();
return;
}
next = next->next;
if (!next) {
next = queue; // Wrap around to the beginning of the list.
}
if (!next) next = queue;
} while (next != start_search);
}
// If current is Terminated and we found no one else, we MUST run idle
// otherwise we return to a dead stack.
if (current->state == Terminated) {
// Find idle task or panic.
// Assuming first task (pid 1) is idle/init and never dies.
// For now, just unlock (unsafe if current is dead) or loop.
}
// If we get here, it means no process is in the 'Ready' state.
// This can happen if all tasks are waiting for I/O or sleeping.
// We just unlock and wait for the next interrupt to change a process's state.
scheduler_unlock();
}
@@ -220,34 +200,14 @@ void scheduler_init()
}
void task_kill(Process* proc) {
//TODO: that proc != current is sus
if (!proc || proc != current) return;
if (!proc) return;
scheduler_lock();
proc->state = Terminated;
// Remove from process queue
if (queue == proc) {
queue = proc->next;
} else {
Process* prev = queue;
while (prev && prev->next != proc) prev = prev->next;
if (prev) prev->next = proc->next;
if(proc == current)
{
schedule();
}
// Schedule next process
Process* next = queue;
while (next && next->state != Ready) next = next->next;
if (!next) next = queue; // Fallback to idle if needed
current = next;
set_page_dir(virt_to_phys(next->pagedir));
// Free resources safely from new context
destroy_page_dir(proc->pagedir);
free_page((void*)virt_to_phys(proc->kstack));
free_page((void*)virt_to_phys(proc));
scheduler_unlock();
switchProcess(next);
}