From: Tulio A M Mendes Date: Tue, 9 Jun 2026 06:19:17 +0000 (-0300) Subject: scheduler: fix SMP waitpid reap race and harden socket file init X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=995fc6470d7e398f74a95f94900ad9870975bc56;p=AdrOS.git scheduler: fix SMP waitpid reap race and harden socket file init Prevent process_waitpid() from reaping a zombie child while it is still the current process on another CPU. This avoids freeing a live process struct and kernel stack during the exit-to-schedule handoff under SMP, which matched the intermittent invalid-opcode panic seen after the job-control path in fulltest. Also zero-initialize struct file allocations in the socket() and accept() paths so mount_root and any future fields cannot inherit heap garbage. Validation: make -j$(nproc), make test-host, make test SMOKE_SMP=4, make test-battery (153/153 PASS). --- diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index b22bf64c..58648bff 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -351,6 +351,17 @@ static struct process* process_find_locked(uint32_t pid) { return NULL; } +static int process_is_current_somewhere(struct process* p) { + if (!p) return 0; +#ifdef __i386__ + uint32_t cpu = p->cpu_id < SCHED_MAX_CPUS ? p->cpu_id : 0; + struct percpu_data* pcpu = percpu_get_ptr(cpu); + return pcpu && *(struct process**)((uint8_t*)pcpu + 12) == p; +#else + return current_process == p; +#endif +} + static void process_reap_locked(struct process* p) { if (!p) return; if (p->pid == 0) return; @@ -537,6 +548,7 @@ int process_waitpid(int pid, int* status_out, uint32_t options) { struct process* it = ready_queue_head; struct process* start = it; int found_child = 0; + int retry_reap = 0; if (it) { do { @@ -544,6 +556,10 @@ int process_waitpid(int pid, int* status_out, uint32_t options) { found_child = 1; if (pid == -1 || (int)it->pid == pid) { if (it->state == PROCESS_ZOMBIE) { + if (process_is_current_somewhere(it)) { + retry_reap = 1; + break; + } int retpid = (int)it->pid; int st = it->exit_status; process_reap_locked(it); @@ -557,6 +573,15 @@ int process_waitpid(int pid, int* status_out, uint32_t options) { } while (it && it != start); } + if (retry_reap) { + spin_unlock_irqrestore(&sched_lock, flags); + if ((options & WNOHANG) != 0) { + return 0; + } + process_sleep(1); + continue; + } + if (!found_child) { spin_unlock_irqrestore(&sched_lock, flags); return -ECHILD; @@ -577,23 +602,9 @@ int process_waitpid(int pid, int* status_out, uint32_t options) { hal_cpu_enable_interrupts(); schedule(); - if (current_process->wait_result_pid != -1) { - int rp = current_process->wait_result_pid; - int st = current_process->wait_result_status; - - uintptr_t flags2 = spin_lock_irqsave(&sched_lock); - struct process* child = process_find_locked((uint32_t)rp); - if (child && child->parent_pid == current_process->pid && child->state == PROCESS_ZOMBIE) { - process_reap_locked(child); - } - spin_unlock_irqrestore(&sched_lock, flags2); - - current_process->waiting = 0; - current_process->wait_pid = -1; - current_process->wait_result_pid = -1; - if (status_out) *status_out = st; - return rp; - } + current_process->waiting = 0; + current_process->wait_pid = -1; + current_process->wait_result_pid = -1; } } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 439ffb71..0e28441d 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -4944,6 +4944,7 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n if (!sn) { ksocket_close(sid); sc_ret(regs) = (uint32_t)-ENOMEM; return; } struct file* f = (struct file*)kmalloc(sizeof(struct file)); if (!f) { sock_node_close(sn); sc_ret(regs) = (uint32_t)-ENOMEM; return; } + memset(f, 0, sizeof(*f)); f->node = sn; f->offset = 0; f->flags = 2; /* O_RDWR — sockets are bidirectional */ @@ -4983,6 +4984,7 @@ static void extended_syscall_dispatch(struct registers* regs, uint32_t syscall_n if (!sn) { ksocket_close(new_sid); sc_ret(regs) = (uint32_t)-ENOMEM; return; } struct file* f = (struct file*)kmalloc(sizeof(struct file)); if (!f) { sock_node_close(sn); sc_ret(regs) = (uint32_t)-ENOMEM; return; } + memset(f, 0, sizeof(*f)); f->node = sn; f->offset = 0; f->flags = 2; /* O_RDWR — sockets are bidirectional */