commit 1e18d00d2fa0e623ef0d612616fcb6d01f2a2c10 from: int16h date: Fri Dec 5 00:53:11 2025 UTC Fix: stack underflow indicated the 16KB kernel stacks were overflowing (blockd’s saved SP was ~45KB below the top). I’ve increased the per-task kernel stack size to 64KB by bumping SCHED_STACK_PAGES from 4 to 16 commit - 9f442e154ffb1bf9c7bf317327fea00ee2d385e8 commit + 1e18d00d2fa0e623ef0d612616fcb6d01f2a2c10 blob - 5b24930828c6a570ce2dfcdf0c719d5c23630c1c blob + 47890b492261bb487177b977cfe95e004065c75e --- kernel/sched/task.c +++ kernel/sched/task.c @@ -50,7 +50,7 @@ #include "arch/fpu.h" #include "sync/spinlock.h" -#define SCHED_STACK_PAGES 4U +#define SCHED_STACK_PAGES 16U #define SCHED_STACK_SIZE (SCHED_STACK_PAGES * 4096U) enum sched_state { @@ -139,6 +139,7 @@ static bool __attribute__((unused)) name_equals(const #if defined(LENIX_DEBUG_EXIT) /* Enable LENIX_DEBUG_EXIT_VERBOSE to re-enable noisy exit/wait logging. */ #endif +static bool sched_ctx_valid_kernel_ret(const struct sched_task *task); #if defined(__x86_64__) struct switch_frame { @@ -189,6 +190,11 @@ static struct sched_task *task_list_head; static void sched_task_trampoline(void) __attribute__((noreturn)); static void sched_user_launch(void *) __attribute__((noreturn)); static void sched_idle(void *); +static bool addr_canonical(uint64_t addr); +static bool sched_ctx_valid_kernel_ret(const struct sched_task *task); +static bool sched_ctx_sp_in_range(const struct sched_task *task); +static void sched_ctx_dump(const struct sched_task *task, const char *tag); +static bool sched_current_sp_in_range(const struct sched_task *task); static void sched_request_reschedule(uint32_t cpu); static void sched_reschedule_ipi(uint32_t cpu, enum smp_ipi_reason reason); static uint32_t sched_pick_cpu(void); @@ -202,6 +208,10 @@ static void exit_status_clear_pid(uint64_t pid); #ifdef LENIX_DEBUG_EXIT static void exit_status_table_dump(const char *tag); #endif +#if defined(__x86_64__) +extern char __kernel_text_start[]; +extern char __kernel_text_end[]; +#endif #ifdef LENIX_DEBUG_EXIT static void debug_serial_write_u64(uint64_t v); static void debug_serial_write_i64(int64_t v); @@ -337,6 +347,124 @@ sched_stack_prepare(void *stack_mem) return (uint64_t *)top; } +static bool +addr_canonical(uint64_t addr) +{ + uint64_t hi = addr >> 47; + + return hi == 0 || hi == 0x1ffffULL; +} + +static bool +sched_ctx_valid_kernel_ret(const struct sched_task *task) +{ +#if defined(__x86_64__) + uint64_t sp; + struct switch_frame *frame; + uint64_t ret; + uint64_t text_lo = (uint64_t)(uintptr_t)__kernel_text_start; + uint64_t text_hi = (uint64_t)(uintptr_t)__kernel_text_end; + + if (task == NULL || task->ctx.sp == NULL) + return true; + sp = (uint64_t)(uintptr_t)task->ctx.sp; + if (!addr_canonical(sp)) + return false; + frame = (struct switch_frame *)task->ctx.sp; + ret = frame->ret; + if (!addr_canonical(ret)) + return false; + if (ret < text_lo || ret >= text_hi) + return false; + return true; +#else + (void)task; + return true; +#endif +} + +static bool +sched_ctx_sp_in_range(const struct sched_task *task) +{ + uint64_t sp; + uint64_t base; + uint64_t limit; + + if (task == NULL || task->stack_base == NULL || task->ctx.sp == NULL) + return true; + base = (uint64_t)(uintptr_t)task->stack_base; + limit = base + SCHED_STACK_SIZE; + sp = (uint64_t)(uintptr_t)task->ctx.sp; + if (!addr_canonical(sp)) + return false; + return (sp >= base) && (sp <= limit); +} + +static bool +sched_current_sp_in_range(const struct sched_task *task) +{ + uint64_t rsp; + uint64_t base; + uint64_t limit; + + if (task == NULL || task->stack_base == NULL) + return true; + __asm__ __volatile__("mov %%rsp, %0" : "=r"(rsp)); + if (!addr_canonical(rsp)) + return false; + base = (uint64_t)(uintptr_t)task->stack_base; + limit = base + SCHED_STACK_SIZE; + return (rsp >= base) && (rsp <= limit); +} + +static void +sched_ctx_dump(const struct sched_task *task, const char *tag) +{ + printk(" ctx_dump "); + if (tag != NULL) + printk(tag); + printk("\n"); + if (task == NULL) { + printk(" task=NULL\n"); + return; + } + printk(" task="); + trap_print_hex64("", (uint64_t)(uintptr_t)task); + printk(" id="); + trap_print_hex64("", sched_task_id(task)); + printk(" name="); + if (task->name != NULL) + printk(task->name); + else + printk("(none)"); + printk("\n"); + printk(" kstack base="); + trap_print_hex64("", (uint64_t)(uintptr_t)task->stack_base); + printk(" sp="); + trap_print_hex64("", (uint64_t)(uintptr_t)task->ctx.sp); + printk(" size="); + trap_print_hex64("", (uint64_t)SCHED_STACK_SIZE); +#if defined(__x86_64__) + if (task->stack_base != NULL && task->ctx.sp != NULL) { + uint64_t base = (uint64_t)(uintptr_t)task->stack_base; + uint64_t top = base + SCHED_STACK_SIZE; + uint64_t sp = (uint64_t)(uintptr_t)task->ctx.sp; + printk(" sp_offset="); + if (sp >= base && sp <= top) + trap_print_hex64("", sp - base); + else if (sp < base) + trap_print_hex64(" -", base - sp); + else + printk("above"); + } + if (task->ctx.sp != NULL) { + struct switch_frame *f = (struct switch_frame *)task->ctx.sp; + trap_print_hex64(" ret=", f->ret); + } +#endif + printk("\n"); +} + /* sched_enqueue - append @task to the run queue if it is not the idle thread. */ static void sched_enqueue(struct sched_cpu_state *state, struct sched_task *task) @@ -516,6 +644,15 @@ sched_switch(bool requeue_prev) #endif prev->state = (prev->is_idle) ? SCHED_STATE_RUNNING : prev->state; arch_fpu_save(&prev->ctx); + if (!sched_ctx_sp_in_range(next) || !sched_ctx_valid_kernel_ret(next)) { + printk("[sched] FATAL: next context invalid (sp/ret)\n"); + sched_ctx_dump(next, "next"); + panic("sched_switch invalid ctx"); + } + if (!sched_current_sp_in_range(prev)) { + printk("[sched] WARN: current stack pointer out of range\n"); + sched_ctx_dump(prev, "current"); + } arch_fpu_restore(&next->ctx); arch_switch_context(&prev->ctx, &next->ctx); } blob - dbfad6f1bbf8ba403824ac4c1dff6e3309e13c96 blob + 005090e453a921b37ccd862f12c01917bc1db58b --- kernel/sys/syscall.c +++ kernel/sys/syscall.c @@ -614,8 +614,22 @@ syscall_memset(void *dst, uint8_t value, size_t len) static void sys_fd_table_destroy(void *ptr) { - if (ptr != NULL) - kmem_free(ptr); + struct sys_fd_table *table = (struct sys_fd_table *)ptr; + + if (table == NULL) + return; + + /* Close any lingering VFS handles so servers can release resources. */ + for (int fd = 0; fd < SYS_MAX_FDS; fd++) { + if (!table->entries[fd].in_use) + continue; + if (table->entries[fd].vfs_handle != 0) + (void)sys_vfs_handle_close(table->entries[fd].vfs_handle); + table->entries[fd].in_use = false; + table->entries[fd].vfs_handle = 0; + } + + kmem_free(table); } /*