From 278b20825815ec04a22fd1c2fcfedbd38e116e6d Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Wed, 14 Oct 2026 07:32:15 +0200 Subject: [PATCH] feat: add process termination codes and SYS_ABORT; whole-process abort() from any thread in libc and montauk-rt --- kernel/src/Api/BuildNo.hpp | 2 +- kernel/src/Api/Process.hpp | 13 +++++++-- kernel/src/Api/Syscall.cpp | 3 ++ kernel/src/Api/Syscall.hpp | 5 ++++ kernel/src/Common/TerminationCodes.hpp | 37 +++++++++++++++++++++++++ kernel/src/Hal/IDT.cpp | 16 +++++------ kernel/src/Sched/Scheduler.cpp | 2 +- kernel/src/Sched/Scheduler.hpp | 5 ++-- programs/include/Api/Syscall.hpp | 5 ++++ programs/include/libc/montauk.h | 1 + programs/include/montauk/statuscodes.h | 27 ++++++++++++++++++ programs/include/montauk/syscall.h | 5 ++++ programs/lib/libc/libc.c | 9 +++--- programs/rust/montauk-rt/src/process.rs | 12 +++----- programs/src/shell/exec.cpp | 21 ++++++++------ 15 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 kernel/src/Common/TerminationCodes.hpp diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 90659fe..0c359d8 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 306 +#define MONTAUK_BUILD_NUMBER 308 diff --git a/kernel/src/Api/Process.hpp b/kernel/src/Api/Process.hpp index 9b775f1..f1e3801 100644 --- a/kernel/src/Api/Process.hpp +++ b/kernel/src/Api/Process.hpp @@ -220,10 +220,17 @@ namespace montauk::abi { return count; } + // Flag every thread of the caller's process with TerminatedAborted, then + // exit this one. A sibling's ExitProcess ends only itself; the main + // thread and the other siblings follow via killPending. + static void Sys_Abort() { + Sched::KillProcess(Sched::GetCurrentPid(), TerminatedAborted); + Sched::ExitProcess(); + } + static int Sys_Kill(int pid) { - // A process may always kill itself: a sibling thread has no other - // way to take the whole process down (SYS_EXIT from a sibling ends - // only that thread), which abort()/panic=abort need. + // A process may always kill itself (kill(getpid(), ...)); waiters + // see TerminatedKilled. abort() uses SYS_ABORT instead. if (pid == Sched::GetCurrentPid()) return Sched::KillProcess(pid); if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) { int ancestor = pid; diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index c994b2d..704bd13 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -400,6 +400,9 @@ namespace montauk::abi { return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2); case SYS_KILL: return (int64_t)Sys_Kill((int)frame->arg1); + case SYS_ABORT: + Sys_Abort(); + return Success; case SYS_SETSESSION: return (int64_t)Sys_SetSession(); case SYS_KILLSESSION: diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index 233324c..baf43fb 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -375,6 +375,11 @@ namespace montauk::abi { static constexpr uint64_t FUTEX_WAIT_FOREVER = ~0ULL; static constexpr uint32_t FUTEX_WAKE_ALL = 0xFFFFFFFFu; + /* Abnormal termination from any thread: ends the whole process (every + thread) with exit code TerminatedAborted (256+SIGABRT). SYS_EXIT from a + sibling thread ends only that thread, so abort() needs its own call. */ + static constexpr uint64_t SYS_ABORT = 198; // () -> does not return + /* Kernel-owned process capabilities. User identities may namespace per-user resources, but never participate in authorization decisions. */ static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0; diff --git a/kernel/src/Common/TerminationCodes.hpp b/kernel/src/Common/TerminationCodes.hpp new file mode 100644 index 0000000..671aaaa --- /dev/null +++ b/kernel/src/Common/TerminationCodes.hpp @@ -0,0 +1,37 @@ +/* + * TerminationCodes.hpp + * MontaukOS process termination codes. + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +#include + +// Exit code recorded when the kernel ends a process, as opposed to the +// process calling SYS_EXIT (which is masked to 0..255). Encoded as +// 256 + POSIX signal number so libc waitpid can report WTERMSIG. +// Mirrored in userspace at programs/include/montauk/statuscodes.h. +enum TerminationCodes { + TerminationBase = 256, + TerminatedIllegalInstruction = TerminationBase + 4, // SIGILL + TerminatedBreakpoint = TerminationBase + 5, // SIGTRAP + TerminatedAborted = TerminationBase + 6, // SIGABRT + TerminatedArithmeticError = TerminationBase + 8, // SIGFPE + TerminatedKilled = TerminationBase + 9, // SIGKILL + TerminatedAccessViolation = TerminationBase + 11 // SIGSEGV +}; + +// Termination code for a user-mode CPU exception. The exact vector is +// kept in the crash report; this is only the category. +inline TerminationCodes TerminationFromVector(uint8_t vector) { + switch (vector) { + case 0x06: return TerminatedIllegalInstruction; // #UD + case 0x01: // #DB + case 0x03: return TerminatedBreakpoint; // #BP + case 0x00: // #DE + case 0x10: // #MF + case 0x13: return TerminatedArithmeticError; // #XM + default: return TerminatedAccessViolation; // #PF, #GP, #SS, #NP, #AC, #OF, #BR, #CP, ... + } +} diff --git a/kernel/src/Hal/IDT.cpp b/kernel/src/Hal/IDT.cpp index 6aaa6fd..0c58057 100644 --- a/kernel/src/Hal/IDT.cpp +++ b/kernel/src/Hal/IDT.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -170,23 +171,20 @@ namespace Hal { CrashReport::AddReport(rep); Sched::SpawnCrashPad(proc->pid); - // Record a killed-by-signal exit code (256+sig) so a parent - // blocked in SYS_WAITPID can tell a crash from a clean exit. + // Record a termination code (256+sig) so a parent blocked in + // SYS_WAITPID can tell a crash from a clean exit. { - int sig = 11; /* SIGSEGV */ - if (i == 0x00) sig = 8; /* #DE -> SIGFPE */ - else if (i == 0x06) sig = 4; /* #UD -> SIGILL */ - else if (i == 0x10 || i == 0x13) sig = 8; /* x87/SIMD FP */ + int code = TerminationFromVector(i); // ExitProcess from a sibling thread ends only that thread, // which would leave the process running with the crash // swallowed. Flag the whole process (main thread and every // sibling) first, carrying the crash code rather than - // KillProcess's default SIGKILL, so waiters see the fault. + // KillProcess's default TerminatedKilled, so waiters see the fault. if (Sched::GetCurrentThreadPtr() != proc) { - Sched::KillProcess(proc->pid, 256 + sig); + Sched::KillProcess(proc->pid, code); } else { - Sched::SetProcessExitCode(proc->pid, 256 + sig); + Sched::SetProcessExitCode(proc->pid, code); } } diff --git a/kernel/src/Sched/Scheduler.cpp b/kernel/src/Sched/Scheduler.cpp index 5476c19..b27f2d3 100644 --- a/kernel/src/Sched/Scheduler.cpp +++ b/kernel/src/Sched/Scheduler.cpp @@ -1885,7 +1885,7 @@ namespace Sched { continue; } - primary.exitCode = 256 + 9; /* killed (SIGKILL) */ + primary.exitCode = TerminatedKilled; primary.killPending = true; if (primary.state == ProcessState::Blocked) { primary.state = ProcessState::Ready; diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index d63c746..a19f827 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include #include #include @@ -303,9 +304,9 @@ namespace Sched { // Kill a process by PID. If the process is running on another CPU, // sets a kill-pending flag checked on the next timer tick. exitCode is - // what a waiter sees (256+sig; SIGKILL by default). + // what a waiter sees (a TerminationCodes value). // Returns 0 on success, -1 on failure. - int KillProcess(int pid, int exitCode = 256 + 9); + int KillProcess(int pid, int exitCode = TerminatedKilled); // Start a new process session for the caller, or terminate every process // belonging to a session. KillSession returns the number of live members diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index 38341f1..0db1028 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -288,6 +288,11 @@ namespace montauk::abi { static constexpr uint64_t FUTEX_WAIT_FOREVER = ~0ULL; static constexpr uint32_t FUTEX_WAKE_ALL = 0xFFFFFFFFu; + /* Abnormal termination from any thread: ends the whole process (every + thread) with exit code TerminatedAborted (256+SIGABRT). SYS_EXIT from a + sibling thread ends only that thread, so abort() needs its own call. */ + static constexpr uint64_t SYS_ABORT = 198; // () -> does not return + /* Kernel-owned process capabilities. User identities may namespace per-user resources, but never participate in authorization decisions. */ static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0; diff --git a/programs/include/libc/montauk.h b/programs/include/libc/montauk.h index 23a705c..d189c08 100644 --- a/programs/include/libc/montauk.h +++ b/programs/include/libc/montauk.h @@ -221,6 +221,7 @@ extern "C" { #define MTK_SYS_CHILDIO_WRITEKEY_EX 195 #define MTK_SYS_FUTEX_WAIT 196 #define MTK_SYS_FUTEX_WAKE 197 +#define MTK_SYS_ABORT 198 /* @SYSCALLS-END */ /* Syscall status codes (montauk/statuscodes.h for C++). */ diff --git a/programs/include/montauk/statuscodes.h b/programs/include/montauk/statuscodes.h index 091e9b0..a1fad9a 100644 --- a/programs/include/montauk/statuscodes.h +++ b/programs/include/montauk/statuscodes.h @@ -101,6 +101,33 @@ inline const char* GetStatusExplanationText(StatusCode statusCode) { return GetStatusText(statusCode); } +// Process termination codes: what waitpid returns when the kernel ended the +// process (crash, kill, abort) instead of it calling exit. 256 + POSIX +// signal number, so normal exit codes (0..255) can never collide. +// Analogous to kernel/src/Common/TerminationCodes.hpp; same values. +typedef enum { + TerminationBase = 256, + TerminatedIllegalInstruction = TerminationBase + 4, // SIGILL + TerminatedBreakpoint = TerminationBase + 5, // SIGTRAP + TerminatedAborted = TerminationBase + 6, // SIGABRT + TerminatedArithmeticError = TerminationBase + 8, // SIGFPE + TerminatedKilled = TerminationBase + 9, // SIGKILL + TerminatedAccessViolation = TerminationBase + 11 // SIGSEGV +} TerminationCode; + +// Shared with C (libc.c), so no nullptr. Returns 0 for an unknown code. +inline const char* GetTerminationText(int exitCode) { + switch (exitCode) { + case TerminatedIllegalInstruction: return "Illegal instruction"; + case TerminatedBreakpoint: return "Breakpoint"; + case TerminatedAborted: return "Aborted"; + case TerminatedArithmeticError: return "Arithmetic error"; + case TerminatedKilled: return "Killed"; + case TerminatedAccessViolation: return "Access violation"; + default: return 0; + } +} + #ifdef __cplusplus } #endif diff --git a/programs/include/montauk/syscall.h b/programs/include/montauk/syscall.h index b2ac9a1..c65e31f 100644 --- a/programs/include/montauk/syscall.h +++ b/programs/include/montauk/syscall.h @@ -527,6 +527,11 @@ namespace montauk { inline int kill(int pid) { return (int)syscall1(montauk::abi::SYS_KILL, (uint64_t)pid); } + // End the whole process from any thread (exit code TerminatedAborted). + [[noreturn]] inline void abort() { + syscall0(montauk::abi::SYS_ABORT); + __builtin_unreachable(); + } inline int setsession() { return (int)syscall0(montauk::abi::SYS_SETSESSION); } diff --git a/programs/lib/libc/libc.c b/programs/lib/libc/libc.c index 44d8207..8f7c434 100644 --- a/programs/lib/libc/libc.c +++ b/programs/lib/libc/libc.c @@ -130,6 +130,7 @@ static inline long _mtk_syscall4(long nr, long a1, long a2, long a3, long a4) { #define SYS_GETENVIRON 171 #define SYS_SETENVIRON 172 #define SYS_SPAWN_ENV 173 +#define SYS_ABORT 198 /* ======================================================================== errno @@ -1243,9 +1244,9 @@ void exit(int status) { __builtin_unreachable(); } +/* Ends the whole process from any thread; waitpid sees WTERMSIG == SIGABRT. */ void abort(void) { - _mtk_syscall1(SYS_PRINT, (long)"abort() called\n"); - _mtk_syscall1(SYS_EXIT, 1); + _mtk_syscall0(SYS_ABORT); __builtin_unreachable(); } @@ -4055,8 +4056,8 @@ pid_t waitpid(pid_t pid, int *status, int options) { } long code = _mtk_syscall1(SYS_WAITPID, (long)pid); if (status != NULL) { - if (code >= 256) { - *status = (int)((code - 256) & 0x7F); /* signaled */ + if (code >= TerminationBase) { + *status = (int)((code - TerminationBase) & 0x7F); /* signaled */ } else { *status = (int)((code & 0xFF) << 8); /* exited */ } diff --git a/programs/rust/montauk-rt/src/process.rs b/programs/rust/montauk-rt/src/process.rs index 9b0d318..85f30c5 100644 --- a/programs/rust/montauk-rt/src/process.rs +++ b/programs/rust/montauk-rt/src/process.rs @@ -18,16 +18,12 @@ pub fn id() -> u32 { unsafe { sys::syscall0(sys::SYS_GETPID) as u32 } } -/// Terminate the whole process from any thread. +/// Terminate the whole process from any thread. Waiters see exit code +/// TerminatedAborted (256 + SIGABRT). pub fn abort() -> ! { unsafe { - if crate::thread::current_id() == id() { - sys::syscall1(sys::SYS_EXIT, 134); - } else { - // Sibling: SYS_EXIT would end only this thread. Self-kill flags - // every thread; park here until the kernel reaps us. - sys::syscall1(sys::SYS_KILL, id() as u64); - } + sys::syscall0(sys::SYS_ABORT); + // Not reached. No panic here: the panic handler calls abort(). loop { sys::syscall0(sys::SYS_YIELD); } diff --git a/programs/src/shell/exec.cpp b/programs/src/shell/exec.cpp index 1071681..9dfc268 100644 --- a/programs/src/shell/exec.cpp +++ b/programs/src/shell/exec.cpp @@ -23,9 +23,9 @@ static void print_exit_code(const int exitCode) { if (code == 0) return; char buf[16]; int n = 0; - if (code >= 256) { + if (code >= montauk::TerminationBase) { montauk::print("[terminated by signal "); - code -= 256; + code -= montauk::TerminationBase; } else { montauk::print("[exit code "); } @@ -39,13 +39,16 @@ static void print_exit_code(const int exitCode) { montauk::print(c); } - if (exitCode > 0 && exitCode <= montauk::MaxStatusCode) { - const char* statusText = montauk::GetStatusText((montauk::StatusCode)exitCode); - - if (statusText != nullptr) { - montauk::print(" - "); - montauk::print(statusText); - } + const char* statusText = nullptr; + if (exitCode >= montauk::TerminationBase) { + statusText = montauk::GetTerminationText(exitCode); + } else if (exitCode > 0 && exitCode <= montauk::MaxStatusCode) { + statusText = montauk::GetStatusText((montauk::StatusCode)exitCode); + } + + if (statusText != nullptr) { + montauk::print(" - "); + montauk::print(statusText); } montauk::print("]\n");