]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
scheduler: fix SMP waitpid reap race and harden socket file init
authorTulio A M Mendes <[email protected]>
Tue, 9 Jun 2026 06:19:17 +0000 (03:19 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 9 Jun 2026 06:19:17 +0000 (03:19 -0300)
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).

src/kernel/scheduler.c
src/kernel/syscall.c

index b22bf64cd521757cdee8d5ad88612ba4cbb100a5..58648bffc2406cad1bf1515b1b918e73cdc75355 100644 (file)
@@ -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;
     }
 }
 
index 439ffb71c55471ea3a8a8f142529ff1a68431f54..0e28441dd56738f819c1afe788cb27f5caae9856 100644 (file)
@@ -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 */