feat: add Rust userspace support; fix TLS layout for p_align < 16; kill whole process on sibling-thread faults

This commit is contained in:
daniel committed 2026-10-14 07:16:42 +02:00
1 parent a81298fb24
commit f8b8f9610f
31 files changed
+1076 -38

No files matched your search

+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 302
#define MONTAUK_BUILD_NUMBER 306
+4
View File
@@ -221,6 +221,10 @@ namespace montauk::abi {
}
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.
if (pid == Sched::GetCurrentPid()) return Sched::KillProcess(pid);
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) {
int ancestor = pid;
bool descendant = false;
+11 -1
View File
@@ -177,7 +177,17 @@ namespace Hal {
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 */
Sched::SetProcessExitCode(proc->pid, 256 + sig);
// 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.
if (Sched::GetCurrentThreadPtr() != proc) {
Sched::KillProcess(proc->pid, 256 + sig);
} else {
Sched::SetProcessExitCode(proc->pid, 256 + sig);
}
}
Sched::ExitProcess();
+7 -10
View File
@@ -536,16 +536,13 @@ namespace Sched {
}
// Build the main thread's TLS block (x86-64 variant II): the block
// lives below the thread pointer, TP = base + S where
// S = align_up(memsz, align), template data at [base, base+filesz),
// .tbss zeroed, and [TP] holds the ABI-required self-pointer.
// lives below the thread pointer (see ComputeTlsLayout), template
// data first, .tbss zeroed, and [TP] holds the ABI self-pointer.
if (tlsPhdr != nullptr && outTls != nullptr) {
uint64_t align = tlsPhdr->p_align < 16 ? 16 : tlsPhdr->p_align;
uint64_t S = (tlsPhdr->p_memsz + align - 1) & ~(align - 1);
TlsLayout layout = ComputeTlsLayout(tlsPhdr->p_memsz, tlsPhdr->p_align);
uint64_t base = (maxVaddrEnd + 0x1FFFULL) & ~0xFFFULL; // page-aligned, 1 guard page gap
uint64_t total = S + 16; // + minimal TCB
uint64_t numPages = (total + 0xFFF) / 0x1000;
uint64_t tp = base + S;
uint64_t numPages = (layout.totalSize + 0xFFF) / 0x1000;
uint64_t tp = base + layout.tpOffset;
bool ok = true;
for (uint64_t p = 0; p < numPages && ok; p++) {
@@ -562,7 +559,7 @@ namespace Sched {
}
// Copy the .tdata template bytes that land in this page.
if (!copyPageFromFile(physAddr, virtAddr, base,
if (!copyPageFromFile(physAddr, virtAddr, base + layout.templateOffset,
tlsPhdr->p_offset, tlsPhdr->p_filesz)) {
ok = false;
break;
@@ -579,7 +576,7 @@ namespace Sched {
outTls->templateVaddr = tlsPhdr->p_vaddr;
outTls->fileSize = tlsPhdr->p_filesz;
outTls->memSize = tlsPhdr->p_memsz;
outTls->align = align;
outTls->align = tlsPhdr->p_align;
} else {
Kt::KernelLogStream(Kt::ERROR, "ELF")
<< "PT_TLS setup failed (out of pages?), "
+22 -1
View File
@@ -60,9 +60,30 @@ namespace Sched {
uint64_t templateVaddr; // source of .tdata in the loaded image
uint64_t fileSize; // initialized bytes
uint64_t memSize; // total bytes (incl. .tbss)
uint64_t align;
uint64_t align; // PT_TLS p_align, NOT rounded up
};
// Geometry of one variant II TLS block, as offsets from its
// page-aligned base. The linker addresses TLS variables at
// TP - align_up(memSize, p_align) + offset, so the template must start
// exactly that far below TP; rounding p_align up first (as an older
// version did) shifts every variable away from its initial value.
// TP itself is kept at least 16-byte aligned, which the gap between
// base and the template absorbs.
struct TlsLayout {
uint64_t templateOffset; // where .tdata/.tbss start
uint64_t tpOffset; // thread pointer; [TP] holds the self-pointer
uint64_t totalSize; // bytes to map, incl. the 16-byte TCB
};
inline TlsLayout ComputeTlsLayout(uint64_t memSize, uint64_t pAlign) {
uint64_t align = pAlign ? pAlign : 1;
uint64_t block = (memSize + align - 1) & ~(align - 1);
uint64_t tpAlign = align < 16 ? 16 : align;
uint64_t tp = (block + tpAlign - 1) & ~(tpAlign - 1);
return {tp - block, tp, tp + 16};
}
uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys, TlsInfo* outTls);
// Load a shared library into a fixed address range.
+16 -16
View File
@@ -720,10 +720,8 @@ namespace Sched {
uint64_t threadTlsPages = 0;
Process& primary = processTable[primarySlot_];
if (primary.tlsMemSize > 0) {
uint64_t align = primary.tlsAlign < 16 ? 16 : primary.tlsAlign;
uint64_t S = (primary.tlsMemSize + align - 1) & ~(align - 1);
uint64_t total = S + 16;
uint64_t numPages = (total + 0xFFF) / 0x1000;
TlsLayout layout = ComputeTlsLayout(primary.tlsMemSize, primary.tlsAlign);
uint64_t numPages = (layout.totalSize + 0xFFF) / 0x1000;
uint64_t base = 0;
if (!ReserveUserHeapRange(primarySlot_, numPages * 0x1000, base)) {
@@ -757,9 +755,9 @@ namespace Sched {
return -1;
}
uint64_t tp = base + S;
memcpy((void*)base, (const void*)primary.tlsTemplateVaddr,
primary.tlsFileSize);
uint64_t tp = base + layout.tpOffset;
memcpy((void*)(base + layout.templateOffset),
(const void*)primary.tlsTemplateVaddr, primary.tlsFileSize);
*(uint64_t*)tp = tp; /* ABI self-pointer */
threadFsBase = tp;
threadTlsBase = base;
@@ -945,14 +943,13 @@ namespace Sched {
return;
}
uint64_t align = primary.tlsAlign < 16 ? 16 : primary.tlsAlign;
uint64_t blockSize = (primary.tlsMemSize + align - 1) & ~(align - 1);
if (blockSize > thr.fsBase || blockSize > UINT64_MAX - 16) {
TlsLayout layout = ComputeTlsLayout(primary.tlsMemSize, primary.tlsAlign);
if (layout.tpOffset > thr.fsBase || layout.totalSize < layout.tpOffset) {
Panic("Invalid sibling TLS metadata during thread teardown", nullptr);
}
uint64_t base = thr.fsBase - blockSize;
uint64_t pages = (blockSize + 16 + 0xFFF) / 0x1000;
uint64_t base = thr.fsBase - layout.tpOffset;
uint64_t pages = (layout.totalSize + 0xFFF) / 0x1000;
thr.fsBase = 0;
Memory::UnmapAndFreeUserRange(primary.pml4Phys, base, pages);
ReleaseUserHeapRange(primarySlot_, base, pages * 0x1000ULL);
@@ -1791,10 +1788,13 @@ namespace Sched {
}
}
int KillProcess(int pid) {
// Refuse to kill PID 0 (init) or caller's own process
int KillProcess(int pid, int exitCode) {
// Refuse to kill PID 0 (init). Killing the caller's own process is
// allowed: a faulting/aborting sibling thread shares the pid and has
// no other way to take the process down. The caller's own slot gets
// killPending below and exits at syscall return (ExitIfKilled) or
// via the ExitProcess the fault handler calls next.
if (pid == 0) return -1;
if (pid == GetCurrentPid()) return -1;
schedLock.Acquire();
@@ -1817,7 +1817,7 @@ namespace Sched {
return -1;
}
processTable[primarySlot_].exitCode = 256 + 9; /* killed (SIGKILL) */
processTable[primarySlot_].exitCode = exitCode;
// Flag the main thread so its next tick (or scheduler dispatch)
// routes through ExitProcess, which sweeps every sibling thread
+3 -2
View File
@@ -302,9 +302,10 @@ namespace Sched {
int FutexWake(uint64_t uaddr, uint32_t count);
// Kill a process by PID. If the process is running on another CPU,
// sets a kill-pending flag checked on the next timer tick.
// sets a kill-pending flag checked on the next timer tick. exitCode is
// what a waiter sees (256+sig; SIGKILL by default).
// Returns 0 on success, -1 on failure.
int KillProcess(int pid);
int KillProcess(int pid, int exitCode = 256 + 9);
// Start a new process session for the caller, or terminate every process
// belonging to a session. KillSession returns the number of live members