Spoof PID across execve() on Windows

It's now possible with cosmo and redbean, to deliver a signal to a child
process after it has called execve(). However the executed program needs
to be compiled using cosmocc. The cosmo runtime WinMain() implementation
now intercepts a _COSMO_PID environment variable that's set by execve().
It ensures the child process will use the same C:\ProgramData\cosmo\sigs
file, which is where kill() will place the delivered signal. We are able
to do this on Windows even better than NetBSD, which has a bug with this

Fixes #1334
This commit is contained in:
Justine Tunney 2024-12-14 12:23:02 -08:00
parent 9cc1bd04b2
commit 26c051c297
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
8 changed files with 187 additions and 21 deletions

View file

@ -17,13 +17,14 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/kprintf.h"
#include "libc/mem/mem.h"
#include "libc/nt/enum/processaccess.h"
#include "libc/nt/enum/startf.h"
@ -33,8 +34,10 @@
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/proc/describefds.internal.h"
#include "libc/proc/ntspawn.h"
#include "libc/runtime/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
@ -43,23 +46,37 @@
#include "libc/thread/thread.h"
#ifdef __x86_64__
__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess;
extern pthread_mutex_t __sig_worker_lock;
static void sys_execve_nt_abort(sigset_t sigmask) {
_pthread_unlock();
pthread_mutex_unlock(&__sig_worker_lock);
__sig_unblock(sigmask);
}
textwindows int sys_execve_nt(const char *program, char *const argv[],
char *const envp[]) {
// execve() needs to be @asyncsignalsafe
sigset_t sigmask = __sig_block();
_pthread_lock();
pthread_mutex_lock(&__sig_worker_lock); // order matters
_pthread_lock(); // order matters
// new process should be a child of our parent
int64_t hParentProcess;
int ppid = sys_getppid_nt();
if (!(hParentProcess = OpenProcess(
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
_pthread_unlock();
__sig_unblock(sigmask);
sys_execve_nt_abort(sigmask);
return -1;
}
// inherit pid
char pidvar[11 + 21];
FormatUint64(stpcpy(pidvar, "_COSMO_PID="), __pid);
// inherit signal mask
char maskvar[6 + 21];
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
@ -84,22 +101,26 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
if (!(fdspec = __describe_fds(g_fds.p, g_fds.n, &si, hParentProcess,
&lpExplicitHandles, &dwExplicitHandleCount))) {
CloseHandle(hParentProcess);
_pthread_unlock();
__sig_unblock(sigmask);
sys_execve_nt_abort(sigmask);
return -1;
}
// inherit pending signals
atomic_fetch_or_explicit(
__sig.process,
atomic_load_explicit(&__get_tls()->tib_sigpending, memory_order_acquire),
memory_order_release);
// launch the process
struct NtProcessInformation pi;
int rc = ntspawn(&(struct NtSpawnArgs){
AT_FDCWD, program, argv, envp, (char *[]){fdspec, maskvar, 0}, 0, 0,
hParentProcess, lpExplicitHandles, dwExplicitHandleCount, &si, &pi});
AT_FDCWD, program, argv, envp, (char *[]){fdspec, maskvar, pidvar, 0}, 0,
0, hParentProcess, lpExplicitHandles, dwExplicitHandleCount, &si, &pi});
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
if (rc == -1) {
free(fdspec);
CloseHandle(hParentProcess);
_pthread_unlock();
__sig_unblock(sigmask);
sys_execve_nt_abort(sigmask);
if (GetLastError() == kNtErrorSharingViolation) {
return etxtbsy();
} else {
@ -112,12 +133,13 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
if (DuplicateHandle(GetCurrentProcess(), pi.hProcess, hParentProcess, &handle,
0, false, kNtDuplicateSameAccess)) {
unassert(!(handle & 0xFFFFFFFFFF000000));
TerminateThisProcess(0x23000000u | handle);
__imp_TerminateProcess(-1, 0x23000000u | handle);
} else {
// TODO(jart): Why does `make loc` print this?
// kprintf("DuplicateHandle failed w/ %d\n", GetLastError());
TerminateThisProcess(ECHILD);
__imp_TerminateProcess(-1, ECHILD);
}
__builtin_unreachable();
}
#endif /* __x86_64__ */