cosmopolitan/libc/calls/execve-nt.greg.c

278 lines
11 KiB
C
Raw Normal View History

2020-06-15 14:18:57 +00:00
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
2020-12-28 01:18:44 +00:00
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
2020-06-15 14:18:57 +00:00
2020-12-28 01:18:44 +00:00
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
2020-06-15 14:18:57 +00:00
*/
2021-08-26 04:35:58 +00:00
#include "libc/calls/calls.h"
2020-06-15 14:18:57 +00:00
#include "libc/calls/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/calls/struct/sigaction.internal.h"
2022-05-23 22:06:11 +00:00
#include "libc/calls/syscall-nt.internal.h"
2023-09-10 15:12:43 +00:00
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
2023-09-10 15:12:43 +00:00
#include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
2023-09-10 15:12:43 +00:00
#include "libc/limits.h"
2023-08-20 09:13:02 +00:00
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
2020-12-01 11:43:40 +00:00
#include "libc/nt/accounting.h"
2022-05-16 20:20:08 +00:00
#include "libc/nt/console.h"
2020-06-15 14:18:57 +00:00
#include "libc/nt/enum/startf.h"
2020-12-01 11:43:40 +00:00
#include "libc/nt/enum/status.h"
2023-09-10 15:12:43 +00:00
#include "libc/nt/enum/threadaccess.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h"
2022-05-16 20:20:08 +00:00
#include "libc/nt/memory.h"
2023-09-10 15:12:43 +00:00
#include "libc/nt/process.h"
2020-06-15 14:18:57 +00:00
#include "libc/nt/runtime.h"
2020-12-01 11:43:40 +00:00
#include "libc/nt/struct/processinformation.h"
2020-06-15 14:18:57 +00:00
#include "libc/nt/struct/startupinfo.h"
2020-12-01 11:43:40 +00:00
#include "libc/nt/synchronization.h"
2023-08-20 09:13:02 +00:00
#include "libc/nt/thread.h"
2022-05-16 20:20:08 +00:00
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
2022-05-16 20:20:08 +00:00
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
2023-08-20 09:13:02 +00:00
#include "libc/runtime/stack.h"
#include "libc/sock/sock.h"
2020-06-15 14:18:57 +00:00
#include "libc/str/str.h"
2022-05-16 20:20:08 +00:00
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/map.h"
2020-06-15 14:18:57 +00:00
#include "libc/sysv/consts/o.h"
2022-05-16 20:20:08 +00:00
#include "libc/sysv/consts/ok.h"
2023-08-20 09:13:02 +00:00
#include "libc/sysv/consts/sig.h"
2022-05-16 20:20:08 +00:00
#include "libc/sysv/errfuns.h"
2023-09-10 15:12:43 +00:00
#include "libc/thread/posixthread.internal.h"
2023-08-20 09:13:02 +00:00
#include "libc/thread/thread.h"
#define keywords textwindows dontasan dontubsan dontinstrument
2022-05-16 20:20:08 +00:00
2023-09-10 15:12:43 +00:00
// clang-format off
2022-05-16 20:20:08 +00:00
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
2023-09-10 15:12:43 +00:00
__msabi extern typeof(ExitProcess) *const __imp_ExitProcess;
__msabi extern typeof(GenerateConsoleCtrlEvent) *const __imp_GenerateConsoleCtrlEvent;
__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
2022-05-16 20:20:08 +00:00
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
2023-09-10 15:12:43 +00:00
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
__msabi extern typeof(SetConsoleCtrlHandler) *const __imp_SetConsoleCtrlHandler;
2023-08-20 09:13:02 +00:00
__msabi extern typeof(TerminateThread) *const __imp_TerminateThread;
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
2023-09-10 15:12:43 +00:00
// clang-format on
2023-08-20 09:13:02 +00:00
2023-09-10 15:12:43 +00:00
extern long __klog_handle;
static void sys_execve_nt_relay(intptr_t, long, long, long);
2023-08-20 09:13:02 +00:00
wontreturn void __switch_stacks(intptr_t, long, long, long,
void (*)(intptr_t, intptr_t, long, long),
intptr_t);
static keywords void PurgeHandle(intptr_t h) {
if (!h) return;
if (h == -1) return;
__imp_CloseHandle(h);
2023-08-20 09:13:02 +00:00
}
static keywords void PurgeThread(intptr_t h) {
if (h && h != -1) {
__imp_TerminateThread(h, SIGKILL);
__imp_CloseHandle(h);
}
}
2022-05-16 20:20:08 +00:00
2023-09-10 15:12:43 +00:00
static keywords void sys_execve_killer(void) {
struct Dll *e;
pthread_spin_lock(&_pthread_lock);
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
enum PosixThreadStatus status;
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
if (tid <= 0 || tid == __imp_GetCurrentThreadId()) continue;
status = atomic_load_explicit(&pt->status, memory_order_acquire);
if (status >= kPosixThreadTerminated) continue;
int64_t hand;
if ((hand = __imp_OpenThread(kNtThreadTerminate, false, tid))) {
__imp_TerminateThread(hand, SIGKILL);
__imp_CloseHandle(hand);
}
2023-08-20 09:13:02 +00:00
}
2023-09-10 15:12:43 +00:00
pthread_spin_unlock(&_pthread_lock);
2022-05-16 20:20:08 +00:00
}
2020-06-15 14:18:57 +00:00
2023-08-20 09:13:02 +00:00
keywords int sys_execve_nt(const char *program, char *const argv[],
char *const envp[]) {
size_t i;
2023-09-10 15:12:43 +00:00
// validate api usage
if (strlen(program) + 4 < PATH_MAX) {
char progbuf[PATH_MAX];
char *end = stpcpy(progbuf, program);
char suffixes[][5] = {"", ".com", ".exe"};
for (i = 0; i < ARRAYLEN(suffixes); ++i) {
stpcpy(end, suffixes[i]);
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
2023-09-10 15:12:43 +00:00
break;
} else if (__imp_GetLastError() == kNtErrorSharingViolation) {
return etxtbsy(); // TODO(jart): does this work
} else {
return eacces();
}
}
2023-09-10 15:12:43 +00:00
} else {
return enametoolong();
2022-05-16 20:20:08 +00:00
}
2023-09-10 15:12:43 +00:00
//
// POINT OF NO RETURN
//
//
// NO! MNO!
// MNO!! [NBK] MNNOO!
// MMNO! MNNOO!!
// MNOONNOO! MMMMMMMMMMPPPOII! MNNO!!!!
// !O! NNO! MMMMMMMMMMMMMPPPOOOII!! NO!
// ! MMMMMMMMMMMMMPPPPOOOOIII! !
// MMMMMMMMMMMMPPPPPOOOOOOII!!
// MMMMMOOOOOOPPPPPPPPOOOOMII!
// MMMMM.. OPPMMP .,OMI!
// MMMM:: o.,OPMP,.o ::I!!
// NNM:::.,,OOPM!P,.::::!!
// MMNNNNNOOOOPMO!!IIPPO!!O!
// MMMMMNNNNOO:!!:!!IPPPPOO!
// MMMMMNNOOMMNNIIIPPPOO!!
// MMMONNMMNNNIIIOO!
// MN MOMMMNNNIIIIIO! OO
// MNO! IiiiiiiiiiiiI OOOO
// NNN.MNO! O!!!!!!!!!O OONO NO!
// MNNNNNO! OOOOOOOOOOO MMNNON!
// MNNNNO! PPPPPPPPP MMNON!
// OO! ON!
//
//
// kill siblings
sys_execve_killer();
PurgeThread(*_weaken(__sigchld_thread));
PurgeThread(*_weaken(__sigwinch_thread));
2022-05-16 20:20:08 +00:00
2023-09-10 15:12:43 +00:00
// close win32 handles for memory mappings
// unlike fork calling execve destroys all memory
// closing a map handle won't impact the mapping itself
for (i = 0; i < _mmi.i; ++i) {
PurgeHandle(_mmi.p[i].h);
2023-08-20 09:13:02 +00:00
}
2023-09-10 15:12:43 +00:00
// close o_cloexec fds and anything that isn't stdio
for (i = 0; i < g_fds.n; ++i) {
if (g_fds.p[i].kind == kFdEmpty) {
g_fds.p[i].handle = -1;
} else if (i > 2 || (g_fds.p[i].flags & O_CLOEXEC)) {
2023-08-20 09:13:02 +00:00
PurgeHandle(g_fds.p[i].handle);
g_fds.p[i].handle = -1;
2022-05-16 20:20:08 +00:00
}
}
2023-09-10 15:12:43 +00:00
// pass bitmask telling child which fds are sockets
int bits;
char buf[32], *v = 0;
if (_weaken(socket)) {
for (bits = i = 0; i < 3; ++i) {
if (g_fds.p[i].kind == kFdSocket) {
bits |= 1 << i;
}
}
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
v = buf;
}
2023-09-10 15:12:43 +00:00
// define stdio handles for the spawned subprocess
struct NtStartupInfo si = {
.cb = sizeof(struct NtStartupInfo),
.dwFlags = kNtStartfUsestdhandles,
.hStdInput = g_fds.p[0].handle,
.hStdOutput = g_fds.p[1].handle,
.hStdError = g_fds.p[2].handle,
};
// launch the process
struct NtProcessInformation pi;
int rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &si, &pi);
2022-05-16 20:20:08 +00:00
if (rc == -1) {
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
2023-09-10 15:12:43 +00:00
if (__imp_GetLastError() == kNtErrorSharingViolation) {
__imp_ExitProcess(SIGVTALRM); // is ETXTBSY
} else {
__imp_ExitProcess(127 << 8);
}
}
PurgeHandle(pi.hThread);
// retreat to original win32-provided stack memory
__switch_stacks(pi.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack);
}
// child is in same process group so wait for it to get killed by this
__msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
return true; // tell win32 we handled signal
}
// this function runs on the original tiny stack that windows gave us
// we need to keep the original process alive simply to pass an int32
// so we unmap all memory to avoid getting a double whammy after fork
static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
uint32_t i, dwExitCode;
// close more handles
__imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1);
PurgeThread(g_fds.stdin.thread); // wasn't inherited by ntspawn
PurgeHandle(g_fds.stdin.reader); // wasn't inherited by ntspawn
PurgeHandle(g_fds.stdin.writer); // wasn't inherited by ntspawn
PurgeHandle(g_fds.p[0].handle); // was inherited via startinfo
PurgeHandle(g_fds.p[1].handle); // was inherited via startinfo
PurgeHandle(g_fds.p[2].handle); // was inherited via startinfo
if (_weaken(__klog_handle)) {
PurgeHandle(*_weaken(__klog_handle)); // wasn't inherited by ntspawn
2022-05-16 20:20:08 +00:00
}
2023-09-10 15:12:43 +00:00
// free all the memory mmap created
for (i = 0; i < _mmi.i; ++i) {
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
}
2022-05-16 20:20:08 +00:00
2023-09-10 15:12:43 +00:00
// wait for process to terminate
//
// WaitForSingleObject can return kNtWaitAbandoned which MSDN
// describes as a "sort of" successful status which indicates
// someone else didn't free a mutex and you should check that
// persistent resources haven't been left corrupted. not sure
// what those resources would be for process objects, however
// this status has actually been observed when waiting on 'em
do {
if (__imp_WaitForSingleObject(h, -1) == kNtWaitFailed) {
notpossible;
}
if (!__imp_GetExitCodeProcess(h, &dwExitCode)) {
notpossible;
}
} while (dwExitCode == kNtStillActive);
// propagate child exit status to parent
__imp_ExitProcess(dwExitCode);
__builtin_unreachable();
2020-06-15 14:18:57 +00:00
}