feat: add process termination codes and SYS_ABORT; whole-process abort() from any thread in libc and montauk-rt
This commit is contained in:
1 parent
f8b8f9610f
commit
278b208258
15 files changed
+126
-37
No files matched your search
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 306
|
#define MONTAUK_BUILD_NUMBER 308
|
||||||
@@ -220,10 +220,17 @@ namespace montauk::abi {
|
|||||||
return count;
|
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) {
|
static int Sys_Kill(int pid) {
|
||||||
// A process may always kill itself: a sibling thread has no other
|
// A process may always kill itself (kill(getpid(), ...)); waiters
|
||||||
// way to take the whole process down (SYS_EXIT from a sibling ends
|
// see TerminatedKilled. abort() uses SYS_ABORT instead.
|
||||||
// only that thread), which abort()/panic=abort need.
|
|
||||||
if (pid == Sched::GetCurrentPid()) return Sched::KillProcess(pid);
|
if (pid == Sched::GetCurrentPid()) return Sched::KillProcess(pid);
|
||||||
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) {
|
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) {
|
||||||
int ancestor = pid;
|
int ancestor = pid;
|
||||||
|
|||||||
@@ -400,6 +400,9 @@ namespace montauk::abi {
|
|||||||
return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2);
|
return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2);
|
||||||
case SYS_KILL:
|
case SYS_KILL:
|
||||||
return (int64_t)Sys_Kill((int)frame->arg1);
|
return (int64_t)Sys_Kill((int)frame->arg1);
|
||||||
|
case SYS_ABORT:
|
||||||
|
Sys_Abort();
|
||||||
|
return Success;
|
||||||
case SYS_SETSESSION:
|
case SYS_SETSESSION:
|
||||||
return (int64_t)Sys_SetSession();
|
return (int64_t)Sys_SetSession();
|
||||||
case SYS_KILLSESSION:
|
case SYS_KILLSESSION:
|
||||||
|
|||||||
@@ -375,6 +375,11 @@ namespace montauk::abi {
|
|||||||
static constexpr uint64_t FUTEX_WAIT_FOREVER = ~0ULL;
|
static constexpr uint64_t FUTEX_WAIT_FOREVER = ~0ULL;
|
||||||
static constexpr uint32_t FUTEX_WAKE_ALL = 0xFFFFFFFFu;
|
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
|
/* Kernel-owned process capabilities. User identities may namespace
|
||||||
per-user resources, but never participate in authorization decisions. */
|
per-user resources, but never participate in authorization decisions. */
|
||||||
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* TerminationCodes.hpp
|
||||||
|
* MontaukOS process termination codes.
|
||||||
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// 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, ...
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <Memory/Heap.hpp>
|
#include <Memory/Heap.hpp>
|
||||||
#include <Common/Panic.hpp>
|
#include <Common/Panic.hpp>
|
||||||
|
#include <Common/TerminationCodes.hpp>
|
||||||
#include <Platform/Registers.hpp>
|
#include <Platform/Registers.hpp>
|
||||||
#include <CppLib/Stream.hpp>
|
#include <CppLib/Stream.hpp>
|
||||||
#include <Memory/PageFrameAllocator.hpp>
|
#include <Memory/PageFrameAllocator.hpp>
|
||||||
@@ -170,23 +171,20 @@ namespace Hal {
|
|||||||
CrashReport::AddReport(rep);
|
CrashReport::AddReport(rep);
|
||||||
Sched::SpawnCrashPad(proc->pid);
|
Sched::SpawnCrashPad(proc->pid);
|
||||||
|
|
||||||
// Record a killed-by-signal exit code (256+sig) so a parent
|
// Record a termination code (256+sig) so a parent blocked in
|
||||||
// blocked in SYS_WAITPID can tell a crash from a clean exit.
|
// SYS_WAITPID can tell a crash from a clean exit.
|
||||||
{
|
{
|
||||||
int sig = 11; /* SIGSEGV */
|
int code = TerminationFromVector(i);
|
||||||
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 */
|
|
||||||
|
|
||||||
// ExitProcess from a sibling thread ends only that thread,
|
// ExitProcess from a sibling thread ends only that thread,
|
||||||
// which would leave the process running with the crash
|
// which would leave the process running with the crash
|
||||||
// swallowed. Flag the whole process (main thread and every
|
// swallowed. Flag the whole process (main thread and every
|
||||||
// sibling) first, carrying the crash code rather than
|
// 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) {
|
if (Sched::GetCurrentThreadPtr() != proc) {
|
||||||
Sched::KillProcess(proc->pid, 256 + sig);
|
Sched::KillProcess(proc->pid, code);
|
||||||
} else {
|
} else {
|
||||||
Sched::SetProcessExitCode(proc->pid, 256 + sig);
|
Sched::SetProcessExitCode(proc->pid, code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1885,7 +1885,7 @@ namespace Sched {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
primary.exitCode = 256 + 9; /* killed (SIGKILL) */
|
primary.exitCode = TerminatedKilled;
|
||||||
primary.killPending = true;
|
primary.killPending = true;
|
||||||
if (primary.state == ProcessState::Blocked) {
|
if (primary.state == ProcessState::Blocked) {
|
||||||
primary.state = ProcessState::Ready;
|
primary.state = ProcessState::Ready;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <Common/TerminationCodes.hpp>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <Api/Syscall.hpp>
|
#include <Api/Syscall.hpp>
|
||||||
|
|
||||||
@@ -303,9 +304,9 @@ namespace Sched {
|
|||||||
|
|
||||||
// Kill a process by PID. If the process is running on another CPU,
|
// 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
|
// 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.
|
// 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
|
// Start a new process session for the caller, or terminate every process
|
||||||
// belonging to a session. KillSession returns the number of live members
|
// belonging to a session. KillSession returns the number of live members
|
||||||
|
|||||||
@@ -288,6 +288,11 @@ namespace montauk::abi {
|
|||||||
static constexpr uint64_t FUTEX_WAIT_FOREVER = ~0ULL;
|
static constexpr uint64_t FUTEX_WAIT_FOREVER = ~0ULL;
|
||||||
static constexpr uint32_t FUTEX_WAKE_ALL = 0xFFFFFFFFu;
|
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
|
/* Kernel-owned process capabilities. User identities may namespace
|
||||||
per-user resources, but never participate in authorization decisions. */
|
per-user resources, but never participate in authorization decisions. */
|
||||||
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ extern "C" {
|
|||||||
#define MTK_SYS_CHILDIO_WRITEKEY_EX 195
|
#define MTK_SYS_CHILDIO_WRITEKEY_EX 195
|
||||||
#define MTK_SYS_FUTEX_WAIT 196
|
#define MTK_SYS_FUTEX_WAIT 196
|
||||||
#define MTK_SYS_FUTEX_WAKE 197
|
#define MTK_SYS_FUTEX_WAKE 197
|
||||||
|
#define MTK_SYS_ABORT 198
|
||||||
/* @SYSCALLS-END */
|
/* @SYSCALLS-END */
|
||||||
|
|
||||||
/* Syscall status codes (montauk/statuscodes.h for C++). */
|
/* Syscall status codes (montauk/statuscodes.h for C++). */
|
||||||
|
|||||||
@@ -101,6 +101,33 @@ inline const char* GetStatusExplanationText(StatusCode statusCode) {
|
|||||||
return GetStatusText(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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -527,6 +527,11 @@ namespace montauk {
|
|||||||
inline int kill(int pid) {
|
inline int kill(int pid) {
|
||||||
return (int)syscall1(montauk::abi::SYS_KILL, (uint64_t)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() {
|
inline int setsession() {
|
||||||
return (int)syscall0(montauk::abi::SYS_SETSESSION);
|
return (int)syscall0(montauk::abi::SYS_SETSESSION);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_GETENVIRON 171
|
||||||
#define SYS_SETENVIRON 172
|
#define SYS_SETENVIRON 172
|
||||||
#define SYS_SPAWN_ENV 173
|
#define SYS_SPAWN_ENV 173
|
||||||
|
#define SYS_ABORT 198
|
||||||
|
|
||||||
/* ========================================================================
|
/* ========================================================================
|
||||||
errno
|
errno
|
||||||
@@ -1243,9 +1244,9 @@ void exit(int status) {
|
|||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ends the whole process from any thread; waitpid sees WTERMSIG == SIGABRT. */
|
||||||
void abort(void) {
|
void abort(void) {
|
||||||
_mtk_syscall1(SYS_PRINT, (long)"abort() called\n");
|
_mtk_syscall0(SYS_ABORT);
|
||||||
_mtk_syscall1(SYS_EXIT, 1);
|
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4055,8 +4056,8 @@ pid_t waitpid(pid_t pid, int *status, int options) {
|
|||||||
}
|
}
|
||||||
long code = _mtk_syscall1(SYS_WAITPID, (long)pid);
|
long code = _mtk_syscall1(SYS_WAITPID, (long)pid);
|
||||||
if (status != NULL) {
|
if (status != NULL) {
|
||||||
if (code >= 256) {
|
if (code >= TerminationBase) {
|
||||||
*status = (int)((code - 256) & 0x7F); /* signaled */
|
*status = (int)((code - TerminationBase) & 0x7F); /* signaled */
|
||||||
} else {
|
} else {
|
||||||
*status = (int)((code & 0xFF) << 8); /* exited */
|
*status = (int)((code & 0xFF) << 8); /* exited */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,12 @@ pub fn id() -> u32 {
|
|||||||
unsafe { sys::syscall0(sys::SYS_GETPID) as 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() -> ! {
|
pub fn abort() -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
if crate::thread::current_id() == id() {
|
sys::syscall0(sys::SYS_ABORT);
|
||||||
sys::syscall1(sys::SYS_EXIT, 134);
|
// Not reached. No panic here: the panic handler calls abort().
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
loop {
|
loop {
|
||||||
sys::syscall0(sys::SYS_YIELD);
|
sys::syscall0(sys::SYS_YIELD);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ static void print_exit_code(const int exitCode) {
|
|||||||
if (code == 0) return;
|
if (code == 0) return;
|
||||||
char buf[16];
|
char buf[16];
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (code >= 256) {
|
if (code >= montauk::TerminationBase) {
|
||||||
montauk::print("[terminated by signal ");
|
montauk::print("[terminated by signal ");
|
||||||
code -= 256;
|
code -= montauk::TerminationBase;
|
||||||
} else {
|
} else {
|
||||||
montauk::print("[exit code ");
|
montauk::print("[exit code ");
|
||||||
}
|
}
|
||||||
@@ -39,13 +39,16 @@ static void print_exit_code(const int exitCode) {
|
|||||||
montauk::print(c);
|
montauk::print(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitCode > 0 && exitCode <= montauk::MaxStatusCode) {
|
const char* statusText = nullptr;
|
||||||
const char* statusText = montauk::GetStatusText((montauk::StatusCode)exitCode);
|
if (exitCode >= montauk::TerminationBase) {
|
||||||
|
statusText = montauk::GetTerminationText(exitCode);
|
||||||
if (statusText != nullptr) {
|
} else if (exitCode > 0 && exitCode <= montauk::MaxStatusCode) {
|
||||||
montauk::print(" - ");
|
statusText = montauk::GetStatusText((montauk::StatusCode)exitCode);
|
||||||
montauk::print(statusText);
|
}
|
||||||
}
|
|
||||||
|
if (statusText != nullptr) {
|
||||||
|
montauk::print(" - ");
|
||||||
|
montauk::print(statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
montauk::print("]\n");
|
montauk::print("]\n");
|
||||||
|
|||||||
Reference in new issue
Block a user