================================================================================ K9 process-model — source-site snapshot ================================================================================ Generated by linux-wasm/tools/k9-process-model-probe/snapshot-sources.sh from Documentation/wasm/process-model.rst §10. Pinned at K9-A2 sub-phase close (per §13 K9-A2). Snapshots across five trees: [LIN] upstream linux/ tree (kernel/exit.c, fs/pipe.c, fs/file.c, kernel/sys.c) [WAS] our wasm32-port/ tree (kernel/spawn.c K5/K7/K8 path; K9-B1 extends with exit-notification ring shape; kernel/signal.c K8 reuse for SIGCHLD; include/asm/ clone.h spawn-ring ABI mirror) [UPS] wackywasm-tools/musl-1.2.5/src/ (wait/pipe/sigchld surfaces K9 demos exercise unchanged) [OVR] wackywasm-tools/musl-src/src_overrides/ (existing K7/K8 src_overrides; K9 may add stdin/wasm32/ if stdin syscall handler needs musl-side wiring) [HWJ] hardwarejs/src/ (kernelSpawnHandler.ts per-tid registry K9-B2 reads from; worker.mjs SYS_read fd=0 / SYS_pipe2 / SYS_dup2 stubs K9-C1/B3/B2 turn into real handlers; bootKernel.ts BootKernelOptions extension point for stdinChannel?) Section legend: [DEF] the function definition (file:line + leading prose) [CALL] call sites in the relevant tree [SITE] inline excerpt of a load-bearing site (no function definition but a code shape we depend on) [K9-B] where K9-B-phases will insert NEW code (annotation only; does not exist in any tree yet) Snapshot taken against: upstream linux: v6.12 upstream musl: 1.2.5 wackywasm-tools/musl-src: K8 src_overrides state (signal/wasm32/ + internal/wasm32/) Generated: 2026-05-25 (UTC, deterministic stamp from `date -u`) Note: snapshot file deliberately omits HH:MM:SS in the stamp so re-runs on the same day produce byte-identical output. Drift detection is at line-number granularity. ================================================================================ 1. linux/kernel/exit.c — do_exit + do_wait + release_task (K9-B1 core) ================================================================================ Module: upstream child-reaping core. K9-B1's arch_notify_child_exit hook lands a wasm32-side caller into do_exit (or composes the do_exit-ish state transitions directly). do_wait is the parent-side syscall handler; release_task is the zombie-reap; do_notify_parent is the SIGCHLD post-exit step that K8's arch_signal_wake_target delivers. [DEF] linux/kernel/exit.c::do_exit(long code) kernel/exit.c:877 void __noreturn do_exit(long code) [DEF] linux/kernel/exit.c::release_task(struct task_struct *p) kernel/exit.c:241 void release_task(struct task_struct *p) [DEF] linux/kernel/exit.c::do_wait(struct wait_opts *) kernel/exit.c:1680 static long do_wait(struct wait_opts *wo) [DEF] linux/kernel/exit.c::do_notify_parent(struct task_struct *, int) [DEF] linux/kernel/exit.c::wait_consider_task(...) kernel/exit.c:1423 static int wait_consider_task(struct wait_opts *wo, int ptrace, [DEF] linux/kernel/exit.c::forget_original_parent(struct task_struct *) kernel/exit.c:692 static void forget_original_parent(struct task_struct *father, [CALL] linux/kernel/exit.c — EXIT_ZOMBIE state references: kernel/exit.c:268: && leader->exit_state == EXIT_ZOMBIE) { kernel/exit.c:674: p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { kernel/exit.c:743: tsk->exit_state = EXIT_ZOMBIE; kernel/exit.c:1138: * Handle sys_wait4 work for one task in state EXIT_ZOMBIE. We hold kernel/exit.c:1169: if (cmpxchg(&p->exit_state, EXIT_ZOMBIE, state) != EXIT_ZOMBIE) kernel/exit.c:1243: state = EXIT_ZOMBIE; kernel/exit.c:1428: * Ensure that EXIT_ZOMBIE -> EXIT_DEAD/EXIT_TRACE transition kernel/exit.c:1468: if (exit_state == EXIT_ZOMBIE) { [DEF] linux/kernel/exit.c::SYSCALL_DEFINE(wait4|waitid): kernel/exit.c:1783:SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, kernel/exit.c:1875:SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, kernel/exit.c:1902:COMPAT_SYSCALL_DEFINE4(wait4, kernel/exit.c:1917:COMPAT_SYSCALL_DEFINE5(waitid, ================================================================================ 2. linux/fs/pipe.c — pipe_read / pipe_write / alloc_pipe_info (K9-B3 core) ================================================================================ Module: upstream pipefs. K9 inherits this unchanged. The load-bearing question is whether the kernel's copy_to_user / copy_from_user calls inside pipe_read / pipe_write can target a DIFFERENT user's env.user_memory (not current's) — the K9-B2 cross-process copy work. [DEF] linux/fs/pipe.c::pipe_read(struct kiocb *, struct iov_iter *) [DEF] linux/fs/pipe.c::pipe_write(struct kiocb *, struct iov_iter *) [DEF] linux/fs/pipe.c::alloc_pipe_info(void) fs/pipe.c:790 struct pipe_inode_info *alloc_pipe_info(void) [DEF] linux/fs/pipe.c::do_pipe2(int *, int) fs/pipe.c:1018 static int do_pipe2(int __user *fildes, int flags) [DEF] linux/fs/pipe.c::SYSCALL_DEFINE(pipe|pipe2): fs/pipe.c:1040:SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags) fs/pipe.c:1045:SYSCALL_DEFINE1(pipe, int __user *, fildes) ================================================================================ 3. linux/fs/file.c — fd table operations (K9-B2 / K9-B3 mirror) ================================================================================ Module: upstream fd table. K9 inherits unchanged. dup2 + copy_files + do_close_on_exec are all upstream-clean; the shell-pipeline pattern (fork → dup2(pipe_w, STDOUT) → exec) relies on these. [DEF] linux/fs/file.c::ksys_dup3 / do_dup2: fs/file.c:1321 static int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags) fs/file.c:1198 static int do_dup2(struct files_struct *files, [DEF] linux/fs/file.c::copy_files(unsigned long, struct task_struct *) [DEF] linux/fs/file.c::do_close_on_exec(struct files_struct *) fs/file.c:796 void do_close_on_exec(struct files_struct *files) [CALL] linux/fs/ — FD_CLOEXEC references: fs/fcntl.c:463: err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; fs/fcntl.c:467: set_close_on_exec(fd, argi & FD_CLOEXEC); ================================================================================ 4. linux/kernel/sys.c — process-group syscalls (K9-C2 — mostly upstream-clean) ================================================================================ Module: upstream pgrp/session. K9 inherits unchanged. sys_setpgid / sys_setsid / sys_getpgid / sys_getpgrp all manipulate task->signal->pgrp / task->signal->session; no arch hook needed. [DEF] linux/kernel/sys.c::SYSCALL_DEFINE(setpgid|setsid|getpgid|getpgrp|getsid): kernel/sys.c:1082:SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) kernel/sys.c:1181:SYSCALL_DEFINE1(getpgid, pid_t, pid) kernel/sys.c:1188:SYSCALL_DEFINE0(getpgrp) kernel/sys.c:1195:SYSCALL_DEFINE1(getsid, pid_t, pid) kernel/sys.c:1267:SYSCALL_DEFINE0(setsid) [DEF] linux/kernel/sys.c::ksys_setpgid / ksys_getpgid (helpers): [CALL] linux/kernel/ — kill_pgrp + group signal delivery: kernel/signal.c:1926:int kill_pgrp(struct pid *pid, int sig, int priv) ================================================================================ 5. linux/include/linux/sched/signal.h — task_pgrp / task_session helpers ================================================================================ [DEF] linux/include/linux/sched/signal.h::task_pgrp/task_session/same_thread_group helpers: include/linux/sched/signal.h:686:static inline struct pid *task_pgrp(struct task_struct *task) include/linux/sched/signal.h:691:static inline struct pid *task_session(struct task_struct *task) ================================================================================ 6. linux/include/linux/uaccess.h — copy_to_user / copy_from_user (K9-B2 ref) ================================================================================ Module: upstream user-memory access. The "current" assumption is baked into copy_{to,from}_user; K9-B2 introduces wasm32-side copy_{to,from}_user_for(tid, ...) helpers that route to the target Worker's named-memory import. [DEF] linux/include/linux/uaccess.h::copy_to_user / copy_from_user: include/linux/uaccess.h:205:copy_from_user(void *to, const void __user *from, unsigned long n) include/linux/uaccess.h:217:copy_to_user(void __user *to, const void *from, unsigned long n) [CALL] linux/fs/pipe.c — copy_{to,from}_user inside pipe r/w: fs/pipe.c:1026: if (unlikely(copy_to_user(fildes, fd, sizeof(fd)))) { ================================================================================ 7. wasm32-port/kernel/ — surface inventory (what exists at K9-A2 time) ================================================================================ [SITE] wasm32-port/kernel/ files at K9-A2: wasm32-port/kernel/asm-offsets.c wasm32-port/kernel/asyncify.c wasm32-port/kernel/binfmt_wasm.c wasm32-port/kernel/clone.c wasm32-port/kernel/entry.c wasm32-port/kernel/fork.c wasm32-port/kernel/head.c wasm32-port/kernel/init.c wasm32-port/kernel/k6_a1_probes.c wasm32-port/kernel/k6_b1_probes.c wasm32-port/kernel/k6_b2_probes.c wasm32-port/kernel/k6_b4_probes.c wasm32-port/kernel/k7_u4b_probes.c wasm32-port/kernel/percpu.c wasm32-port/kernel/percpu_anchors.c wasm32-port/kernel/process.c wasm32-port/kernel/sched.c wasm32-port/kernel/sections.c wasm32-port/kernel/setup.c wasm32-port/kernel/signal.c wasm32-port/kernel/smp.c wasm32-port/kernel/spawn.c wasm32-port/kernel/switch_to.c wasm32-port/kernel/syscall_table.c wasm32-port/kernel/time.c wasm32-port/kernel/traps.c wasm32-port/kernel/wasm_module.c [SITE] wasm32-port/include/asm/ files at K9-A2: wasm32-port/include/asm/asyncify.h wasm32-port/include/asm/atomic.h wasm32-port/include/asm/barrier.h wasm32-port/include/asm/boot.h wasm32-port/include/asm/bug.h wasm32-port/include/asm/cache.h wasm32-port/include/asm/clone.h wasm32-port/include/asm/cmpxchg.h wasm32-port/include/asm/current.h wasm32-port/include/asm/elf.h wasm32-port/include/asm/futex.h wasm32-port/include/asm/host.h wasm32-port/include/asm/io.h wasm32-port/include/asm/irq.h wasm32-port/include/asm/irqflags.h wasm32-port/include/asm/linkage.h wasm32-port/include/asm/mmu_context.h wasm32-port/include/asm/page.h wasm32-port/include/asm/pgalloc.h wasm32-port/include/asm/pgtable.h wasm32-port/include/asm/processor.h wasm32-port/include/asm/ptrace.h wasm32-port/include/asm/signal.h wasm32-port/include/asm/smp.h wasm32-port/include/asm/switch_to.h wasm32-port/include/asm/syscall.h wasm32-port/include/asm/thread_info.h wasm32-port/include/asm/tlbflush.h wasm32-port/include/asm/uaccess.h wasm32-port/include/asm/vmalloc.h wasm32-port/include/asm/wasm_module.h ================================================================================ 8. wasm32-port/kernel/spawn.c — K5/K7/K8 ring producer (K9-B1 mirror site) ================================================================================ The K5/K7/K8-pattern producer. K9-B1's exit-notification ring follows the IDENTICAL submit-and-bump shape (slot allocation by atomic seq increment, payload write, ack-wait by Atomics.wait on the ack word). The producer signature for K9 is sketched as: int wasm_exit_submit(pid_t tid, int code) or alternatively K9-B1 may extend the spawn-ring shape with a WASM_SPAWN_KIND_EXIT message. The decision is K9-B1's; the substrate is identical either way. [DEF] wasm32-port/kernel/spawn.c — exported submit functions: ================================================================================ 9. wasm32-port/kernel/signal.c — K8 reuse (SIGCHLD via arch_signal_wake_target) ================================================================================ K8 implementation. K9 reuses arch_signal_wake_target unchanged for SIGCHLD-on-child-exit delivery; K9-B1's arch_notify_child_exit calls into the kernel-side do_notify_parent path which eventually calls signal_wake_up (a wrapper around signal_wake_up_state) which routes through arch_signal_wake_target. [DEF] wasm32-port/kernel/signal.c — exported functions: kernel/signal.c:123:int wasm_signal_submit(int tid, int sig, int sender_pid) kernel/signal.c:176:void arch_signal_wake_target(struct task_struct *t) ================================================================================ 10. wackywasm-tools/musl-1.2.5/src/ — wait/pipe/stdin syscall surface ================================================================================ The musl-side glue K9 demos exercise unchanged. wait4 / waitpid / wait wrappers, pipe / pipe2 wrappers, dup2 wrapper, fread/fgets/getline reading from stdin. [DEF] musl-1.2.5/src/process/wait.c + waitpid.c: src/process/waitpid.c:4:pid_t waitpid(pid_t pid, int *status, int options) src/process/wait.c:3:pid_t wait(int *status) [DEF] musl-1.2.5/src/unistd/pipe.c + pipe2.c: src/unistd/pipe2.c:6:int pipe2(int fd[2], int flag) src/unistd/pipe.c:4:int pipe(int fd[2]) [DEF] musl-1.2.5/src/unistd/dup2.c + dup3.c: src/unistd/dup2.c:6:int dup2(int old, int new) src/unistd/dup.c:4:int dup(int fd) [CALL] musl-1.2.5 — SYS_wait4 / SYS_pipe2 / SYS_dup3 / SYS_setpgid call sites: src/internal/emulate_wait4.c:4:#ifndef SYS_wait4 src/internal/emulate_wait4.c:23: if (cp) r = __syscall_cp(SYS_waitid, t, pid, &info, options|WEXITED, kru); src/internal/emulate_wait4.c:24: else r = __syscall(SYS_waitid, t, pid, &info, options|WEXITED, kru); src/internal/syscall.h:394:#ifdef SYS_wait4 src/internal/syscall.h:395:#define __sys_wait4(a,b,c,d) __syscall(SYS_wait4,a,b,c,d) src/internal/syscall.h:396:#define __sys_wait4_cp(a,b,c,d) __syscall_cp(SYS_wait4,a,b,c,d) src/linux/wait4.c:24: if (SYS_wait4_time64 == SYS_wait4 || r != -ENOSYS) src/process/posix_spawn.c:28: return __syscall(SYS_dup3, old, new, 0); src/process/posix_spawn.c:70: if ((ret=__syscall(SYS_setsid)) < 0) src/process/posix_spawn.c:74: if ((ret=__syscall(SYS_setpgid, 0, attr->__pgrp))) src/process/waitid.c:6: return syscall_cp(SYS_waitid, type, id, info, options, 0); src/unistd/dup2.c:16: while ((r=__syscall(SYS_dup3, old, new, 0))==-EBUSY); src/unistd/dup3.c:13: while ((r=__syscall(SYS_dup3, old, new, flags))==-EBUSY); src/unistd/dup3.c:21: while ((r=__syscall(SYS_dup3, old, new, flags))==-EBUSY); src/unistd/getpgid.c:6: return syscall(SYS_getpgid, pid); src/unistd/getpgrp.c:6: return __syscall(SYS_getpgid, 0); src/unistd/getsid.c:6: return syscall(SYS_getsid, pid); src/unistd/pipe.c:9: return syscall(SYS_pipe2, fd, 0); src/unistd/pipe2.c:9: int ret = __syscall(SYS_pipe2, fd, flag); src/unistd/setpgid.c:6: return syscall(SYS_setpgid, pid, pgid); src/unistd/setsid.c:6: return syscall(SYS_setsid); ================================================================================ 11. wackywasm-tools/musl-src/src_overrides/ — K8-state overrides (K9 may extend) ================================================================================ Existing src_overrides at K9-A2 (K8's state). K9 may add a new stdin/wasm32/ subtree if the stdin syscall handler needs musl-side wiring; otherwise it stays at the worker.mjs level. [SITE] wackywasm-tools/musl-src/src_overrides/ — files at K9-A2: ../wackywasm-tools/musl-src/src_overrides/env/wasm32/__init_tls.c ../wackywasm-tools/musl-src/src_overrides/internal/wasm32/syscall_ret.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/__wasm_signal_dispatch.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/sigaction.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/sigsuspend.c ../wackywasm-tools/musl-src/src_overrides/signal/wasm32/sigtimedwait.c ../wackywasm-tools/musl-src/src_overrides/thread/wasm32/__set_thread_area.c ../wackywasm-tools/musl-src/src_overrides/thread/wasm32/clone.c ../wackywasm-tools/musl-src/src_overrides/thread/wasm32/pthread_sigmask.c ================================================================================ 12. hardwarejs/src/kernelSpawnHandler.ts — per-tid registry (K9-B2 reads) ================================================================================ The K7-U4c / K8-B2 registry: tid → { wasmBytes, userMemory, signalPendingOffset }. K9-B2 reads from this registry to resolve a target tid's env.user_memory SAB during copy_to_user_for / copy_from_user_for. K9-C1 reads from it to resolve a target tid's per-Worker stdin region offset when the HardwareJS-side stdin channel writes bytes. [DEF] hardwarejs/src/kernelSpawnHandler.ts — exported types/functions: kernelSpawnHandler.ts:214:export interface KernelSpawnParentRecord { kernelSpawnHandler.ts:241:export type KernelSpawnHandler = SpawnHandler & { kernelSpawnHandler.ts:247:export interface KernelSpawnHandlerOptions { kernelSpawnHandler.ts:319:export function makeKernelSpawnHandler( [CALL] hardwarejs/src/kernelSpawnHandler.ts — SYS_read / SYS_pipe2 / SYS_dup3 / SYS_setpgid stubs: kernelSpawnHandler.ts:79:const __NR_read = 0; kernelSpawnHandler.ts:811: case __NR_read: { ================================================================================ 13. hardwarejs/src/worker.mjs — SYS_read fd=0 / SYS_pipe2 / SYS_dup2 stubs ================================================================================ The user-Worker syscall fast path. SYS_read with fd==0 is currently the K5 stub returning 0 (EOF). K9-C1 turns this into a real handler that reads from the per-Worker stdin region. [CALL] hardwarejs/src/worker.mjs — SYS_read (the K9-C1 wire-in site): [CALL] hardwarejs/src/worker.mjs — SYS_FUTEX (K9 will mirror the in-Worker dispatch shape): worker.mjs:116: const SYS_FUTEX = 202; worker.mjs:179: function handleFutex(uaddr, futex_op, val, timeout_ptr, _uaddr2, _val3) { worker.mjs:278: if (nr === SYS_FUTEX) { worker.mjs:279: return handleFutex(a0 | 0, a1 | 0, a2 | 0, a3 | 0, a4 | 0, a5 | 0); ================================================================================ 14. hardwarejs/src/bootKernel.ts — BootKernelOptions (K9-C1 extension point) ================================================================================ The K5+/K6+/K7+/K8+ public API surface. K9-C1 adds the optional stdinChannel field (UserStdinChannel interface). Default at K9-C1 is a no-op channel (read returns EOF; preserves K5/K6 regression behavior). [DEF] hardwarejs/src/bootKernel.ts — BootKernelOptions interface: bootKernel.ts:45:export interface BootKernelOptions { [CALL] hardwarejs/src/bootKernel.ts — onUserExit / onUserOutput callback shape (K9-B1 mirror): ================================================================================ 15. K9-B insertion-point summary (annotation only; no code yet) ================================================================================ [K9-B1] linux-wasm/patches/0009-exit-add-arch_notify_child_exit-hook.patch Upstream weak-hook in kernel/exit.c::do_exit (or equivalent — likely a sibling of do_notify_parent), invoked AFTER the EXIT_ZOMBIE transition so the arch can route the exit event to its embedder. [K9-B1] arch/wasm32/include/asm/process.h (NEW) struct wasm_exit_request layout (mirror of wasm_signal_request); arch_notify_child_exit prototype. [K9-B1] arch/wasm32/kernel/process.c (NEW; or extend spawn.c) — kernel-side hook; composes a wasm_exit_request payload, submits via the new exit-notification ring (or extends spawn-ring with WASM_SPAWN_KIND_EXIT). [K9-B1] hardwarejs/src/exitNotificationRing.ts (NEW) — JS-side ring producer (HardwareJS observes the user Worker's 'exit' event, submits the exit payload via the ring). [K9-B2] arch/wasm32/include/asm/uaccess.h (extend) + arch/wasm32/kernel/uaccess.c (extend) — copy_to_user_for(tid, ...) / copy_from_user_for(tid, ...) helpers reading/writing the target Worker's named-memory import. [K9-B2] hardwarejs/src/kernelSpawnHandler.ts (extend) — name-and-import every fork-mode user Worker's env.user_memory into the kernel Worker as env.user_memory_ at spawn time (extends K7-U4c thread-mode-only). [K9-B3] hardwarejs/src/kernelSpawnHandler.ts (extend) — SYS_pipe2 handler that allocates a kernel-side ring buffer + returns two fds; SYS_read/SYS_write handlers that route fd-by-fd-table-lookup. [K9-C1] arch/wasm32/include/asm/stdin.h (NEW) struct wasm_stdin_region layout (16-byte header + ring buffer) + arch_user_stdin_region accessor. [K9-C1] hardwarejs/src/stdinChannel.ts (NEW) UserStdinChannel interface + makeStdinChannel factory. [K9-C1] hardwarejs/src/worker.mjs::SYS_read fd==0 case (extend) — read from the per-Worker stdin region at tail; Atomics.wait on read_notify when empty; honor signal-pending pre/post-check (K8-B1 shape). [K9-C1] hardwarejs/src/bootKernel.ts (extend) — BootKernelOptions.stdinChannel? optional field; default no-op; threading through to kernelSpawnHandler. [K9-C2] (all upstream-clean: setpgid/getpgid/setsid/getpgrp/ getsid + kill_pgrp). Acceptance test only. ================================================================================ END OF SNAPSHOT ================================================================================