Overhaul process spawning

This commit is contained in:
Justine Tunney 2023-09-10 08:12:43 -07:00
parent 99dc1281f5
commit 26e254fb4d
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
96 changed files with 1848 additions and 1541 deletions

View file

@ -27,10 +27,12 @@
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/context.h"
#include "libc/nt/runtime.h"
@ -225,13 +227,20 @@ textwindows bool __sig_handle(int sigops, int sig, int sic, ucontext_t *ctx) {
uint32_t cmode;
intptr_t hStderr;
const char *signame;
char *end, sigbuf[21], output[22];
char *end, sigbuf[21], output[123];
signame = strsignal_r(sig, sigbuf);
STRACE("terminating due to uncaught %s", signame);
if (__sig_is_core(sig)) {
hStderr = GetStdHandle(kNtStdErrorHandle);
if (GetConsoleMode(hStderr, &cmode)) {
end = stpcpy(stpcpy(output, signame), "\n");
end = stpcpy(output, signame);
end = stpcpy(end, " ");
end = stpcpy(
end,
DescribeBacktrace(
ctx ? (struct StackFrame *)ctx->uc_mcontext.BP
: (struct StackFrame *)__builtin_frame_address(0)));
end = stpcpy(end, "\n");
WriteFile(hStderr, output, end - output, 0, 0);
}
}

View file

@ -18,7 +18,9 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/runtime/runtime.h"
/**
@ -27,6 +29,8 @@
void __assert_fail(const char *expr, const char *file, int line) {
char ibuf[12];
FormatInt32(ibuf, line);
tinyprint(2, "\n", file, ":", ibuf, ": assert(", expr, ") failed\n", NULL);
tinyprint(2, "\n", file, ":", ibuf, ": assert(", expr, ") failed (",
program_invocation_short_name, " ",
DescribeBacktrace(__builtin_frame_address(0)), ")\n", NULL);
abort();
}

View file

@ -19,6 +19,7 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"

View file

@ -16,22 +16,28 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
#include "libc/nt/accounting.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/enum/threadaccess.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
@ -50,26 +56,31 @@
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#define keywords textwindows dontasan dontubsan dontinstrument
extern long __klog_handle;
// clang-format off
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(ExitProcess) *const __imp_ExitProcess;
__msabi extern typeof(GenerateConsoleCtrlEvent) *const __imp_GenerateConsoleCtrlEvent;
__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
__msabi extern typeof(SetConsoleCtrlHandler) *const __imp_SetConsoleCtrlHandler;
__msabi extern typeof(TerminateThread) *const __imp_TerminateThread;
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
// clang-format on
extern long __klog_handle;
static void sys_execve_nt_relay(intptr_t, long, long, long);
wontreturn void __switch_stacks(intptr_t, long, long, long,
void (*)(intptr_t, intptr_t, long, long),
intptr_t);
__msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
return true; // block sigint and sigquit in execve() parent process
}
static keywords void PurgeHandle(intptr_t h) {
if (!h) return;
if (h == -1) return;
@ -83,66 +94,87 @@ static keywords void PurgeThread(intptr_t h) {
}
}
// 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;
__imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1);
PurgeThread(g_fds.stdin.thread);
PurgeHandle(g_fds.stdin.reader);
PurgeHandle(g_fds.stdin.writer);
PurgeHandle(g_fds.p[0].handle);
PurgeHandle(g_fds.p[1].handle);
PurgeHandle(g_fds.p[2].handle);
for (i = 0; i < _mmi.i; ++i) {
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
PurgeHandle(_mmi.p[i].h);
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);
}
}
do {
__imp_WaitForSingleObject(h, -1);
dwExitCode = kNtStillActive;
__imp_GetExitCodeProcess(h, &dwExitCode);
} while (dwExitCode == kNtStillActive);
__imp_ExitProcess(dwExitCode);
__builtin_unreachable();
pthread_spin_unlock(&_pthread_lock);
}
keywords int sys_execve_nt(const char *program, char *const argv[],
char *const envp[]) {
int rc;
size_t i;
char progbuf[PATH_MAX];
struct NtStartupInfo startinfo;
struct NtProcessInformation procinfo;
if (strlen(program) + 4 + 1 > PATH_MAX) {
return enametoolong();
}
// this is a non-recoverable operation, so do some manual validation
if (sys_faccessat_nt(AT_FDCWD, program, X_OK, 0) == -1) {
stpcpy(stpcpy(progbuf, program), ".com");
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
program = progbuf;
} else {
stpcpy(stpcpy(progbuf, program), ".exe");
// 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) {
program = progbuf;
break;
} else if (__imp_GetLastError() == kNtErrorSharingViolation) {
return etxtbsy(); // TODO(jart): does this work
} else {
return eacces();
}
}
} else {
return enametoolong();
}
//////////////////////////////////////////////////////////////////////////////
// execve operation is unrecoverable from this point
//
// 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!
//
//
if (_weaken(pthread_kill_siblings_np)) {
_weaken(pthread_kill_siblings_np)();
// kill siblings
sys_execve_killer();
// 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);
}
// close non-stdio and cloexec handles
// 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;
@ -152,12 +184,7 @@ keywords int sys_execve_nt(const char *program, char *const argv[],
}
}
if (_weaken(__klog_handle) && //
*_weaken(__klog_handle) != 0 && //
*_weaken(__klog_handle) != -1) {
PurgeHandle(*_weaken(__klog_handle));
}
// pass bitmask telling child which fds are sockets
int bits;
char buf[32], *v = 0;
if (_weaken(socket)) {
@ -169,23 +196,79 @@ keywords int sys_execve_nt(const char *program, char *const argv[],
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
v = buf;
}
bzero(&startinfo, sizeof(startinfo));
startinfo.cb = sizeof(struct NtStartupInfo);
startinfo.dwFlags = kNtStartfUsestdhandles;
startinfo.hStdInput = g_fds.p[0].handle;
startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle;
// spawn the process
rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &startinfo, &procinfo);
// 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);
if (rc == -1) {
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
__imp_ExitProcess(11);
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
}
//////////////////////////////////////////////////////////////////////////////
// zombify this process which lingers on to relay the status code
// free all the memory mmap created
for (i = 0; i < _mmi.i; ++i) {
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
}
PurgeHandle(procinfo.hThread);
__switch_stacks(procinfo.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack);
// 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();
}

View file

@ -19,5 +19,5 @@
#include "libc/calls/state.internal.h"
#include "libc/thread/thread.h"
unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG];
unsigned __sighandrvas[NSIG + 1];
unsigned __sighandflags[NSIG + 1];

View file

@ -641,6 +641,7 @@ static int ioctl_siocgifflags(int fd, void *arg) {
* - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)`
* - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)`
* - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)`
* - `TIOCSCTTY` isn't polyfilled; use `login_tty()`
* - `TCGETS` isn't polyfilled; use tcgetattr()
* - `TCSETS` isn't polyfilled; use tcsetattr()
* - `TCSETSW` isn't polyfilled; use tcsetattr()

View file

@ -29,12 +29,12 @@
*
* The order of precedence is:
*
* - $TMPDIR/
* - GetTempPath()
* - /tmp/
* - $TMPDIR/ is always favored if defined
* - GetTempPath(), for the New Technology
* - /tmp/ to make security scene go crazy
*
* This guarantees trailing slash.
* We also guarantee `kTmpPath` won't be longer than `PATH_MAX / 2`.
* This guarantees an absolute path with a trailing slash. We also
* ensure `kTmpPath` isn't longer than `PATH_MAX-NAME_MAX`.
*/
char kTmpPath[PATH_MAX];
@ -48,15 +48,20 @@ __attribute__((__constructor__)) static void kTmpPathInit(void) {
uint32_t n;
char16_t path16[PATH_MAX];
if ((s = getenv("TMPDIR")) && (n = strlen(s)) < PATH_MAX / 2) {
if (n) memcpy(kTmpPath, s, n);
if (n && kTmpPath[n - 1] != '/') {
kTmpPath[n + 0] = '/';
kTmpPath[n + 1] = 0;
if ((s = getenv("TMPDIR"))) {
if (*s != '/') {
if (!getcwd(kTmpPath, PATH_MAX)) {
goto GiveUp;
}
strlcat(kTmpPath, "/", sizeof(kTmpPath));
}
strlcat(kTmpPath, s, sizeof(kTmpPath));
if (strlcat(kTmpPath, "/", sizeof(kTmpPath)) < PATH_MAX - NAME_MAX) {
return;
}
return;
}
GiveUp:
if (IsWindows() &&
((n = GetTempPath(ARRAYLEN(path16), path16)) && n < ARRAYLEN(path16))) {
// turn c:\foo\bar\ into c:/foo/bar/

View file

@ -17,18 +17,22 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/ntspawn.h"
#include "libc/assert.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/pushpop.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/sysv/errfuns.h"
struct SpawnBlock {
union {
@ -95,6 +99,8 @@ textwindows int ntspawn(
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
} else if (GetLastError() == kNtErrorSharingViolation) {
etxtbsy();
}
if (block) UnmapViewOfFile(block);
if (handle) CloseHandle(handle);

View file

@ -26,6 +26,10 @@
/**
* Changes process group for process.
*
* @param pid is process id (may be zero for current process)
* @param pgid is process group id (may be zero for current process)
* @return 0 on success, or -1 w/ errno
* @vforksafe
*/
int setpgid(int pid, int pgid) {
@ -35,16 +39,14 @@ int setpgid(int pid, int pgid) {
} else {
me = getpid();
if ((!pid || pid == me) && (!pgid || pgid == me)) {
/*
* "When a process is created with CREATE_NEW_PROCESS_GROUP
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
* is made on behalf of the new process; this means that the new
* process has CTRL+C disabled. This lets shells handle CTRL+C
* themselves, and selectively pass that signal on to
* sub-processes. CTRL+BREAK is not disabled, and may be used to
* interrupt the process/process group."
* Quoth MSDN § CreateProcessW()
*/
// "When a process is created with kNtCreateNewProcessGroup
// specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
// is made on behalf of the new process; this means that the new
// process has CTRL+C disabled. This lets shells handle CTRL+C
// themselves, and selectively pass that signal on to
// sub-processes. CTRL+BREAK is not disabled, and may be used to
// interrupt the process/process group."
// ──Quoth MSDN § CreateProcessW()
if (SetConsoleCtrlHandler(0, 1)) {
rc = 0;
} else {

View file

@ -23,14 +23,16 @@
/**
* Creates session and sets the process group id.
*
* @return new session id, or -1 w/ errno
* @raise EPERM if already the leader
*/
int setsid(void) {
int rc;
if (!IsWindows() && !IsMetal()) {
rc = sys_setsid();
} else {
rc = 0;
rc = getpid();
}
STRACE("setsid() → %d% m", rc);
return rc;

View file

@ -59,21 +59,14 @@ static void sigaltstack2linux(struct sigaltstack *linux,
* struct sigaction sa;
* struct sigaltstack ss;
* ss.ss_flags = 0;
* ss.ss_sp = NewCosmoStack();
* ss.ss_size = GetStackSize();
* ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
* MAP_STACK | MAP_ANONYMOUS, -1, 0);
* sigaltstack(&ss, 0);
* sigemptyset(&sa.ss_mask);
* sa.sa_flags = SA_ONSTACK;
* sa.sa_handler = OnStackOverflow;
* __cxa_atexit(free, ss[0].ss_sp, 0);
* sigemptyset(&sa.ss_mask);
* sigaltstack(&ss, 0);
* sigaction(SIGSEGV, &sa, 0);
*
* It's strongly recommended that you allocate a stack with the same
* size as GetStackSize() and that it have GetStackSize() alignment.
* Otherwise some of your runtime support code (e.g. ftrace stack use
* logging, kprintf() memory safety) won't be able to work as well.
*
* @param neu if non-null will install new signal alt stack
* @param old if non-null will receive current signal alt stack
* @return 0 on success, or -1 w/ errno
@ -85,9 +78,8 @@ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
void *b;
const void *a;
struct sigaltstack_bsd bsd;
if (IsAsan() && ((old && __asan_check(old, sizeof(*old)).kind) ||
(neu && (__asan_check(neu, sizeof(*neu)).kind ||
__asan_check(neu->ss_sp, neu->ss_size).kind)))) {
if (IsAsan() && ((old && !__asan_is_valid(old, sizeof(*old))) ||
(neu && !__asan_is_valid(neu, sizeof(*neu))))) {
rc = efault();
} else if (neu && neu->ss_size < MINSIGSTKSZ) {
rc = enomem();

View file

@ -41,9 +41,9 @@ privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo,
ucontext_t uc;
siginfo_t si;
} g;
rva = __sighandrvas[sig & (NSIG - 1)];
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig & (NSIG - 1)];
flags = __sighandflags[sig];
if (~flags & SA_SIGINFO) {
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
} else {

View file

@ -33,9 +33,9 @@
privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) {
int i, rva, flags;
rva = __sighandrvas[sig & (NSIG - 1)];
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig & (NSIG - 1)];
flags = __sighandflags[sig];
// WSL1 doesn't set the fpregs field.
// https://github.com/microsoft/WSL/issues/2555
if ((flags & SA_SIGINFO) && UNLIKELY(!ctx->uc_mcontext.fpregs)) {

View file

@ -39,9 +39,9 @@ privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si,
int rva, flags;
ucontext_t uc;
struct siginfo si2;
rva = __sighandrvas[sig & (NSIG - 1)];
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig & (NSIG - 1)];
flags = __sighandflags[sig];
if (~flags & SA_SIGINFO) {
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
} else {

View file

@ -41,9 +41,9 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
ucontext_t uc;
struct siginfo si;
} g;
rva = __sighandrvas[sig & (NSIG - 1)];
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig & (NSIG - 1)];
flags = __sighandflags[sig];
if (~flags & SA_SIGINFO) {
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
} else {

View file

@ -493,9 +493,9 @@ privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
ucontext_t uc;
siginfo_t si;
} g;
rva = __sighandrvas[sig & (NSIG - 1)];
rva = __sighandrvas[sig];
if (rva >= kSigactionMinRva) {
flags = __sighandflags[sig & (NSIG - 1)];
flags = __sighandflags[sig];
if (~flags & SA_SIGINFO) {
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
} else {

View file

@ -8,10 +8,10 @@ COSMOPOLITAN_C_START_
extern int __vforked;
extern bool __time_critical;
extern unsigned __sighandrvas[NSIG];
extern unsigned __sighandflags[NSIG];
extern pthread_mutex_t __fds_lock_obj;
extern pthread_mutex_t __sig_lock_obj;
extern unsigned __sighandrvas[NSIG + 1];
extern unsigned __sighandflags[NSIG + 1];
extern const struct NtSecurityAttributes kNtIsInheritable;
void __fds_lock(void);

52
libc/calls/tcsetsid.c Normal file
View file

@ -0,0 +1,52 @@
/*-*- 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 2023 Justine Alexandra Roberts Tunney
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.
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.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
/**
* Associates session with controlling tty.
*
* @return 0 on success, or -1 w/ errno
* @raise EBADF if `fd` isn't an open file descriptor
* @raise ENOTTY if `fd` is isn't controlling teletypewriter
* @raise EINVAL if `pid` isn't session id associated with this process
* @raise EPERM if calling process isn't the session leader
* @raise ENOSYS on Windows and Bare Metal
*/
int tcsetsid(int fd, int pid) {
int rc;
if (fd < 0) {
rc = ebadf();
} else if (IsWindows() || IsMetal()) {
rc = enosys();
} else if (pid != sys_getsid(0)) {
rc = einval();
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else {
rc = sys_ioctl(fd, TIOCSCTTY, 0);
}
STRACE("tcsetsid(%d, %d) → %d% m", fd, pid, rc);
return rc;
}

View file

@ -28,6 +28,7 @@ int tcgetsid(int);
int tcgetpgrp(int);
int tcflow(int, int);
int tcflush(int, int);
int tcsetsid(int, int);
int tcsetpgrp(int, int);
int tcsendbreak(int, int);
void cfmakeraw(struct termios *);

View file

@ -45,7 +45,7 @@ int _mkstemp(char *, int);
* This creates a secure temporary file inside $TMPDIR. If it isn't
* defined, then /tmp is used on UNIX and GetTempPath() is used on the
* New Technology. This resolution of $TMPDIR happens once in a ctor,
* which is copied to the `kTmpDir` global.
* which is copied to the `kTmpPath` global.
*
* Once close() is called, the returned file is guaranteed to be deleted
* automatically. On UNIX the file is unlink()'d before this function

View file

@ -118,6 +118,8 @@ static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
return echild();
}
}
// wait for one of the processes to terminate
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);
@ -132,12 +134,20 @@ static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
}
}
if (i == kNtWaitFailed) {
STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
return __winerr();
notpossible;
}
// WaitForMultipleObjects can say 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
i &= ~kNtWaitAbandoned;
// this is where things get especially hairy. see exit() doc
if (!GetExitCodeProcess(handles[i], &dwExitCode)) {
STRACE("%s failed %u", "GetExitCodeProcess", GetLastError());
return __winerr();
notpossible;
}
if (dwExitCode == kNtStillActive) {
return -2;
@ -145,6 +155,8 @@ static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
if (dwExitCode == 0xc9af3d51u) {
dwExitCode = kNtStillActive;
}
// now pass along the result
if (opt_out_wstatus) {
*opt_out_wstatus = dwExitCode;
}

View file

@ -19,44 +19,22 @@
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/str/str.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#include "libc/thread/tls2.internal.h"
#ifdef __x86_64__
// win32 calls this; we're running inside the thread that crashed
__msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
int sig, code;
struct CosmoTib *tib;
static bool noreentry;
noreentry = true;
if ((tib = __tls_enabled ? __get_tls() : 0)) {
if (~tib->tib_flags & TIB_FLAG_WINCRASHING) {
tib->tib_flags |= TIB_FLAG_WINCRASHING;
} else {
ExitProcess(SIGSEGV);
}
} else {
if (!noreentry) {
noreentry = true;
} else {
ExitProcess(SIGSEGV);
}
}
switch (ep->ExceptionRecord->ExceptionCode) {
case kNtSignalBreakpoint:
@ -133,7 +111,8 @@ __msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
break;
}
STRACE("wincrash %G rip %x bt %s", sig, ep->ContextRecord->Rip,
STRACE("win32 vectored exception raising 0x%08Xu %G rip %x bt %s",
ep->ExceptionRecord->ExceptionCode, sig, ep->ContextRecord->Rip,
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
if (__sighandflags[sig] & SA_SIGINFO) {
@ -143,11 +122,6 @@ __msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
__sig_handle(kSigOpUnmaskable, sig, code, 0);
}
noreentry = false;
if (tib) {
tib->tib_flags &= ~TIB_FLAG_WINCRASHING;
}
return kNtExceptionContinueExecution;
}

View file

@ -128,6 +128,7 @@ textwindows static char16_t *CreateStdinPipeName(char16_t *a, int64_t h) {
// this makes it possible for our read() implementation to periodically
// poll for signals while performing a blocking overlapped io operation
textwindows void WinMainStdin(void) {
uint32_t conmode;
char16_t pipename[64];
int64_t hStdin, hWriter, hReader, hThread, hSemaphore;
if (!SupportsWindows()) return;
@ -137,6 +138,10 @@ textwindows void WinMainStdin(void) {
Log("<stdin> GetStdHandle failed\n");
return;
}
if (!__imp_GetConsoleMode(hStdin, &conmode)) {
Log("<stdin> stdin not a console\n");
return;
}
CreateStdinPipeName(pipename, hStdin);
hReader = __imp_CreateFileW(pipename, kNtGenericRead, 0, 0, kNtOpenExisting,
kNtFileFlagOverlapped, 0);

View file

@ -17,8 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/rlimit.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bits.h"
@ -40,6 +43,7 @@
#include "libc/nt/enum/version.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/stdckdint.h"
#include "libc/str/str.h"
@ -47,6 +51,8 @@
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#include "third_party/dlmalloc/dlmalloc.h"
@ -910,7 +916,7 @@ static __wur __asan_die_f *__asan_report(const void *addr, int size,
*p = 0;
kprintf("%s", buf);
__asan_report_memory_origin(addr, size, kind);
kprintf("\nthe crash was caused by\n");
kprintf("\nthe crash was caused by %s\n", program_invocation_name);
ftrace_enabled(+1);
return __asan_die();
}
@ -1428,32 +1434,6 @@ static size_t __asan_strlen(const char *s) {
return i;
}
static textstartup void __asan_shadow_string(char *s) {
__asan_map_shadow((intptr_t)s, __asan_strlen(s) + 1);
}
static textstartup void __asan_shadow_auxv(intptr_t *auxv) {
size_t i;
for (i = 0; auxv[i]; i += 2) {
if (_weaken(AT_RANDOM) && auxv[i] == *_weaken(AT_RANDOM)) {
__asan_map_shadow(auxv[i + 1], 16);
} else if (_weaken(AT_EXECFN) && auxv[i] == *_weaken(AT_EXECFN)) {
__asan_shadow_string((char *)auxv[i + 1]);
} else if (_weaken(AT_PLATFORM) && auxv[i] == *_weaken(AT_PLATFORM)) {
__asan_shadow_string((char *)auxv[i + 1]);
}
}
__asan_map_shadow((uintptr_t)auxv, (i + 2) * sizeof(intptr_t));
}
static textstartup void __asan_shadow_string_list(char **list) {
size_t i;
for (i = 0; list[i]; ++i) {
__asan_shadow_string(list[i]);
}
__asan_map_shadow((uintptr_t)list, (i + 1) * sizeof(char *));
}
static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m,
size_t i) {
uintptr_t x, y;
@ -1465,18 +1445,94 @@ static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m,
}
}
static textstartup void __asan_shadow_existing_mappings(void) {
static textstartup char *__asan_get_last_string(char **list) {
char *res = 0;
for (int i = 0; list[i]; ++i) {
res = list[i];
}
return res;
}
static textstartup uintptr_t __asan_get_stack_top(int argc, char **argv,
char **envp,
unsigned long *auxv,
long pagesz) {
uintptr_t top;
const char *s;
if ((s = __asan_get_last_string(envp)) ||
(s = __asan_get_last_string(argv))) {
top = (uintptr_t)s + __asan_strlen(s);
} else {
unsigned long *xp = auxv;
while (*xp) xp += 2;
top = (uintptr_t)xp;
}
return (top + (pagesz - 1)) & -pagesz;
}
static textstartup void __asan_shadow_existing_mappings(int argc, char **argv,
char **envp,
unsigned long *auxv) {
__asan_shadow_mapping(&_mmi, 0);
__asan_map_shadow(GetStackAddr(), GetStackSize());
__asan_poison((void *)GetStackAddr(), getauxval(AT_PAGESZ),
kAsanStackOverflow);
// WinMain() maps its own stack and its shadow too
if (IsWindows()) {
return;
}
// get microprocessor page size
long pagesz = 4096;
for (int i = 0; auxv[i]; i += 2) {
if (auxv[i] == AT_PAGESZ) {
pagesz = auxv[i + 1];
}
}
// get configured stack size of main thread
// supported platforms use defaults ranging from 4mb to 512mb
struct rlimit rlim;
uintptr_t stack_size = 4 * 1024 * 1024;
if (!sys_getrlimit(RLIMIT_STACK, &rlim) && //
rlim.rlim_cur > 0 && rlim.rlim_cur < RLIM_INFINITY) {
stack_size = (rlim.rlim_cur + (pagesz - 1)) & -pagesz;
}
// Every UNIX system in our support vector creates arg blocks like:
//
// <HIGHEST-STACK-ADDRESS>
// last environ string
// ...
// first environ string
// ...
// auxiliary value pointers
// environ pointers
// argument pointers
// argument count
// --- %rsp _start()
// ...
// ...
// ... program's stack
// ...
// ...
// <LOWEST-STACK-ADDRESS>
//
// The region of memory between highest and lowest can be computed
// across all supported platforms ±1 page accuracy as the distance
// between the last character of the last environ variable rounded
// up to the microprocessor page size (this computes the top addr)
// and the bottom is computed by subtracting RLIMIT_STACK rlim_cur
// It's simple but gets tricky if we consider environ can be empty
uintptr_t stack_top = __asan_get_stack_top(argc, argv, envp, auxv, pagesz);
uintptr_t stack_bot = stack_top - stack_size;
__asan_map_shadow(stack_bot, stack_top - stack_bot);
__asan_poison((void *)stack_bot, GetGuardSize(), kAsanStackOverflow);
}
forceinline ssize_t __write_str(const char *s) {
return sys_write(2, s, __asan_strlen(s));
}
void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) {
void __asan_init(int argc, char **argv, char **envp, unsigned long *auxv) {
static bool once;
if (!_cmpxchg(&once, false, true)) return;
if (IsWindows() && NtGetVersion() < kNtVersionWindows10) {
@ -1493,16 +1549,13 @@ void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) {
REQUIRE(dlmemalign);
REQUIRE(dlmalloc_usable_size);
}
__asan_shadow_existing_mappings();
__asan_shadow_existing_mappings(argc, argv, envp, auxv);
__asan_map_shadow((uintptr_t)__executable_start, _end - __executable_start);
__asan_map_shadow(0, 4096);
__asan_poison((void *)__veil("r", 0L), getauxval(AT_PAGESZ), kAsanNullPage);
if (!IsWindows()) {
sys_mprotect((void *)0x7fff8000, 0x10000, PROT_READ);
}
__asan_shadow_string_list(argv);
__asan_shadow_string_list(envp);
__asan_shadow_auxv(auxv);
__asan_install_malloc_hooks();
STRACE(" _ ____ _ _ _ ");
STRACE(" / \\ / ___| / \\ | \\ | |");

View file

@ -18,22 +18,30 @@
*/
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/libfatal.internal.h"
#include "libc/nexgen32e/stackframe.h"
#define N 64
#define N 100
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
const char *(DescribeBacktrace)(char buf[N], struct StackFrame *fr) {
int o = 0;
dontinstrument dontasan const char *(DescribeBacktrace)(char buf[N],
struct StackFrame *fr) {
bool gotsome = false;
char *p = buf;
char *pe = p + N;
while (fr) {
if (gotsome) {
append(" ");
if (p + 1 < pe) {
*p++ = ' ';
*p = 0;
}
} else {
gotsome = true;
}
append("%x", fr->addr);
if (p + 17 <= pe) {
p = __hexcpy(p, fr->addr);
}
fr = fr->next;
}
return buf;

View file

@ -5,8 +5,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
const char *DescribeBacktrace(char[64], struct StackFrame *);
#define DescribeBacktrace(x) DescribeBacktrace(alloca(64), x)
const char *DescribeBacktrace(char[100], struct StackFrame *);
#define DescribeBacktrace(x) DescribeBacktrace(alloca(100), x)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -22,7 +22,9 @@
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/sysv/consts/map.h"
@ -30,7 +32,7 @@
#define G FRAMESIZE
static void *_mapframe(void *p, int f) {
static dontasan void *_mapframe(void *p, int f) {
int rc, prot, flags;
struct DirectMap dm;
prot = PROT_READ | PROT_WRITE;

View file

@ -41,7 +41,7 @@
* @threadsafe
* @vforksafe
*/
int getpid(void) {
dontasan int getpid(void) {
int rc;
if (IsMetal()) {
rc = 1;

View file

@ -19,10 +19,13 @@
#include "libc/intrin/kmalloc.h"
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/extend.internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/sysv/consts/map.h"
@ -63,7 +66,7 @@ void __kmalloc_unlock(void) {
* @return zero-initialized memory on success, or null w/ errno
* @raise ENOMEM if we require more vespene gas
*/
void *kmalloc(size_t size) {
dontasan void *kmalloc(size_t size) {
char *p, *e;
size_t i, n, t;
n = ROUNDUP(size + (IsAsan() * 8), KMALLOC_ALIGN);

View file

@ -325,13 +325,13 @@ privileged long kloghandle(void) {
long proc;
proc = __imp_GetCurrentProcess();
hand = __imp_GetStdHandle(kNtStdErrorHandle);
__imp_DuplicateHandle(proc, hand, proc, &hand, 0, true,
__imp_DuplicateHandle(proc, hand, proc, &hand, 0, false,
kNtDuplicateSameAccess);
} else if (n && n < 512) {
hand = __imp_CreateFileW(
path, kNtFileAppendData,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
&kNtIsInheritable, kNtOpenAlways, kNtFileAttributeNormal, 0);
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
kNtOpenAlways, kNtFileAttributeNormal, 0);
} else {
hand = -1; // KPRINTF_LOG was empty string or too long
}

View file

@ -16,20 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/errno.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/log/log.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#include "libc/str/str.h"
#if SupportsMetal()
__static_yoink("_idt");
@ -37,29 +35,18 @@ __static_yoink("_idt");
/**
* Aborts process after printing a backtrace.
*
* If a debugger is present then this will trigger a breakpoint.
*/
relegated wontreturn void __die(void) {
/* asan runtime depends on this function */
int me, owner;
static atomic_int once;
owner = 0;
me = __tls_enabled ? __get_tls()->tib_tid : sys_gettid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
if (__vforked ||
atomic_compare_exchange_strong_explicit(
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
__restore_tty();
if (IsDebuggerPresent(false)) {
DebugBreak();
}
ShowBacktrace(2, __builtin_frame_address(0));
_Exit(77);
} else if (owner == me) {
kprintf("die failed while dying\n");
_Exit(79);
} else {
_Exit1(79);
}
relegated dontasan wontreturn void __die(void) {
// print vital error nubers reliably
// the surface are of code this calls is small and audited
kprintf("\r\n\e[1;31m__die %s pid %d tid %d bt %s\e[0m\n",
program_invocation_short_name, getpid(), sys_gettid(),
DescribeBacktrace(__builtin_frame_address(0)));
// print much friendlier backtrace less reliably
// we're in a broken runtime state and so much can go wrong
__restore_tty();
ShowBacktrace(2, __builtin_frame_address(0));
_Exit(77);
}

View file

@ -9,6 +9,12 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
__funline unsigned long __strlen(const char *s) {
unsigned long n = 0;
while (*s++) ++n;
return n;
}
__funline int __strcmp(const char *l, const char *r) {
size_t i = 0;
while (l[i] == r[i] && r[i]) ++i;
@ -24,6 +30,15 @@ __funline char *__stpcpy(char *d, const char *s) {
}
}
__funline long __write_linux(int fd, const void *p, long n) {
long ax = 1;
asm volatile("syscall"
: "+a"(ax)
: "D"(fd), "S"(p), "d"(n)
: "rcx", "r11", "memory");
return ax;
}
__funline void *__repstosb(void *di, char al, size_t cx) {
#if defined(__x86__) && defined(__GNUC__) && !defined(__STRICT_ANSI__)
asm("rep stosb"

View file

@ -21,6 +21,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/utsname.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/ucontext.h"
@ -29,6 +30,7 @@
#include "libc/fmt/itoa.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kmalloc.h"
#include "libc/intrin/kprintf.h"
@ -259,113 +261,42 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
kprintf("\n");
}
static wontreturn relegated dontinstrument void __minicrash(int sig,
struct siginfo *si,
ucontext_t *ctx,
const char *kind) {
kprintf("\n"
"\n"
"CRASHED %s WITH %G\n"
"%s\n"
"RIP %x\n"
"RSP %x\n"
"RBP %x\n"
"PID %d\n"
"TID %d\n"
"\n",
kind, sig, __argv[0], ctx ? ctx->uc_mcontext.rip : 0,
ctx ? ctx->uc_mcontext.rsp : 0, ctx ? ctx->uc_mcontext.rbp : 0, __pid,
__tls_enabled ? __get_tls()->tib_tid : sys_gettid());
_Exit(119);
static relegated wontreturn void RaiseCrash(int sig) {
sigset_t ss;
sigfillset(&ss);
sigdelset(&ss, sig);
sigprocmask(SIG_SETMASK, &ss, 0);
signal(sig, SIG_DFL);
kill(getpid(), sig);
_Exit(128 + sig);
}
/**
* Crashes in a developer-friendly human-centric way.
*
* We first try to launch GDB if it's an interactive development
* session. Otherwise we show a really good crash report, sort of like
* Python, that includes filenames and line numbers. Many editors, e.g.
* Emacs, will even recognize its syntax for quick hopping to the
* failing line. That's only possible if the the .com.dbg file is in the
* same folder. If the concomitant debug binary can't be found, we
* simply print addresses which may be cross-referenced using objdump.
*
* This function never returns, except for traps w/ human supervision.
*
* @threadsafe
* @vforksafe
*/
relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) {
int bZero;
#ifdef ATTACH_GDB_ON_CRASH
intptr_t rip;
#endif
int me, owner;
int gdbpid, err;
ucontext_t *ctx = arg;
static atomic_int once;
static atomic_int once2;
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
// print vital error nubers reliably
// the surface are of code this calls is small and audited
kprintf(
"\r\n\e[1;31m__oncrash %G %s pid %d tid %d rip %x bt %s\e[0m\n", sig,
program_invocation_short_name, getpid(), sys_gettid(),
ctx ? ctx->uc_mcontext.rip : 0,
DescribeBacktrace(ctx ? (struct StackFrame *)ctx->uc_mcontext.rbp
: (struct StackFrame *)__builtin_frame_address(0)));
// print friendlier detailed crash report less reliably
// we're in a broken runtime state and so much can go wrong
ftrace_enabled(-1);
strace_enabled(-1);
owner = 0;
me = __tls_enabled ? __get_tls()->tib_tid : sys_gettid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
if (atomic_compare_exchange_strong_explicit(
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
if (!__vforked) {
#ifdef ATTACH_GDB_ON_CRASH
rip = ctx ? ctx->uc_mcontext.rip : 0;
#endif
err = errno;
if ((gdbpid = IsDebuggerPresent(true))) {
DebugBreak();
} else if (__nocolor || g_isrunningundermake) {
gdbpid = -1;
#if ATTACH_GDB_ON_CRASH
} else if (!IsTiny() && IsLinux() && FindDebugBinary() && !__isworker) {
// RestoreDefaultCrashSignalHandlers();
gdbpid = AttachDebugger(
((sig == SIGTRAP || sig == SIGQUIT) &&
(rip >= (intptr_t)&__executable_start && rip < (intptr_t)&_etext))
? rip
: 0);
#endif
}
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
__restore_tty();
ShowCrashReport(err, sig, si, ctx);
_Exit(128 + sig);
}
atomic_store_explicit(&once, 0, memory_order_relaxed);
} else {
atomic_store_explicit(&once, 0, memory_order_relaxed);
__minicrash(sig, si, ctx, "WHILE VFORKED");
}
} else if (sig == SIGTRAP) {
// chances are IsDebuggerPresent() confused strace w/ gdb
goto ItsATrap;
} else if (owner == me) {
// we crashed while generating a crash report
bZero = false;
if (atomic_compare_exchange_strong_explicit(
&once2, &bZero, true, memory_order_relaxed, memory_order_relaxed)) {
__minicrash(sig, si, ctx, "WHILE CRASHING");
} else {
// somehow __minicrash() crashed not possible
for (;;) {
abort();
}
}
} else {
// multiple threads have crashed
// kill current thread assuming process dies soon
// TODO(jart): It'd be nice to report on all threads.
_Exit1(8);
err = errno;
if ((gdbpid = IsDebuggerPresent(true))) {
DebugBreak();
}
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
__restore_tty();
ShowCrashReport(err, sig, si, ctx);
RaiseCrash(sig);
}
ItsATrap:
strace_enabled(+1);
ftrace_enabled(+1);
}
#endif /* __x86_64__ */

View file

@ -21,6 +21,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/struct/aarch64.internal.h"
#include "libc/calls/struct/rusage.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
@ -63,18 +64,28 @@ struct Buffer {
int i;
};
static bool IsCode(uintptr_t p) {
static relegated bool IsCode(uintptr_t p) {
return __executable_start <= (uint8_t *)p && (uint8_t *)p < _etext;
}
static void Append(struct Buffer *b, const char *fmt, ...) {
static relegated void Append(struct Buffer *b, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
b->i += kvsnprintf(b->p + b->i, b->n - b->i, fmt, va);
va_end(va);
}
static const char *ColorRegister(int r) {
static relegated wontreturn void RaiseCrash(int sig) {
sigset_t ss;
sigfillset(&ss);
sigdelset(&ss, sig);
sigprocmask(SIG_SETMASK, &ss, 0);
signal(sig, SIG_DFL);
kill(getpid(), sig);
_Exit(128 + sig);
}
static relegated const char *ColorRegister(int r) {
if (__nocolor) return "";
switch (r) {
case 0: // arg / res
@ -115,8 +126,8 @@ static const char *ColorRegister(int r) {
}
}
static bool AppendFileLine(struct Buffer *b, const char *addr2line,
const char *debugbin, long addr) {
static relegated bool AppendFileLine(struct Buffer *b, const char *addr2line,
const char *debugbin, long addr) {
ssize_t rc;
char *p, *q, buf[128];
int j, k, ws, pid, pfd[2];
@ -167,8 +178,8 @@ static bool AppendFileLine(struct Buffer *b, const char *addr2line,
}
}
static char *GetSymbolName(struct SymbolTable *st, int symbol, char **mem,
size_t *memsz) {
static relegated char *GetSymbolName(struct SymbolTable *st, int symbol,
char **mem, size_t *memsz) {
char *s, *t;
if ((s = __get_symbol_name(st, symbol)) && //
s[0] == '_' && s[1] == 'Z' && //
@ -348,7 +359,7 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
}
sys_write(2, b->p, MIN(b->i, b->n));
__print_maps();
_Exit(128 + sig);
RaiseCrash(sig);
}
#endif /* __aarch64__ */

View file

@ -97,7 +97,7 @@ static void RemoveCrashHandler(void *arg) {
strace_enabled(+1);
}
static void InstallCrashHandler(int sig, sigaction_f thunk, int extraflags) {
static void InstallCrashHandler(int sig, sigaction_f thunk) {
int e;
struct sigaction sa;
struct CrashHandler *ch;
@ -114,7 +114,7 @@ static void InstallCrashHandler(int sig, sigaction_f thunk, int extraflags) {
sigdelset(&sa.sa_mask, SIGABRT);
sigdelset(&sa.sa_mask, SIGBUS);
sigdelset(&sa.sa_mask, SIGURG);
sa.sa_flags = SA_SIGINFO | SA_NODEFER | extraflags;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (!sigaction(sig, &sa, &ch->old)) {
__cxa_atexit(RemoveCrashHandler, ch, 0);
}
@ -138,24 +138,21 @@ static void InstallCrashHandler(int sig, sigaction_f thunk, int extraflags) {
* useful, for example, if a program is caught in an infinite loop.
*/
void ShowCrashReports(void) {
int ef = 0;
struct sigaltstack ss;
_wantcrashreports = true;
if (!IsWindows()) {
ef = SA_ONSTACK;
struct sigaltstack ss;
ss.ss_flags = 0;
ss.ss_size = GetStackSize();
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
npassert((ss.ss_sp = _mapanon(GetStackSize())));
npassert(!sigaltstack(&ss, 0));
ss.ss_size = SIGSTKSZ;
ss.ss_sp = malloc(SIGSTKSZ);
sigaltstack(&ss, 0);
__cxa_atexit(free, ss.ss_sp, 0);
}
InstallCrashHandler(SIGQUIT, __got_sigquit, ef); // ctrl+\ aka ctrl+break
InstallCrashHandler(SIGFPE, __got_sigfpe, ef); // 1 / 0
InstallCrashHandler(SIGILL, __got_sigill, ef); // illegal instruction
InstallCrashHandler(SIGSEGV, __got_sigsegv, ef); // bad memory access
InstallCrashHandler(SIGTRAP, __got_sigtrap, ef); // bad system call
InstallCrashHandler(SIGBUS, __got_sigbus, ef); // misalign, mmap i/o failed
InstallCrashHandler(SIGURG, __got_sigurg, ef); // placeholder
InstallCrashHandler(SIGQUIT, __got_sigquit); // ctrl+\ aka ctrl+break
InstallCrashHandler(SIGFPE, __got_sigfpe); // 1 / 0
InstallCrashHandler(SIGILL, __got_sigill); // illegal instruction
InstallCrashHandler(SIGSEGV, __got_sigsegv); // bad memory access
InstallCrashHandler(SIGTRAP, __got_sigtrap); // bad system call
InstallCrashHandler(SIGBUS, __got_sigbus); // misalign, mmap i/o failed
InstallCrashHandler(SIGURG, __got_sigurg); // placeholder
_wantcrashreports = true;
GetSymbolTable();
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/gc.h"
#include "libc/assert.h"
#include "libc/intrin/likely.h"
#include "libc/mem/mem.h"

View file

@ -155,6 +155,11 @@ static char *Finish(void) {
}
}
static int Pause(void) {
pause();
return 0;
}
static int True(void) {
return 0;
}
@ -671,6 +676,7 @@ static int TryBuiltin(void) {
if (!strcmp(args[0], "true")) return True();
if (!strcmp(args[0], "test")) return Test();
if (!strcmp(args[0], "kill")) return Kill();
if (!strcmp(args[0], "pause")) return Pause();
if (!strcmp(args[0], "flock")) return Flock();
if (!strcmp(args[0], "chmod")) return Chmod();
if (!strcmp(args[0], "touch")) return Touch();

View file

@ -16,9 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/utmp.h"
@ -28,23 +30,37 @@
/**
* Prepares terminal for login.
*
* After this operation `fd` will be used for all stdio handles.
*
* @return 0 on success, or -1 w/ errno
* @raise EPERM if terminal is already controlling another sid?
* @raise EPERM if pledge() was used without tty
* @raise ENOTTY if `fd` isn't a teletypewriter
* @raise ENOSYS on Windows and Metal
* @raise EPERM if terminal is already controlling another sid
* @raise EBADF if `fd` isn't open
*/
int login_tty(int fd) {
int i, rc;
if (IsLinux() || IsBsd()) {
setsid();
if (!sys_ioctl(fd, TIOCSCTTY, 0)) {
for (i = 0; i < 3; ++i) dup2(fd, i);
if (fd > 2) close(fd);
rc = 0;
} else {
rc = -1;
}
} else {
int rc;
if (!IsLinux() && !IsBsd()) {
rc = enosys();
} else if (!isatty(fd)) {
rc = -1; // validate before changing the process's state
} else {
// become session leader
// we don't care if it fails due to already being the one
int e = errno;
sys_setsid();
errno = e;
// take control of teletypewriter (requires being leader)
if ((rc = sys_ioctl(fd, TIOCSCTTY, 0)) != -1) {
unassert(!sys_dup2(fd, 0, 0));
unassert(!sys_dup2(fd, 1, 0));
unassert(!sys_dup2(fd, 2, 0));
if (fd > 2) {
unassert(!sys_close(fd));
}
rc = 0;
}
}
STRACE("login_tty(%d) → %d% m", fd, rc);
return rc;

View file

@ -76,6 +76,7 @@ o/$(MODE)/libc/runtime/ftracer.o: private \
o/$(MODE)/libc/runtime/cosmo2.o \
o/$(MODE)/libc/runtime/fork-nt.o \
o/$(MODE)/libc/runtime/enable_tls.o \
o/$(MODE)/libc/runtime/printmemoryintervals.o \
o/$(MODE)/libc/runtime/findmemoryinterval.o \
o/$(MODE)/libc/runtime/sys_mprotect.greg.o \
@ -92,6 +93,7 @@ o/$(MODE)/libc/runtime/print.greg.o \
o/$(MODE)/libc/runtime/stackchkfail.o \
o/$(MODE)/libc/runtime/stackchkfaillocal.o \
o/$(MODE)/libc/runtime/winmain.greg.o \
o/$(MODE)/libc/runtime/interceptflag.greg.o \
o/$(MODE)/libc/runtime/opensymboltable.o: private \
CFLAGS += \
-Os \

View file

@ -60,7 +60,8 @@ static textexit void LogStackUse(void) {
if (!PLEDGED(STDIO) || !PLEDGED(WPATH) || !PLEDGED(CPATH)) return;
usage = GetStackUsage((char *)GetStackAddr(), GetStackSize());
e = errno;
if ((fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY, 0644)) != -1) {
if ((fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0644)) !=
-1) {
p = FormatUint64(stacklog, usage);
for (i = 0; i < __argc; ++i) {
n = strlen(__argv[i]);

View file

@ -21,6 +21,8 @@
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/getenv.internal.h"
#include "libc/intrin/weaken.h"
@ -176,6 +178,27 @@ __msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) {
struct WinArgs *wa =
(struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs)));
// allocate asan memory if needed
if (IsAsan()) {
uintptr_t shadowaddr = 0x7fff8000 + (stackaddr >> 3);
uintptr_t shadowend = 0x7fff8000 + ((stackaddr + stacksize) >> 3);
uintptr_t shallocaddr = ROUNDDOWN(shadowaddr, FRAMESIZE);
uintptr_t shallocend = ROUNDUP(shadowend, FRAMESIZE);
uintptr_t shallocsize = shallocend - shallocaddr;
__imp_MapViewOfFileEx(
(_mmi.p[1].h =
__imp_CreateFileMappingW(-1, &kNtIsInheritable, kNtPageReadwrite,
shallocsize >> 32, shallocsize, NULL)),
kNtFileMapWrite, 0, 0, shallocsize, (void *)shallocaddr);
_mmi.p[1].x = shallocaddr >> 16;
_mmi.p[1].y = (shallocaddr >> 16) + ((shallocsize - 1) >> 16);
_mmi.p[1].prot = PROT_READ | PROT_WRITE;
_mmi.p[1].flags = 0x00000022; // private+anonymous
_mmi.p[1].size = shallocsize;
_mmi.i = 2;
__asan_poison((void *)stackaddr, GetGuardSize(), kAsanStackOverflow);
}
// parse utf-16 command into utf-8 argv array in argument block
int count = GetDosArgv(cmdline, wa->argblock, ARRAYLEN(wa->argblock),
wa->argv, ARRAYLEN(wa->argv));
@ -231,13 +254,13 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
// sloppy flag-only check for early initialization
if (__strstr16(cmdline, u"--strace")) ++__strace;
#endif
if (_weaken(WinSockInit)) {
_weaken(WinSockInit)();
}
DeduplicateStdioHandles();
if (_weaken(WinMainStdin)) {
_weaken(WinMainStdin)();
}
if (_weaken(WinSockInit)) {
_weaken(WinSockInit)();
}
if (_weaken(WinMainForked)) {
_weaken(WinMainForked)();
}

31
libc/stdio/confstr.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- 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 2023 Justine Alexandra Roberts Tunney
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.
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.
*/
#include "libc/paths.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
size_t confstr(int name, char *buf, size_t len) {
if (name == _CS_PATH) {
return strlcpy(buf, _PATH_DEFPATH, len) + 1;
} else {
einval();
return 0;
}
}

View file

@ -22,6 +22,7 @@
#include "libc/intrin/weaken.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/fflush.internal.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/f.h"
@ -42,6 +43,7 @@
* @param mode can be:
* - `"r"` for reading from subprocess standard output
* - `"w"` for writing to subprocess standard input
* - `"e"` for `O_CLOEXEC` on returned file
* @raise EINVAL if `mode` is invalid or specifies read+write
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached
@ -53,7 +55,7 @@
* @threadsafe
*/
FILE *popen(const char *cmdline, const char *mode) {
FILE *f;
FILE *f, *f2;
int e, rc, pid, dir, flags, pipefds[2];
flags = fopenflags(mode);
if ((flags & O_ACCMODE) == O_RDONLY) {
@ -77,6 +79,14 @@ FILE *popen(const char *cmdline, const char *mode) {
// we can't rely on cloexec because cocmd builtins don't execve
if (pipefds[0] != !dir) unassert(!close(pipefds[0]));
if (pipefds[1] != !dir) unassert(!close(pipefds[1]));
// "The popen() function shall ensure that any streams from
// previous popen() calls that remain open in the parent
// process are closed in the new child process." -POSIX
for (int i = 0; i < __fflush.handles.i; ++i) {
if ((f2 = __fflush.handles.p[i]) && f2->pid) {
close(f2->fd);
}
}
_Exit(_cocmd(3,
(char *[]){
"popen",
@ -88,18 +98,21 @@ FILE *popen(const char *cmdline, const char *mode) {
default:
f->pid = pid;
unassert(!close(pipefds[!dir]));
if (!(flags & O_CLOEXEC)) {
unassert(!fcntl(pipefds[dir], F_SETFD, 0));
}
return f;
case -1:
e = errno;
unassert(!fclose(f));
unassert(!close(pipefds[!dir]));
fclose(f);
close(pipefds[!dir]);
errno = e;
return NULL;
}
} else {
e = errno;
unassert(!close(pipefds[0]));
unassert(!close(pipefds[1]));
close(pipefds[0]);
close(pipefds[1]);
errno = e;
return NULL;
}

View file

@ -21,9 +21,13 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
@ -42,8 +46,11 @@
#include "libc/sock/sock.h"
#include "libc/stdio/posix_spawn.h"
#include "libc/stdio/posix_spawn.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
@ -149,13 +156,14 @@ static textwindows errno_t posix_spawn_windows_impl(
};
// figure out the flags
short flags;
short flags = 0;
bool bInheritHandles = false;
uint32_t dwCreationFlags = 0;
for (i = 0; i < 3; ++i) {
bInheritHandles |= stdio_handle[i] != -1;
}
if (attrp && *attrp && !posix_spawnattr_getflags(attrp, &flags)) {
if (attrp && *attrp) {
flags = (*attrp)->flags;
if (flags & POSIX_SPAWN_SETSID) {
dwCreationFlags |= kNtDetachedProcess;
}
@ -208,8 +216,11 @@ static textwindows dontinline errno_t posix_spawn_windows(
return err;
}
static wontreturn void posix_spawn_die(const char *fail_func) {
STRACE("posix_spawn: %s failed% m", fail_func);
static wontreturn void posix_spawn_die(const char *thing) {
const char *reason; // print b/c there's no other way
if (!(reason = _strerdoc(errno))) reason = "Unknown error";
tinyprint(2, program_invocation_short_name, ": posix_spawn ", thing,
" failed: ", reason, "\n", NULL);
_Exit(127);
}
@ -229,7 +240,7 @@ static void RunUnixFileActions(struct _posix_faction *a) {
case _POSIX_SPAWN_OPEN: {
int t;
if ((t = open(a->path, a->oflag, a->mode)) == -1) {
posix_spawn_die("open");
posix_spawn_die(a->path);
}
if (t != a->fildes) {
if (dup2(t, a->fildes) == -1) {
@ -251,8 +262,7 @@ static void RunUnixFileActions(struct _posix_faction *a) {
/**
* Spawns process, the POSIX way.
*
* This provides superior process creation performance across systems.
*
* This provides superior process creation performance across systems
* Processes are normally spawned by calling fork() and execve(), but
* that goes slow on Windows if the caller has allocated a nontrivial
* number of memory mappings, all of which need to be copied into the
@ -260,10 +270,19 @@ static void RunUnixFileActions(struct _posix_faction *a) {
* fork() bears a similar cost that's 100x less bad, which is copying
* the page tables. So what this implementation does is on Windows it
* calls CreateProcess() directly and on UNIX it uses vfork() if it's
* possible (XNU and OpenBSD don't have it).
* possible (XNU and OpenBSD don't have it). On UNIX this API has the
* benefit of avoiding the footguns of using vfork() directly because
* this implementation will ensure signal handlers can't be called in
* the child process since that'd likely corrupt the parent's memory.
*
* If the child process exits with status 127 then use the `--strace`
* flag to get an explanation of failures that occurred during spawn.
* This implementation doesn't create a pipe like Musl Libc, and will
* report errors in the child process by printing the cause to stderr
* which ensures posix_spawn stays fast, and works w/ `RLIMIT_FILENO`
* There is however one particular case where knowing the execve code
* truly matters, which is ETXTBSY. Thankfully, there's a rarely used
* signal with the exact same magic number across supported platforms
* called SIGVTALRM which we send to wait4 when execve raises ETXTBSY
* Please note that on Windows posix_spawn() returns ETXTBSY directly
*
* @param pid if non-null shall be set to child pid on success
* @param path is resolved path of program which is not `$PATH` searched
@ -280,67 +299,85 @@ errno_t posix_spawn(int *pid, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const argv[],
char *const envp[]) {
short flags = 0;
sigset_t sigmask;
int s, child, policy;
struct sched_param param;
struct sigaction dfl = {0};
if (IsWindows()) {
return posix_spawn_windows(pid, path, file_actions, attrp, argv, envp);
}
sigset_t blockall, oldmask;
int child, res, cs, e = errno;
if (access(path, X_OK)) {
res = errno;
errno = e;
return res;
}
sigfillset(&blockall);
sys_sigprocmask(SIG_SETMASK, &blockall, &oldmask);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
if (!(child = vfork())) {
if (attrp && *attrp) {
posix_spawnattr_getflags(attrp, &flags);
if (flags & POSIX_SPAWN_SETSID) {
if (setsid()) {
posix_spawn_die("setsid");
}
}
if (flags & POSIX_SPAWN_SETPGROUP) {
if (setpgid(0, (*attrp)->pgroup)) {
posix_spawn_die("setpgid");
}
}
if (flags & POSIX_SPAWN_SETSIGMASK) {
posix_spawnattr_getsigmask(attrp, &sigmask);
sigprocmask(SIG_SETMASK, &sigmask, 0);
}
if ((flags & POSIX_SPAWN_RESETIDS) &&
(setgid(getgid()) || setuid(getuid()))) {
posix_spawn_die("setuid");
}
if (flags & POSIX_SPAWN_SETSIGDEF) {
for (s = 1; s < 32; s++) {
if (sigismember(&(*attrp)->sigdefault, s)) {
if (sigaction(s, &dfl, 0)) {
posix_spawn_die("sigaction");
}
}
}
sigset_t *childmask;
struct sigaction dfl = {0};
short flags = attrp && *attrp ? (*attrp)->flags : 0;
for (int sig = 1; sig < _NSIG; sig++) {
if (__sighandrvas[sig] != (long)SIG_DFL &&
(__sighandrvas[sig] != (long)SIG_IGN ||
((flags & POSIX_SPAWN_SETSIGDEF) &&
sigismember(&(*attrp)->sigdefault, sig) == 1) ||
sig == SIGVTALRM)) {
sigaction(sig, &dfl, 0);
}
}
if (flags & POSIX_SPAWN_SETSIGMASK) {
childmask = &(*attrp)->sigmask;
} else {
childmask = &oldmask;
}
sys_sigprocmask(SIG_SETMASK, childmask, 0);
if (flags & POSIX_SPAWN_SETSID) {
setsid();
}
if ((flags & POSIX_SPAWN_SETPGROUP) && setpgid(0, (*attrp)->pgroup)) {
posix_spawn_die("setpgid");
}
if ((flags & POSIX_SPAWN_RESETIDS) && setgid(getgid())) {
posix_spawn_die("setgid");
}
if ((flags & POSIX_SPAWN_RESETIDS) && setgid(getgid())) {
posix_spawn_die("setuid");
}
if (file_actions) {
RunUnixFileActions(*file_actions);
}
if (attrp && *attrp) {
if (IsLinux() || IsFreebsd() || IsNetbsd()) {
if (flags & POSIX_SPAWN_SETSCHEDULER) {
posix_spawnattr_getschedpolicy(attrp, &policy);
posix_spawnattr_getschedparam(attrp, &param);
if (sched_setscheduler(0, policy, &param) == -1) {
if (sched_setscheduler(0, (*attrp)->schedpolicy,
&(*attrp)->schedparam) == -1) {
posix_spawn_die("sched_setscheduler");
}
}
if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
posix_spawnattr_getschedparam(attrp, &param);
if (sched_setparam(0, &param)) {
if (sched_setparam(0, &(*attrp)->schedparam)) {
posix_spawn_die("sched_setparam");
}
}
}
if (flags & POSIX_SPAWN_SETRLIMIT) {
for (int rez = 0; rez <= ARRAYLEN((*attrp)->rlim); ++rez) {
if ((*attrp)->rlimset & (1u << rez)) {
if (setrlimit(rez, (*attrp)->rlim + rez)) {
posix_spawn_die("setrlimit");
}
}
}
}
if (!envp) envp = environ;
execve(path, argv, envp);
posix_spawn_die("execve");
} else if (child != -1) {
if (errno == ETXTBSY) {
sys_kill(getpid(), SIGVTALRM, 1);
}
posix_spawn_die(path);
}
sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
pthread_setcancelstate(cs, 0);
if (child != -1) {
if (pid) *pid = child;
return 0;
} else {

View file

@ -1,8 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWN_H_
#define COSMOPOLITAN_LIBC_STDIO_SPAWN_H_
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigset.h"
#define POSIX_SPAWN_USEVFORK 0
#define POSIX_SPAWN_RESETIDS 1
#define POSIX_SPAWN_SETPGROUP 2
#define POSIX_SPAWN_SETSIGDEF 4
@ -10,6 +12,7 @@
#define POSIX_SPAWN_SETSCHEDPARAM 16
#define POSIX_SPAWN_SETSCHEDULER 32
#define POSIX_SPAWN_SETSID 128
#define POSIX_SPAWN_SETRLIMIT 256
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -45,6 +48,8 @@ int posix_spawnattr_getsigmask(const posix_spawnattr_t *, sigset_t *);
int posix_spawnattr_setsigmask(posix_spawnattr_t *, const sigset_t *);
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *, sigset_t *);
int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *);
int posix_spawnattr_getrlimit(const posix_spawnattr_t *, int, struct rlimit *);
int posix_spawnattr_setrlimit(posix_spawnattr_t *, int, const struct rlimit *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_
#define COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigset.h"
@ -11,15 +12,16 @@
COSMOPOLITAN_C_START_
struct _posix_spawna {
char flags;
short flags;
bool schedparam_isset;
bool schedpolicy_isset;
bool sigmask_isset;
int pgroup;
int rlimset;
int schedpolicy;
struct sched_param schedparam;
sigset_t sigmask;
sigset_t sigdefault;
struct rlimit rlim[16];
};
struct _posix_faction {

View file

@ -21,6 +21,7 @@
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/stdio/posix_spawn.h"
#include "libc/stdio/posix_spawn.internal.h"
@ -76,11 +77,6 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
/**
* Sets posix_spawn() flags.
*
* Setting these flags is needed in order for the other setters in this
* function to take effect. If a flag is known but unsupported by the
* host platform, it'll be silently removed from the flags. You can
* check for this by calling the getter afterwards.
*
* @param attr was initialized by posix_spawnattr_init()
* @param flags may have any of the following
* - `POSIX_SPAWN_RESETIDS`
@ -94,9 +90,6 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
* @raise EINVAL if `flags` has invalid bits
*/
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
if (!(IsLinux() || IsFreebsd() || IsNetbsd())) {
flags &= ~(POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER);
}
if (flags &
~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF |
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM |
@ -107,134 +100,62 @@ int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
return 0;
}
/**
* Gets process group id associated with attributes.
*
* @param attr was initialized by posix_spawnattr_init()
* @param pgroup receives the result on success
* @return 0 on success, or errno on error
*/
int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, int *pgroup) {
*pgroup = (*attr)->pgroup;
return 0;
}
/**
* Specifies process group into which child process is placed.
*
* Setting `pgroup` to zero will ensure newly created processes are
* placed within their own brand new process group.
*
* This setter also sets the `POSIX_SPAWN_SETPGROUP` flag.
*
* @param attr was initialized by posix_spawnattr_init()
* @param pgroup is the process group id, or 0 for self
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
(*attr)->flags |= POSIX_SPAWN_SETPGROUP;
(*attr)->pgroup = pgroup;
return 0;
}
/**
* Gets scheduler policy that'll be used for spawned process.
*
* If the setter wasn't called then this function will return the
* scheduling policy of the current process.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedpolicy receives the result
* @return 0 on success, or errno on error
* @raise ENOSYS if platform support isn't available
*/
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
int *schedpolicy) {
int rc, e = errno;
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
if (!a->schedpolicy_isset) {
rc = sched_getscheduler(0);
if (rc == -1) {
rc = errno;
errno = e;
return rc;
}
a->schedpolicy = rc;
a->schedpolicy_isset = true;
}
*schedpolicy = a->schedpolicy;
return 0;
}
/**
* Specifies scheduler policy override for spawned process.
*
* Scheduling policies are inherited by default. Use this to change it.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedpolicy receives the result
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
(*attr)->schedpolicy = schedpolicy;
(*attr)->schedpolicy_isset = true;
return 0;
}
/**
* Gets scheduler parameter that'll be used for spawned process.
*
* If the setter wasn't called then this function will return the
* scheduling parameter of the current process.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedparam receives the result
* @return 0 on success, or errno on error
* @raise ENOSYS if platform support isn't available
*/
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
struct sched_param *schedparam) {
int rc, e = errno;
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
if (!a->schedparam_isset) {
rc = sched_getparam(0, &a->schedparam);
if (rc == -1) {
rc = errno;
errno = e;
return rc;
}
a->schedparam_isset = true;
}
*schedparam = a->schedparam;
return 0;
}
/**
* Specifies scheduler parameter override for spawned process.
*
* Scheduling parameters are inherited by default. Use this to change it.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedparam receives the result
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
const struct sched_param *schedparam) {
(*attr)->schedparam = *schedparam;
(*attr)->schedparam_isset = true;
return 0;
}
/**
* Gets signal mask for sigprocmask() in child process.
*
* If the setter wasn't called then this function will return the
* scheduling parameter of the current process.
* The signal mask is applied to the child process in such a way that
* signal handlers from the parent process can't get triggered in the
* child process.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
sigset_t *sigmask) {
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
if (!a->sigmask_isset) {
npassert(!sigprocmask(SIG_SETMASK, 0, &a->sigmask));
a->sigmask_isset = true;
}
*sigmask = a->sigmask;
*sigmask = (*attr)->sigmask;
return 0;
}
/**
* Specifies signal mask for sigprocmask() in child process.
*
* Signal masks are inherited by default. Use this to change it.
* This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
const sigset_t *sigmask) {
(*attr)->flags |= POSIX_SPAWN_SETSIGMASK;
(*attr)->sigmask = *sigmask;
(*attr)->sigmask_isset = true;
return 0;
}
@ -252,10 +173,119 @@ int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
/**
* Specifies which signals should be restored to `SIG_DFL`.
*
* This routine isn't necessary in most cases, since posix_spawn() by
* default will try to avoid vfork() race conditions by tracking what
* signals have a handler function and then resets them automatically
* within the child process, before applying the child's signal mask.
* This function may be used to ensure the `SIG_IGN` disposition will
* not propagate across execve in cases where this process explicitly
* set the signals to `SIG_IGN` earlier (since posix_spawn() will not
* issue O(128) system calls just to be totally pedantic about that).
*
* Using this setter automatically sets `POSIX_SPAWN_SETSIGDEF`.
*
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
const sigset_t *sigdefault) {
(*attr)->flags |= POSIX_SPAWN_SETSIGDEF;
(*attr)->sigdefault = *sigdefault;
return 0;
}
/**
* Gets resource limit for spawned process.
*
* @return 0 on success, or errno on error
* @raise EINVAL if `resource` is invalid
* @raise ENOENT if `resource` is absent
*/
int posix_spawnattr_getrlimit(const posix_spawnattr_t *attr, int resource,
struct rlimit *rlim) {
if ((0 <= resource && resource < ARRAYLEN((*attr)->rlim))) {
if (((*attr)->rlimset & (1u << resource))) {
*rlim = (*attr)->rlim[resource];
return 0;
} else {
return ENOENT;
}
} else {
return EINVAL;
}
}
/**
* Sets resource limit on spawned process.
*
* Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`.
*
* @return 0 on success, or errno on error
* @raise EINVAL if resource is invalid
*/
int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource,
const struct rlimit *rlim) {
if (0 <= resource && resource < ARRAYLEN((*attr)->rlim)) {
(*attr)->flags |= POSIX_SPAWN_SETRLIMIT;
(*attr)->rlimset |= 1u << resource;
(*attr)->rlim[resource] = *rlim;
return 0;
} else {
return EINVAL;
}
}
/**
* Gets scheduler policy that'll be used for spawned process.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedpolicy receives the result
* @return 0 on success, or errno on error
*/
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
int *schedpolicy) {
*schedpolicy = (*attr)->schedpolicy;
return 0;
}
/**
* Specifies scheduler policy override for spawned process.
*
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`.
*
* @param attr was initialized by posix_spawnattr_init()
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
(*attr)->flags |= POSIX_SPAWN_SETSCHEDULER;
(*attr)->schedpolicy = schedpolicy;
return 0;
}
/**
* Gets scheduler parameter.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedparam receives the result
* @return 0 on success, or errno on error
*/
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
struct sched_param *schedparam) {
*schedparam = (*attr)->schedparam;
return 0;
}
/**
* Specifies scheduler parameter override for spawned process.
*
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`.
*
* @param attr was initialized by posix_spawnattr_init()
* @param schedparam receives the result
* @return 0 on success, or errno on error
*/
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
const struct sched_param *schedparam) {
(*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM;
(*attr)->schedparam = *schedparam;
return 0;
}

View file

@ -1,11 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_STDIO_H_
#define COSMOPOLITAN_LIBC_STDIO_H_
#define EOF -1 /* end of file */
#define WEOF -1u /* end of file (multibyte) */
#define _IOFBF 0 /* fully buffered */
#define _IOLBF 1 /* line buffered */
#define _IONBF 2 /* no buffering */
#define EOF -1 /* end of file */
#define WEOF -1u /* end of file (multibyte) */
#define _IOFBF 0 /* fully buffered */
#define _IOLBF 1 /* line buffered */
#define _IONBF 2 /* no buffering */
#define _CS_PATH 0
#define L_tmpnam 20
#define L_ctermid 20
@ -80,6 +81,7 @@ int setvbuf(FILE *, char *, int, size_t);
int pclose(FILE *);
char *ctermid(char *);
void perror(const char *) relegated;
size_t confstr(int, char *, size_t);
typedef uint64_t fpos_t;
char *gets(char *) paramsnonnull();

View file

@ -34,7 +34,7 @@
* This creates a secure temporary file inside $TMPDIR. If it isn't
* defined, then /tmp is used on UNIX and GetTempPath() is used on the
* New Technology. This resolution of $TMPDIR happens once in a ctor,
* which is copied to the `kTmpDir` global.
* which is copied to the `kTmpPath` global.
*
* Once fclose() is called, the returned file is guaranteed to be
* deleted automatically. On UNIX the file is unlink()'d before this

View file

@ -62,8 +62,8 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) {
r.ax = 0;
r.dx = 0;
for (;;) {
#ifdef __x86_64__
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
#if defined(__x86_64__) && !IsModeDbg()
if (!((uintptr_t)(src + r.dx) & 15)) {
r = tprecode8to16_sse2(dst, dstsize, src, r);
}
#endif

View file

@ -3,7 +3,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
char *mktemp(char *) returnsnonnull paramsnonnull() __wur;
char *mktemp(char *) returnsnonnull paramsnonnull();
char *mkdtemp(char *) paramsnonnull() __wur;
int mkstemp(char *) paramsnonnull() __wur;
int mkstemps(char *, int) paramsnonnull() __wur;

View file

@ -27,6 +27,7 @@
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigaltstack.h"
#include "libc/calls/struct/sigset.h"
#include "libc/intrin/dll.h"
#include "libc/runtime/runtime.h"
@ -75,7 +76,6 @@ struct PosixThread {
void *(*start)(void *); // creation callback
void *arg; // start's parameter
void *rc; // start's return value
char *altstack; // thread sigaltstack
char *tls; // bottom of tls allocation
struct CosmoTib *tib; // middle of tls allocation
struct Dll list; // list of threads

View file

@ -20,7 +20,6 @@
#include "libc/atomic.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaltstack.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -29,7 +28,7 @@
#include "libc/intrin/bits.h"
#include "libc/intrin/bsr.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/popcnt.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/internal.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
@ -60,64 +59,30 @@ static unsigned long roundup2pow(unsigned long x) {
}
void _pthread_free(struct PosixThread *pt) {
static atomic_uint freed;
if (pt->flags & PT_STATIC) return;
free(pt->tls);
if ((pt->flags & PT_OWNSTACK) && //
pt->attr.__stackaddr && //
pt->attr.__stackaddr != MAP_FAILED) {
npassert(!munmap(pt->attr.__stackaddr, pt->attr.__stacksize));
}
if (pt->altstack) {
free(pt->altstack);
unassert(!munmap(pt->attr.__stackaddr, pt->attr.__stacksize));
}
free(pt);
if (popcnt(atomic_fetch_add_explicit(&freed, 1, memory_order_acq_rel)) == 1) {
malloc_trim(0);
}
}
void pthread_kill_siblings_np(void) {
struct Dll *e, *e2;
struct PosixThread *pt, *self;
self = (struct PosixThread *)__get_tls()->tib_pthread;
pthread_spin_lock(&_pthread_lock);
for (e = dll_first(_pthread_list); e; e = e2) {
e2 = dll_next(_pthread_list, e);
pt = POSIXTHREAD_CONTAINER(e);
if (pt != self) {
pthread_kill((pthread_t)pt, SIGKILL);
dll_remove(&_pthread_list, e);
pthread_spin_unlock(&_pthread_lock);
_pthread_free(pt);
}
}
pthread_spin_unlock(&_pthread_lock);
}
static int PosixThread(void *arg, int tid) {
void *rc;
struct sigaltstack ss;
struct PosixThread *pt = arg;
unassert(__get_tls()->tib_tid > 0);
if (pt->altstack) {
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
ss.ss_sp = pt->altstack;
if (sigaltstack(&ss, 0)) {
notpossible;
}
}
if (pt->attr.__inheritsched == PTHREAD_EXPLICIT_SCHED) {
_pthread_reschedule(pt);
}
// set long jump handler so pthread_exit can bring control back here
if (!setjmp(pt->exiter)) {
__get_tls()->tib_pthread = (pthread_t)pt;
npassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
unassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
rc = pt->start(pt->arg);
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
npassert(!pt->cleanup);
unassert(!pt->cleanup);
// calling pthread_exit() will either jump back here, or call exit
pthread_exit(rc);
}
@ -251,11 +216,6 @@ static errno_t pthread_create_impl(pthread_t *thread,
}
}
// setup signal handler stack
if (_wantcrashreports && !IsWindows()) {
pt->altstack = malloc(SIGSTKSZ);
}
// set initial status
if (!pt->attr.__havesigmask) {
pt->attr.__havesigmask = true;

View file

@ -194,7 +194,6 @@ int pthread_spin_unlock(pthread_spinlock_t *) paramsnonnull();
int pthread_testcancel_np(void);
int pthread_tryjoin_np(pthread_t, void **);
int pthread_yield(void);
void pthread_kill_siblings_np(void);
pthread_id_np_t pthread_getthreadid_np(void);
pthread_t pthread_self(void) pureconst;
void *pthread_getspecific(pthread_key_t);