diff --git a/libc/calls/getcwd.greg.c b/libc/calls/getcwd.greg.c index f7bc1801fbb..e68cd53c609 100644 --- a/libc/calls/getcwd.greg.c +++ b/libc/calls/getcwd.greg.c @@ -164,6 +164,6 @@ int __getcwd(char *buf, size_t size) { } else { rc = sys_getcwd_metal(buf, size); } - STRACE("__getcwd([%#hhs], %'zu) → %d% m", rc != -1 ? buf : "n/a", size, rc); + STRACE("getcwd([%#hhs], %'zu) → %d% m", rc != -1 ? buf : "n/a", size, rc); return rc; } diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index 26014b92da5..e6cb1b4058b 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -126,7 +126,8 @@ textwindows int ntspawn( if (CreateProcess(sb->path, sb->cmdline, 0, 0, true, dwCreationFlags | kNtCreateUnicodeEnvironment | kNtExtendedStartupinfoPresent | - kNtInheritParentAffinity, + kNtInheritParentAffinity | + GetPriorityClass(GetCurrentProcess()), sb->envblock, opt_lpCurrentDirectory, &info.StartupInfo, opt_out_lpProcessInformation)) { rc = 0; diff --git a/libc/calls/park.c b/libc/calls/park.c index 5b4890de870..e6fa668ee7b 100644 --- a/libc/calls/park.c +++ b/libc/calls/park.c @@ -32,11 +32,7 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask, bool restartable) { int sig; if (_check_cancel() == -1) return -1; - if ((sig = __sig_get(waitmask))) { - int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); - if (_check_cancel() == -1) return -1; - if (!restartable || handler_was_called == 1) return eintr(); - } + if ((sig = __sig_get(waitmask))) goto HandleSignal; int expect = 0; atomic_int futex = 0; struct PosixThread *pt = _pthread_self(); @@ -45,9 +41,12 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask, bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay); atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release); if (ok && (sig = __sig_get(waitmask))) { + HandleSignal: int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); if (_check_cancel() == -1) return -1; - if (!restartable || handler_was_called == 1) return eintr(); + if (!restartable || (handler_was_called & SIG_HANDLED_NO_RESTART)) { + return eintr(); + } } return 0; } diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index 3e37c8da446..d5c63ada755 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -55,6 +55,8 @@ #include "libc/thread/tls.h" #ifdef __x86_64__ +#define POLL_INTERVAL_MS 10 + // Polls on the New Technology. // // This function is used to implement poll() and select(). You may poll @@ -184,7 +186,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds, remain = timespec_sub(deadline, now); millis = timespec_tomillis(remain); waitfor = MIN(millis, 0xffffffffu); - waitfor = MIN(waitfor, __SIG_POLL_INTERVAL_MS); + waitfor = MIN(waitfor, POLL_INTERVAL_MS); if (waitfor) { POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor, timespec_tomillis(remain)); diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index cb3e1eadd2e..63d7dc4463b 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -723,8 +723,6 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) { int sig; int64_t sem; uint32_t wi, ms = -1; - int handler_was_called; - struct PosixThread *pt; if (!__ttyconf.vmin) { if (!__ttyconf.vtime) { return 0; // non-blocking w/o raising eagain @@ -732,31 +730,24 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) { ms = __ttyconf.vtime * 100; } } - if (f->flags & _O_NONBLOCK) { - return eagain(); // standard unix non-blocking - } if (_check_cancel() == -1) return -1; - if ((sig = __sig_get(waitmask))) { - handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); - if (_check_cancel() == -1) return -1; - if (handler_was_called != 1) return -2; - return eintr(); - } - pt = _pthread_self(); - pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0); - pthread_cleanup_push((void *)CloseHandle, (void *)sem); + if (f->flags & _O_NONBLOCK) return eagain(); + if ((sig = __sig_get(waitmask))) goto DeliverSignal; + struct PosixThread *pt = _pthread_self(); pt->pt_blkmask = waitmask; + pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0); atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release); wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms); atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release); - pthread_cleanup_pop(true); + CloseHandle(sem); if (wi == kNtWaitTimeout) return 0; // vtime elapsed if (wi == 0) return -2; // console data if (wi != 1) return __winerr(); // wait failed if (!(sig = __sig_get(waitmask))) return eintr(); - handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); +DeliverSignal: + int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); if (_check_cancel() == -1) return -1; - if (handler_was_called != 1) return -2; + if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) return -2; return eintr(); } diff --git a/libc/calls/readwrite-nt.c b/libc/calls/readwrite-nt.c index 266dc54cf39..095503241db 100644 --- a/libc/calls/readwrite-nt.c +++ b/libc/calls/readwrite-nt.c @@ -16,15 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" #include "libc/calls/createfileflags.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/fd.internal.h" -#include "libc/calls/struct/sigset.internal.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/errno.h" -#include "libc/intrin/atomic.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/errors.h" #include "libc/nt/events.h" @@ -34,28 +31,10 @@ #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/stdio/sysparam.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" #ifdef __x86_64__ -struct ReadwriteResources { - int64_t handle; - struct NtOverlapped *overlap; -}; - -static void UnwindReadwrite(void *arg) { - uint32_t got; - struct ReadwriteResources *rwc = arg; - CancelIoEx(rwc->handle, rwc->overlap); - GetOverlappedResult(rwc->handle, rwc->overlap, &got, true); - CloseHandle(rwc->overlap->hEvent); -} - /** * Runs code that's common to read/write/pread/pwrite/etc on Windows. * @@ -64,17 +43,11 @@ static void UnwindReadwrite(void *arg) { */ textwindows ssize_t sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, - int64_t handle, uint64_t waitmask, + int64_t handle, sigset_t waitmask, bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *, struct NtOverlapped *)) { - bool32 ok; - int sig = 0; + int sig; uint32_t exchanged; - int olderror = errno; - bool eagained = false; - bool canceled = false; - int handler_was_called; - struct PosixThread *pt; struct Fd *f = g_fds.p + fd; // win32 i/o apis generally take 32-bit values thus we implicitly @@ -106,33 +79,26 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, } RestartOperation: + bool eagained = false; + // check for signals and cancelation + if (_check_cancel() == -1) return -1; // ECANCELED + if ((sig = __sig_get(waitmask))) goto HandleInterrupt; + // signals have already been fully blocked by caller // perform i/o operation with atomic signal/cancel checking struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0), .Pointer = offset}; - struct ReadwriteResources rwc = {handle, &overlap}; - pthread_cleanup_push(UnwindReadwrite, &rwc); - ok = ReadOrWriteFile(handle, data, size, 0, &overlap); + bool32 ok = ReadOrWriteFile(handle, data, size, 0, &overlap); if (!ok && GetLastError() == kNtErrorIoPending) { // win32 says this i/o operation needs to block if (f->flags & _O_NONBLOCK) { // abort the i/o operation if file descriptor is in non-blocking mode CancelIoEx(handle, &overlap); eagained = true; - } else if (_check_cancel()) { - // _check_cancel() can go three ways: - // 1. it'll return 0 if we're fine and no thread cancelation happened - // 2. it'll pthread_exit() and cleanup, when cancelation was deferred - // 3. it'll return -1 and raise ECANCELED if a cancelation was masked - CancelIoEx(handle, &overlap); - canceled = true; - } else if ((sig = __sig_get(waitmask))) { - // we've dequeued a signal that was pending per caller's old sigmask - // we can't call the signal handler until we release win32 resources - CancelIoEx(handle, &overlap); } else { // wait until i/o either completes or is canceled by another thread // we avoid a race condition by having a second mask for unblocking + struct PosixThread *pt; pt = _pthread_self(); pt->pt_blkmask = waitmask; pt->pt_iohandle = handle; @@ -147,30 +113,13 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, if (ok) { ok = GetOverlappedResult(handle, &overlap, &exchanged, true); } - pthread_cleanup_pop(false); CloseHandle(overlap.hEvent); - // if we acknowledged a pending masked mode cancelation request then - // we must pass it to the caller immediately now that cleanup's done - if (canceled) { - return ecanceled(); - } - - // if we removed a pending signal then we must raise it - // it's now safe to call a signal handler that longjmps - if (sig) { - handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); - if (_check_cancel() == -1) return -1; - } else { - handler_was_called = 0; - } - // if i/o succeeded then return its result if (ok) { if (!pwriting && seekable) { f->pointer = offset + exchanged; } - errno = olderror; return exchanged; } @@ -180,17 +129,15 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, if (eagained) { return eagain(); } - // at this point the i/o must have been canceled due to a signal. - // this could be because we found the signal earlier and canceled - // ourself. otherwise it's due to a kill from another thread that - // added something to our mask and canceled our i/o, so we check. - if (!handler_was_called && (sig = __sig_get(waitmask))) { - handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); - if (_check_cancel() == -1) return -1; - } - // read() is @restartable unless non-SA_RESTART hands were called - if (handler_was_called != 1) { - goto RestartOperation; + // otherwise it must be due to a kill() via __sig_cancel() + if ((sig = __sig_get(waitmask))) { + HandleInterrupt: + int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); + if (_check_cancel() == -1) return -1; // possible if we SIGTHR'd + // read() is @restartable unless non-SA_RESTART hands were called + if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) { + goto RestartOperation; + } } return eintr(); } diff --git a/libc/calls/sig.c b/libc/calls/sig.c index 467c19939c9..8c620ee93ab 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -18,8 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/sysv/consts/sig.h" #include "ape/sections.internal.h" -#include "libc/assert.h" -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/sig.internal.h" #include "libc/calls/state.internal.h" @@ -30,34 +28,26 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" #include "libc/intrin/bsf.h" #include "libc/intrin/describebacktrace.internal.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/popcnt.h" +#include "libc/intrin/dll.h" #include "libc/intrin/strace.internal.h" -#include "libc/intrin/weaken.h" #include "libc/nt/console.h" #include "libc/nt/enum/context.h" #include "libc/nt/enum/exceptionhandleractions.h" #include "libc/nt/enum/signal.h" #include "libc/nt/enum/status.h" -#include "libc/nt/errors.h" #include "libc/nt/runtime.h" #include "libc/nt/signals.h" -#include "libc/nt/struct/context.h" #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" -#include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/ss.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" #ifdef __x86_64__ /** @@ -206,16 +196,19 @@ textwindows int __sig_raise(volatile int sig, int sic) { memory_order_acquire); // call the user's signal handler - char ssbuf[2][128]; + char ssbuf[128]; siginfo_t si = {.si_signo = sig, .si_code = sic}; - STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva), - (DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask), - (DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask)); + STRACE("__sig_raise(%G, %t) mask %s", sig, __sig_handler(rva), + (DescribeSigset)(ssbuf, 0, (sigset_t *)&pt->tib->tib_sigmask)); __sig_handler(rva)(sig, &si, &ctx); (void)ssbuf; // record this handler - handler_was_called |= (flags & SA_RESTART) ? 2 : 1; + if (flags & SA_RESTART) { + handler_was_called |= SIG_HANDLED_SA_RESTART; + } else { + handler_was_called |= SIG_HANDLED_NO_RESTART; + } } // restore sigmask @@ -320,12 +313,18 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) { __sig_terminate(sig); } + // ignore signals already pending + uintptr_t th = _pthread_syshand(pt); + if (atomic_load_explicit(&pt->tib->tib_sigpending, memory_order_acquire) & + (1ull << (sig - 1))) { + return 0; + } + // take control of thread // suspending the thread happens asynchronously // however getting the context blocks until it's frozen static pthread_spinlock_t killer_lock; pthread_spin_lock(&killer_lock); - uintptr_t th = _pthread_syshand(pt); if (SuspendThread(th) == -1u) { STRACE("SuspendThread failed w/ %d", GetLastError()); pthread_spin_unlock(&killer_lock); @@ -414,6 +413,10 @@ textwindows void __sig_generate(int sig, int sic) { STRACE("terminating on %G due to no handler", sig); __sig_terminate(sig); } + if (atomic_load_explicit(&__sig.pending, memory_order_acquire) & + (1ull << (sig - 1))) { + return; + } BLOCK_SIGNALS; _pthread_lock(); for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) { @@ -428,7 +431,6 @@ textwindows void __sig_generate(int sig, int sic) { // choose this thread if it isn't masking sig if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) & (1ull << (sig - 1)))) { - STRACE("generating %G by killing %d", sig, _pthread_tid(pt)); _pthread_ref(pt); mark = pt; break; @@ -439,7 +441,6 @@ textwindows void __sig_generate(int sig, int sic) { if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) && !(atomic_load_explicit(&pt->pt_blkmask, memory_order_relaxed) & (1ull << (sig - 1)))) { - STRACE("generating %G by unblocking %d", sig, _pthread_tid(pt)); _pthread_ref(pt); mark = pt; break; @@ -450,7 +451,6 @@ textwindows void __sig_generate(int sig, int sic) { __sig_killer(mark, sig, sic); _pthread_unref(mark); } else { - STRACE("all threads block %G so adding to pending signals of process", sig); atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1), memory_order_relaxed); } diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index d6f2c1bace4..cedc38338af 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -3,13 +3,8 @@ #include "libc/calls/struct/sigset.h" #include "libc/thread/posixthread.internal.h" -#define __SIG_LOCK_INTERVAL_MS 1000 /* semaphore synchronization: solid */ -#define __SIG_SIG_INTERVAL_MS 1000 /* posix signal polyfill also solid */ -#define __SIG_PROC_INTERVAL_MS 1000 /* process waiting also pretty good */ -#define __SIG_IO_INTERVAL_MS 1000 /* read/write cancel/notify is good */ -#define __SIG_POLL_INTERVAL_MS 20 /* poll on windows is dumpster fire */ -#define __SIG_LOGGING_INTERVAL_MS 1700 -#define __SIG_QUEUE_LENGTH 32 +#define SIG_HANDLED_NO_RESTART 1 +#define SIG_HANDLED_SA_RESTART 2 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/calls/struct/timespec.h b/libc/calls/struct/timespec.h index 739cfadad68..3b8b04c314e 100644 --- a/libc/calls/struct/timespec.h +++ b/libc/calls/struct/timespec.h @@ -41,10 +41,10 @@ int sys_futex(int *, int, int, const struct timespec *, int *); static inline struct timespec timespec_fromseconds(int64_t __x) { return (struct timespec){__x}; } -static inline bool timespec_iszero(struct timespec __ts) { +static inline int timespec_iszero(struct timespec __ts) { return !(__ts.tv_sec | __ts.tv_nsec); } -static inline bool timespec_isvalid(struct timespec __ts) { +static inline int timespec_isvalid(struct timespec __ts) { return __ts.tv_sec >= 0 && __ts.tv_nsec + 0ull < 1000000000ull; } #endif /* _COSMO_SOURCE */ diff --git a/libc/calls/struct/timeval.h b/libc/calls/struct/timeval.h index 9c12dfa1336..85f41e222ea 100644 --- a/libc/calls/struct/timeval.h +++ b/libc/calls/struct/timeval.h @@ -39,10 +39,10 @@ static inline struct timeval timeval_fromseconds(int64_t __x) { static inline struct timespec timeval_totimespec(struct timeval __tv) { return (struct timespec){__tv.tv_sec, __tv.tv_usec * 1000}; } -static inline bool timeval_iszero(struct timeval __tv) { +static inline int timeval_iszero(struct timeval __tv) { return !(__tv.tv_sec | __tv.tv_usec); } -static inline bool timeval_isvalid(struct timeval __tv) { +static inline int timeval_isvalid(struct timeval __tv) { return __tv.tv_sec >= 0 && __tv.tv_usec + 0ull < 1000000ull; } #endif /* _COSMO_SOURCE */ diff --git a/libc/calls/ucontext.c b/libc/calls/ucontext.c index cb99bb669e2..66281516bd7 100644 --- a/libc/calls/ucontext.c +++ b/libc/calls/ucontext.c @@ -17,17 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/ucontext.h" -#include "libc/calls/struct/sigset.h" -#include "libc/runtime/runtime.h" +#include "libc/calls/struct/sigset.internal.h" +#include "libc/dce.h" +#include "libc/intrin/atomic.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/tls.h" int __tailcontext(const ucontext_t *); -static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) { - // now context switching needs to go 14x slower - return sigprocmask(SIG_SETMASK, opt_set, opt_out_oldset); -} - /** * Sets machine context. * @@ -37,14 +34,31 @@ static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) { * @see getcontext() */ int setcontext(const ucontext_t *uc) { - if (__contextmask(&uc->uc_sigmask, 0)) return -1; + if (IsWindows()) { + atomic_store_explicit(&__get_tls()->tib_sigmask, uc->uc_sigmask, + memory_order_release); + } else { + sys_sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0); + } return __tailcontext(uc); } int __getcontextsig(ucontext_t *uc) { - return __contextmask(0, &uc->uc_sigmask); + if (IsWindows()) { + uc->uc_sigmask = + atomic_load_explicit(&__get_tls()->tib_sigmask, memory_order_acquire); + return 0; + } else { + return sys_sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask); + } } int __swapcontextsig(ucontext_t *x, const ucontext_t *y) { - return __contextmask(&y->uc_sigmask, &x->uc_sigmask); + if (IsWindows()) { + x->uc_sigmask = atomic_exchange_explicit( + &__get_tls()->tib_sigmask, y->uc_sigmask, memory_order_acquire); + return 0; + } else { + return sys_sigprocmask(SIG_SETMASK, &y->uc_sigmask, &x->uc_sigmask); + } } diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index edec8bba3d5..a2941b71551 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -73,7 +73,8 @@ imaxdiv_t imaxdiv(intmax_t, intmax_t) pureconst; #endif #if (__GNUC__ * 100 + __GNUC_MINOR__ >= 406 || defined(__llvm__)) && \ - !defined(__STRICT_ANSI__) && defined(_COSMO_SOURCE) + !defined(__STRICT_ANSI__) && defined(_COSMO_SOURCE) && \ + !defined(__COSMOCC__) int128_t i128abs(int128_t) libcesque pureconst; int128_t strtoi128(const char *, char **, int) paramsnonnull((1)); diff --git a/libc/intrin/createfile.c b/libc/intrin/createfile.c index 183477e45c6..2aab825be18 100644 --- a/libc/intrin/createfile.c +++ b/libc/intrin/createfile.c @@ -71,7 +71,7 @@ CreateFile(const char16_t *lpFileName, // switch (__imp_GetLastError()) { case kNtErrorPipeBusy: if (micros >= 1024) __imp_Sleep(micros / 1024); - if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1; + if (micros < 1024 * 1024) micros <<= 1; goto TryAgain; case kNtErrorAccessDenied: // GetNtOpenFlags() always greedily requests execute permissions diff --git a/libc/intrin/createnamedpipe.c b/libc/intrin/createnamedpipe.c index aec9b2e03ec..2f23361fd3b 100644 --- a/libc/intrin/createnamedpipe.c +++ b/libc/intrin/createnamedpipe.c @@ -50,7 +50,7 @@ textwindows int64_t CreateNamedPipe( nDefaultTimeOutMs, opt_lpSecurityAttributes); if (hServer == -1 && __imp_GetLastError() == kNtErrorPipeBusy) { if (micros >= 1024) __imp_Sleep(micros / 1024); - if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1; + if (micros < 1024 * 1024) micros <<= 1; goto TryAgain; } if (hServer == -1) __winerr(); diff --git a/libc/intrin/pthread_unref.c b/libc/intrin/pthread_unref.c index 20c40740d61..f67d157ba84 100644 --- a/libc/intrin/pthread_unref.c +++ b/libc/intrin/pthread_unref.c @@ -23,9 +23,7 @@ static bool _pthread_deref(struct PosixThread *pt) { int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire); - if (!refs) return true; - unassert(refs > 0); - return !atomic_fetch_sub(&pt->pt_refs, 1); + return !refs || !atomic_fetch_sub(&pt->pt_refs, 1); } void _pthread_unref(struct PosixThread *pt) { diff --git a/libc/intrin/sigprocmask-nt.c b/libc/intrin/sigprocmask-nt.c index 781589726d0..5acc34692cd 100644 --- a/libc/intrin/sigprocmask-nt.c +++ b/libc/intrin/sigprocmask-nt.c @@ -42,7 +42,7 @@ textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) { if (how == SIG_BLOCK) { oldmask = atomic_fetch_or_explicit(mask, *neu, memory_order_acq_rel); } else if (how == SIG_UNBLOCK) { - oldmask = atomic_fetch_and_explicit(mask, *neu, memory_order_acq_rel); + oldmask = atomic_fetch_and_explicit(mask, ~*neu, memory_order_acq_rel); } else { // SIG_SETMASK oldmask = atomic_exchange_explicit(mask, *neu, memory_order_acq_rel); } diff --git a/libc/intrin/wsarecv.c b/libc/intrin/wsarecv.c index 89cbf35bf73..a35e7f7ccda 100644 --- a/libc/intrin/wsarecv.c +++ b/libc/intrin/wsarecv.c @@ -30,9 +30,7 @@ __msabi extern typeof(WSARecv) *const __imp_WSARecv; /** * Receives data from Windows socket. - * * @return 0 on success, or -1 on failure - * @note this wrapper takes care of ABI, STRACE(), and __winerr() */ textwindows int WSARecv( uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount, @@ -57,9 +55,6 @@ textwindows int WSARecv( if (opt_out_lpNumberOfBytesRecvd) { *opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd; } - if (rc == -1) { - __winsockerr(); - } if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) { kprintf(STRACE_PROLOGUE "WSARecv(%lu, [", s); DescribeIovNt(inout_lpBuffers, dwBufferCount, @@ -73,9 +68,6 @@ textwindows int WSARecv( rc = __imp_WSARecv(s, inout_lpBuffers, dwBufferCount, opt_out_lpNumberOfBytesRecvd, inout_lpFlags, opt_inout_lpOverlapped, opt_lpCompletionRoutine); - if (rc == -1) { - __winsockerr(); - } #endif return rc; } diff --git a/libc/intrin/wsarecvfrom.c b/libc/intrin/wsarecvfrom.c index 74d881dce48..49f70e736e0 100644 --- a/libc/intrin/wsarecvfrom.c +++ b/libc/intrin/wsarecvfrom.c @@ -29,9 +29,7 @@ __msabi extern typeof(WSARecvFrom) *const __imp_WSARecvFrom; /** * Receives data from Windows socket. - * * @return 0 on success, or -1 on failure - * @note this wrapper takes care of ABI, STRACE(), and __winerr() */ textwindows int WSARecvFrom( uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount, @@ -52,9 +50,6 @@ textwindows int WSARecvFrom( if (opt_out_lpNumberOfBytesRecvd) { *opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd; } - if (rc == -1) { - __winerr(); - } if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) { kprintf(STRACE_PROLOGUE "WSARecvFrom(%lu, [", s); DescribeIovNt(inout_lpBuffers, dwBufferCount, @@ -69,9 +64,6 @@ textwindows int WSARecvFrom( opt_out_lpNumberOfBytesRecvd, inout_lpFlags, opt_out_fromsockaddr, opt_inout_fromsockaddrlen, opt_inout_lpOverlapped, opt_lpCompletionRoutine); - if (rc == -1) { - __winerr(); - } #endif return rc; } diff --git a/libc/proc/execve-nt.greg.c b/libc/proc/execve-nt.greg.c index 9894b6484ee..a8799cda65b 100644 --- a/libc/proc/execve-nt.greg.c +++ b/libc/proc/execve-nt.greg.c @@ -22,6 +22,7 @@ #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/kprintf.h" #include "libc/mem/mem.h" #include "libc/nt/enum/processaccess.h" @@ -46,7 +47,7 @@ textwindows int sys_execve_nt(const char *program, char *const argv[], char *const envp[]) { // execve() needs to be @asyncsignalsafe - sigset_t m = __sig_block(); + sigset_t sigmask = __sig_block(); _pthread_lock(); // new process should be a child of our parent @@ -55,10 +56,14 @@ textwindows int sys_execve_nt(const char *program, char *const argv[], if (!(hParentProcess = OpenProcess( kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) { _pthread_unlock(); - __sig_unblock(m); + __sig_unblock(sigmask); return -1; } + // inherit signal mask + char maskvar[6 + 21]; + FormatUint64(stpcpy(maskvar, "_MASK="), sigmask); + // define stdio handles for the spawned subprocess struct NtStartupInfo si = { .cb = sizeof(struct NtStartupInfo), @@ -80,21 +85,21 @@ textwindows int sys_execve_nt(const char *program, char *const argv[], &lpExplicitHandles, &dwExplicitHandleCount))) { CloseHandle(hParentProcess); _pthread_unlock(); - __sig_unblock(m); + __sig_unblock(sigmask); return -1; } // launch the process struct NtProcessInformation pi; - int rc = ntspawn(AT_FDCWD, program, argv, envp, (char *[]){fdspec, 0}, 0, 0, - hParentProcess, lpExplicitHandles, dwExplicitHandleCount, - &si, &pi); + int rc = ntspawn(AT_FDCWD, program, argv, envp, + (char *[]){fdspec, maskvar, 0}, 0, 0, hParentProcess, + lpExplicitHandles, dwExplicitHandleCount, &si, &pi); __undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount); if (rc == -1) { free(fdspec); CloseHandle(hParentProcess); _pthread_unlock(); - __sig_unblock(m); + __sig_unblock(sigmask); if (GetLastError() == kNtErrorSharingViolation) { return etxtbsy(); } else { diff --git a/libc/proc/fork-nt.c b/libc/proc/fork-nt.c index 529004c8964..1be406dd1f4 100644 --- a/libc/proc/fork-nt.c +++ b/libc/proc/fork-nt.c @@ -401,7 +401,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { } } if (rc == -1) { - __proc_free(proc); + dll_make_first(&__proc.free, &proc->elem); } ftrace_enabled(+1); strace_enabled(+1); diff --git a/libc/proc/posix_spawn.c b/libc/proc/posix_spawn.c index bd9466daeeb..f2327a34003 100644 --- a/libc/proc/posix_spawn.c +++ b/libc/proc/posix_spawn.c @@ -248,9 +248,9 @@ static textwindows errno_t posix_spawn_nt_impl( struct SpawnFds fds = {0}; int64_t dirhand = AT_FDCWD; int64_t *lpExplicitHandles = 0; + sigset_t sigmask = __sig_block(); uint32_t dwExplicitHandleCount = 0; int64_t hCreatorProcess = GetCurrentProcess(); - sigset_t m = __sig_block(); // reserve process tracking object __proc_lock(); @@ -266,11 +266,11 @@ static textwindows errno_t posix_spawn_nt_impl( free(fdspec); if (proc) { __proc_lock(); - __proc_free(proc); + dll_make_first(&__proc.free, &proc->elem); __proc_unlock(); } spawnfds_destroy(&fds); - __sig_unblock(m); + __sig_unblock(sigmask); errno = e; return err; } @@ -360,13 +360,17 @@ static textwindows errno_t posix_spawn_nt_impl( } } + // inherit signal mask + char maskvar[6 + 21]; + FormatUint64(stpcpy(maskvar, "_MASK="), sigmask); + // launch process int rc = -1; struct NtProcessInformation procinfo; if (!envp) envp = environ; if ((fdspec = __describe_fds(fds.p, fds.n, &startinfo, hCreatorProcess, &lpExplicitHandles, &dwExplicitHandleCount))) { - rc = ntspawn(dirhand, path, argv, envp, (char *[]){fdspec, 0}, + rc = ntspawn(dirhand, path, argv, envp, (char *[]){fdspec, maskvar, 0}, dwCreationFlags, lpCurrentDirectory, 0, lpExplicitHandles, dwExplicitHandleCount, &startinfo, &procinfo); } diff --git a/libc/proc/posix_spawnattr_setflags.c b/libc/proc/posix_spawnattr_setflags.c index 9521bb9b2cd..cccc017f6bc 100644 --- a/libc/proc/posix_spawnattr_setflags.c +++ b/libc/proc/posix_spawnattr_setflags.c @@ -32,15 +32,20 @@ * - `POSIX_SPAWN_SETSCHEDPARAM` * - `POSIX_SPAWN_SETSCHEDULER` * - `POSIX_SPAWN_SETSID` + * - `POSIX_SPAWN_SETRLIMIT` * @return 0 on success, or errno on error * @raise EINVAL if `flags` has invalid bits */ int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { - if (flags & - ~(POSIX_SPAWN_USEVFORK | POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | - POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK | - POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | - POSIX_SPAWN_SETSID | POSIX_SPAWN_SETRLIMIT)) { + if (flags & ~(POSIX_SPAWN_USEVFORK | // + POSIX_SPAWN_RESETIDS | // + POSIX_SPAWN_SETPGROUP | // + POSIX_SPAWN_SETSIGDEF | // + POSIX_SPAWN_SETSIGMASK | // + POSIX_SPAWN_SETSCHEDPARAM | // + POSIX_SPAWN_SETSCHEDULER | // + POSIX_SPAWN_SETSID | // + POSIX_SPAWN_SETRLIMIT)) { return EINVAL; } (*attr)->flags = flags; diff --git a/libc/proc/posix_spawnattr_setpgroup.c b/libc/proc/posix_spawnattr_setpgroup.c index 2d6263d611b..9e1d53d8534 100644 --- a/libc/proc/posix_spawnattr_setpgroup.c +++ b/libc/proc/posix_spawnattr_setpgroup.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/proc/posix_spawn.h" #include "libc/proc/posix_spawn.internal.h" /** @@ -25,14 +24,14 @@ * 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. + * You also need to pass `POSIX_SPAWN_SETPGROUP` to + * posix_spawnattr_setflags() for it to take effect. * * @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; } diff --git a/libc/proc/posix_spawnattr_setrlimit.c b/libc/proc/posix_spawnattr_setrlimit.c index 90be67c431d..7c7202edbdc 100644 --- a/libc/proc/posix_spawnattr_setrlimit.c +++ b/libc/proc/posix_spawnattr_setrlimit.c @@ -26,7 +26,8 @@ /** * Sets resource limit on spawned process. * - * Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`. + * You also need to pass `POSIX_SPAWN_SETRLIMIT` to + * posix_spawnattr_setflags() for it to take effect. * * @return 0 on success, or errno on error * @raise EINVAL if resource is invalid @@ -34,7 +35,6 @@ int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource, const struct rlimit *rlim) { if (0 <= resource && resource < MIN(RLIM_NLIMITS, ARRAYLEN((*attr)->rlim))) { - (*attr)->flags |= POSIX_SPAWN_SETRLIMIT; (*attr)->rlimset |= 1u << resource; (*attr)->rlim[resource] = *rlim; return 0; diff --git a/libc/proc/posix_spawnattr_setschedparam.c b/libc/proc/posix_spawnattr_setschedparam.c index e73fdce0155..b7628d49d21 100644 --- a/libc/proc/posix_spawnattr_setschedparam.c +++ b/libc/proc/posix_spawnattr_setschedparam.c @@ -16,14 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/sched_param.h" -#include "libc/proc/posix_spawn.h" #include "libc/proc/posix_spawn.internal.h" /** * Specifies scheduler parameter override for spawned process. * - * Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`. + * You also need to pass `POSIX_SPAWN_SETSCHEDPARAM` to + * posix_spawnattr_setflags() for it to take effect. * * @param attr was initialized by posix_spawnattr_init() * @param schedparam receives the result @@ -31,7 +30,6 @@ */ int posix_spawnattr_setschedparam(posix_spawnattr_t *attr, const struct sched_param *schedparam) { - (*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM; (*attr)->schedparam = *schedparam; return 0; } diff --git a/libc/proc/posix_spawnattr_setschedpolicy.c b/libc/proc/posix_spawnattr_setschedpolicy.c index dd1ed863cf7..e52c9ca93fe 100644 --- a/libc/proc/posix_spawnattr_setschedpolicy.c +++ b/libc/proc/posix_spawnattr_setschedpolicy.c @@ -16,19 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/proc/posix_spawn.h" #include "libc/proc/posix_spawn.internal.h" /** * Specifies scheduler policy override for spawned process. * - * Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`. + * You also need to pass `POSIX_SPAWN_SETSCHEDULER` to + * posix_spawnattr_setflags() for it to take effect. * * @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; } diff --git a/libc/proc/posix_spawnattr_setsigdefault.c b/libc/proc/posix_spawnattr_setsigdefault.c index 7f277d83997..7c55e6494b4 100644 --- a/libc/proc/posix_spawnattr_setsigdefault.c +++ b/libc/proc/posix_spawnattr_setsigdefault.c @@ -16,8 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/sigset.h" -#include "libc/proc/posix_spawn.h" #include "libc/proc/posix_spawn.internal.h" /** @@ -32,13 +30,13 @@ * 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`. + * You also need to pass `POSIX_SPAWN_SETSIGDEF` to + * posix_spawnattr_setflags() for it to take effect. * * @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; } diff --git a/libc/proc/posix_spawnattr_setsigmask.c b/libc/proc/posix_spawnattr_setsigmask.c index 842fcd8f177..a882f79a0cf 100644 --- a/libc/proc/posix_spawnattr_setsigmask.c +++ b/libc/proc/posix_spawnattr_setsigmask.c @@ -16,20 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/sigset.h" -#include "libc/proc/posix_spawn.h" #include "libc/proc/posix_spawn.internal.h" /** * Specifies signal mask for sigprocmask() in child process. * - * This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag. + * You also need to pass `POSIX_SPAWN_SETSIGMASK` to + * posix_spawnattr_setflags() for it to take effect. * * @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; return 0; } diff --git a/libc/proc/proc.c b/libc/proc/proc.c index 4c3cbef1e31..10a8a80ee53 100644 --- a/libc/proc/proc.c +++ b/libc/proc/proc.c @@ -16,7 +16,6 @@ │ 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/internal.h" #include "libc/calls/sig.internal.h" @@ -37,6 +36,7 @@ #include "libc/nt/enum/processcreationflags.h" #include "libc/nt/enum/status.h" #include "libc/nt/enum/wait.h" +#include "libc/nt/events.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/filetime.h" @@ -59,95 +59,167 @@ struct Procs __proc; -static textwindows void GetProcessStats(int64_t h, struct rusage *ru) { +static textwindows void __proc_stats(int64_t h, struct rusage *ru) { bzero(ru, sizeof(*ru)); struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)}; - unassert(GetProcessMemoryInfo(h, &memcount, sizeof(memcount))); + GetProcessMemoryInfo(h, &memcount, sizeof(memcount)); ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024; ru->ru_majflt = memcount.PageFaultCount; struct NtFileTime createtime, exittime; struct NtFileTime kerneltime, usertime; - unassert(GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime)); + GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime); ru->ru_utime = WindowsDurationToTimeVal(ReadFileTime(usertime)); ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime)); struct NtIoCounters iocount; - unassert(GetProcessIoCounters(h, &iocount)); + GetProcessIoCounters(h, &iocount); ru->ru_inblock = iocount.ReadOperationCount; ru->ru_oublock = iocount.WriteOperationCount; } +// performs accounting on exited process +// multiple threads can wait on a process +// it's important that only one calls this +textwindows int __proc_harvest(struct Proc *pr, bool iswait4) { + int sic = 0; + uint32_t status; + struct rusage ru; + GetExitCodeProcess(pr->handle, &status); + if (status == kNtStillActive) return 0; + __proc_stats(pr->handle, &ru); + rusage_add(&pr->ru, &ru); + rusage_add(&__proc.ruchlds, &ru); + if ((status & 0xFF000000u) == 0x23000000u) { + // handle child execve() + CloseHandle(pr->handle); + pr->handle = status & 0x00FFFFFF; + } else { + // handle child _Exit() + if (status == 0xc9af3d51u) { + status = kNtStillActive; + } + pr->wstatus = status; + if (!iswait4 && !pr->waiters && !__proc.waiters && + (__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN || + (__sighandflags[SIGCHLD] & SA_NOCLDWAIT))) { + // perform automatic zombie reaping + dll_remove(&__proc.list, &pr->elem); + dll_make_first(&__proc.free, &pr->elem); + CloseHandle(pr->handle); + } else { + // transitions process to zombie state + // wait4 is responsible for reaping it + pr->status = PROC_ZOMBIE; + dll_remove(&__proc.list, &pr->elem); + dll_make_first(&__proc.zombies, &pr->elem); + SetEvent(__proc.haszombies); + if (!pr->waiters && !__proc.waiters) { + if (WIFSIGNALED(status)) { + sic = CLD_KILLED; + } else { + sic = CLD_EXITED; + } + } + } + } + return sic; +} + static textwindows dontinstrument uint32_t __proc_worker(void *arg) { - __bootstrap_tls(&__proc.tls, __builtin_frame_address(0)); + struct CosmoTib tls; + __bootstrap_tls(&tls, __builtin_frame_address(0)); for (;;) { // assemble a group of processes to wait on. if more than 64 // children exist, then we'll use a small timeout and select // processes with a shifting window via a double linked list - struct rusage ru; + // if fewer than 64 processes exist then we'll also wait for + // process birth notifications, and wait on them immediately int64_t handles[64]; - int sic, dosignal = 0; - struct Proc *pr, *objects[64]; + struct Proc *objects[64]; + uint32_t millis, i, n = 0; struct Dll *e, *e2, *samples = 0; - uint32_t millis, status, i, n = 1; __proc_lock(); - handles[0] = __proc.onstart; - for (e = dll_first(__proc.list); e && n < 64; ++n, e = e2) { - pr = PROC_CONTAINER(e); + for (e = dll_first(__proc.list); e && n < 64; e = e2) { + struct Proc *pr = PROC_CONTAINER(e); e2 = dll_next(__proc.list, e); + // cycle process to end of list dll_remove(&__proc.list, e); dll_make_last(&samples, e); - handles[n] = pr->handle; - objects[n] = pr; + // don't bother waiting if it's already awaited + if (!pr->waiters) { + handles[n] = pr->handle; + objects[n] = pr; + ++pr->waiters; + ++n; + } } dll_make_last(&__proc.list, samples); __proc_unlock(); - // wait for win32 to report any status change - millis = n == 64 ? 20 : -1u; - unassert((i = WaitForMultipleObjects(n, handles, false, millis)) != -1u); - i &= ~kNtWaitAbandoned; - if (!i || i == kNtWaitTimeout) continue; - GetExitCodeProcess(handles[i], &status); - if (status == kNtStillActive) continue; - GetProcessStats(handles[i], &ru); - - // update data structures and notify folks - __proc_lock(); - pr = objects[i]; - rusage_add(&pr->ru, &ru); - rusage_add(&__proc.ruchlds, &ru); - if ((status & 0xFF000000u) == 0x23000000u) { - // handle child execve() - CloseHandle(pr->handle); - pr->handle = status & 0x00FFFFFF; + // wait for something to happen + if (n == 64) { + millis = 5; } else { - // handle child _exit() - CloseHandle(pr->handle); - if (status == 0xc9af3d51u) { - status = kNtStillActive; + millis = -1u; + handles[n++] = __proc.onbirth; + } + i = WaitForMultipleObjects(n, handles, false, millis); + if (i == -1u) { + STRACE("proc wait panic %d", GetLastError()); + _Exit(157); + } + if (i & kNtWaitAbandoned) { + i &= ~kNtWaitAbandoned; + STRACE("proc %u handle %ld abandoned", i, handles[i]); + } + __proc_lock(); + + // release our waiter status + for (int j = 0; j < n; ++j) { + if (handles[j] == __proc.onbirth) continue; + if (j == i) continue; + if (!--objects[j]->waiters && objects[j]->status == PROC_UNDEAD) { + __proc_free(objects[j]); } - pr->wstatus = status; - if ((__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN || - (__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) && - (!pr->waiters && !__proc.waiters)) { - dll_remove(&__proc.list, &pr->elem); - dll_make_first(&__proc.free, &pr->elem); - } else { - pr->iszombie = 1; - dll_remove(&__proc.list, &pr->elem); - dll_make_first(&__proc.zombies, &pr->elem); - if (pr->waiters) { - nsync_cv_broadcast(&pr->onexit); - } else if (__proc.waiters) { - nsync_cv_signal(&__proc.onexit); - } else { - dosignal = 1; - sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED; + } + + // check if we need to churn due to >64 processes + if (i == kNtWaitTimeout) { + __proc_unlock(); + continue; + } + + // churn on new process birth + if (handles[i] == __proc.onbirth) { + __proc_unlock(); + continue; + } + + // handle process status change + int sic = 0; + --objects[i]->waiters; + switch (objects[i]->status) { + case PROC_ALIVE: + sic = __proc_harvest(objects[i], false); + break; + case PROC_ZOMBIE: + break; + case PROC_UNDEAD: + if (!objects[i]->waiters) { + __proc_free(objects[i]); } - } + break; + default: + __builtin_unreachable(); } + __proc_unlock(); - if (dosignal) { + + // don't raise SIGCHLD if + // 1. wait4() is being used + // 2. SIGCHLD has SIG_IGN handler + // 3. SIGCHLD has SA_NOCLDWAIT flag + if (sic) { __sig_generate(SIGCHLD, sic); } } @@ -158,7 +230,8 @@ static textwindows dontinstrument uint32_t __proc_worker(void *arg) { * Lazy initializes process tracker data structures and worker. */ static textwindows void __proc_setup(void) { - __proc.onstart = CreateSemaphore(0, 0, 1, 0); + __proc.onbirth = CreateEvent(0, 0, 0, 0); // auto reset + __proc.haszombies = CreateEvent(0, 1, 0, 0); // manual reset __proc.thread = CreateThread(0, 65536, __proc_worker, 0, kNtStackSizeParamIsAReservation, 0); } @@ -191,7 +264,7 @@ textwindows void __proc_wipe(void) { * The returned memory is not tracked by any list. It must be filled in * with system process information and then added back to the system by * calling __proc_add(). If process creation fails, then it needs to be - * released using __proc_free(). + * added back to the __proc.free list by caller. */ textwindows struct Proc *__proc_new(void) { struct Dll *e; @@ -230,16 +303,13 @@ IGNORE_LEAKS(__proc_new) */ textwindows void __proc_add(struct Proc *proc) { dll_make_first(&__proc.list, &proc->elem); - ReleaseSemaphore(__proc.onstart, 1, 0); + SetEvent(__proc.onbirth); } -/** - * Frees process allocation. - * - * Process must not be currently tracked in the active or zombies list. - */ -textwindows void __proc_free(struct Proc *proc) { - dll_make_first(&__proc.free, &proc->elem); +textwindows void __proc_free(struct Proc *pr) { + dll_remove(&__proc.undead, &pr->elem); + dll_make_first(&__proc.free, &pr->elem); + CloseHandle(pr->handle); } // returns owned handle of direct child process diff --git a/libc/proc/proc.internal.h b/libc/proc/proc.internal.h index c9ae58507da..1d9417d10a2 100644 --- a/libc/proc/proc.internal.h +++ b/libc/proc/proc.internal.h @@ -2,25 +2,27 @@ #define COSMOPOLITAN_LIBC_PROC_H_ #include "libc/atomic.h" #include "libc/calls/struct/rusage.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "third_party/nsync/cv.h" #include "third_party/nsync/mu.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#define PROC_ALIVE 0 +#define PROC_ZOMBIE 1 +#define PROC_UNDEAD 2 + #define PROC_CONTAINER(e) DLL_CONTAINER(struct Proc, elem, e) struct Proc { int pid; + int status; int waiters; - bool iszombie; bool wasforked; uint32_t wstatus; int64_t handle; struct Dll elem; - nsync_cv onexit; struct rusage ru; }; @@ -28,15 +30,15 @@ struct Procs { int waiters; atomic_uint once; nsync_mu lock; - nsync_cv onexit; intptr_t thread; - intptr_t onstart; + intptr_t onbirth; + intptr_t haszombies; struct Dll *list; struct Dll *free; + struct Dll *undead; struct Dll *zombies; struct Proc pool[8]; unsigned allocated; - struct CosmoTib tls; struct rusage ruchlds; }; @@ -50,6 +52,7 @@ int64_t __proc_search(int); struct Proc *__proc_new(void); void __proc_add(struct Proc *); void __proc_free(struct Proc *); +int __proc_harvest(struct Proc *, bool); int sys_wait4_nt(int, int *, int, struct rusage *); COSMOPOLITAN_C_END_ diff --git a/libc/proc/proc.mk b/libc/proc/proc.mk index 420ac911f5d..b8d7ef9265c 100644 --- a/libc/proc/proc.mk +++ b/libc/proc/proc.mk @@ -35,8 +35,7 @@ LIBC_PROC_A_DIRECTDEPS = \ LIBC_STR \ LIBC_SYSV \ LIBC_SYSV_CALLS \ - THIRD_PARTY_NSYNC \ - THIRD_PARTY_NSYNC_MEM + THIRD_PARTY_NSYNC LIBC_PROC_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x)))) diff --git a/libc/proc/verynice.c b/libc/proc/verynice.c index 602c3c65bb1..75a9832e260 100644 --- a/libc/proc/verynice.c +++ b/libc/proc/verynice.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/sched_param.h" #include "libc/errno.h" +#include "libc/limits.h" #include "libc/sysv/consts/ioprio.h" #include "libc/sysv/consts/prio.h" #include "libc/sysv/consts/sched.h" @@ -31,7 +32,7 @@ */ int verynice(void) { int e = errno; - setpriority(PRIO_PROCESS, 0, 10); + setpriority(PRIO_PROCESS, 0, NZERO); sys_ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)); struct sched_param param = {sched_get_priority_min(SCHED_IDLE)}; diff --git a/libc/proc/wait4-nt.c b/libc/proc/wait4-nt.c index f5459712311..07fc4252615 100644 --- a/libc/proc/wait4-nt.c +++ b/libc/proc/wait4-nt.c @@ -16,139 +16,191 @@ │ 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/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/calls/struct/rusage.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" -#include "libc/calls/struct/timespec.h" -#include "libc/cosmo.h" -#include "libc/errno.h" -#include "libc/fmt/wintime.internal.h" -#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" #include "libc/intrin/strace.internal.h" -#include "libc/macros.internal.h" -#include "libc/nt/accounting.h" -#include "libc/nt/process.h" +#include "libc/nt/enum/wait.h" +#include "libc/nt/events.h" #include "libc/nt/runtime.h" -#include "libc/nt/struct/filetime.h" -#include "libc/nt/struct/processmemorycounters.h" +#include "libc/nt/synchronization.h" #include "libc/proc/proc.internal.h" -#include "libc/str/str.h" +#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" #ifdef __x86_64__ -static textwindows struct timespec GetNextDeadline(struct timespec deadline) { - if (timespec_iszero(deadline)) deadline = timespec_real(); - struct timespec delay = timespec_frommillis(__SIG_PROC_INTERVAL_MS); - return timespec_add(deadline, delay); -} - -static textwindows int ReapZombie(struct Proc *pr, int *wstatus, - struct rusage *opt_out_rusage) { +static textwindows int __proc_reap(struct Proc *pr, int *wstatus, + struct rusage *opt_out_rusage) { if (wstatus) { *wstatus = pr->wstatus; } if (opt_out_rusage) { *opt_out_rusage = pr->ru; } - if (!pr->waiters) { - dll_remove(&__proc.zombies, &pr->elem); + dll_remove(&__proc.zombies, &pr->elem); + if (dll_is_empty(__proc.zombies)) { + ResetEvent(__proc.haszombies); + } + if (pr->waiters) { + pr->status = PROC_UNDEAD; + dll_make_first(&__proc.undead, &pr->elem); + } else { dll_make_first(&__proc.free, &pr->elem); + CloseHandle(pr->handle); } return pr->pid; } -static textwindows int CheckZombies(int pid, int *wstatus, +static textwindows int __proc_check(int pid, int *wstatus, struct rusage *opt_out_rusage) { struct Dll *e; for (e = dll_first(__proc.zombies); e; e = dll_next(__proc.zombies, e)) { struct Proc *pr = PROC_CONTAINER(e); - if (pid == -1 && pr->waiters) { - continue; // this zombie has been claimed - } if (pid == -1 || pid == pr->pid) { - return ReapZombie(pr, wstatus, opt_out_rusage); + return __proc_reap(pr, wstatus, opt_out_rusage); } } return 0; } -static textwindows void UnwindWaiterCount(void *arg) { - int *waiters = arg; - --*waiters; -} +static textwindows int __proc_wait(int pid, int *wstatus, int options, + struct rusage *rusage, sigset_t waitmask) { + for (;;) { -static textwindows int WaitForProcess(int pid, int *wstatus, int options, - struct rusage *rusage, - uint64_t waitmask) { - uint64_t m; - int rc, *wv; - nsync_cv *cv; - struct Dll *e; - struct Proc *pr; - struct timespec deadline = timespec_zero; + // check for signals and cancelation + int sig, handler_was_called; + if (_check_cancel() == -1) { + return -1; + } + if ((sig = __sig_get(waitmask))) { + handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); + if (_check_cancel() == -1) { + return -1; // ECANCELED because SIGTHR was just handled + } + if (handler_was_called & SIG_HANDLED_NO_RESTART) { + return eintr(); // a non-SA_RESTART handler was called + } + } - // check list of processes that've already exited - if ((rc = CheckZombies(pid, wstatus, rusage))) { - return rc; - } + // check for zombie to harvest + __proc_lock(); + CheckForZombies: + int rc = __proc_check(pid, wstatus, rusage); + if (rc || (options & WNOHANG)) { + __proc_unlock(); + return rc; + } - // find the mark - pr = 0; - if (pid == -1) { + // there's no zombies left + // check if there's any living processes if (dll_is_empty(__proc.list)) { + __proc_unlock(); return echild(); } - cv = &__proc.onexit; - wv = &__proc.waiters; - } else { - for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) { - if (pid == PROC_CONTAINER(e)->pid) { + + // get appropriate wait object + // register ourself as waiting + struct Proc *pr = 0; + uintptr_t hWaitObject; + if (pid == -1) { + // wait for any status change + hWaitObject = __proc.haszombies; + ++__proc.waiters; + } else { + // wait on specific child + for (struct Dll *e = dll_first(__proc.list); e; + e = dll_next(__proc.list, e)) { pr = PROC_CONTAINER(e); + if (pid == pr->pid) break; + } + if (pr) { + // by making the waiter count non-zero, the proc daemon stops + // being obligated to monitor this process. this means we may + // need to assume responsibility later on for zombifying this + ++pr->waiters; + hWaitObject = pr->handle; + } else { + __proc_unlock(); + return echild(); } } - if (pr) { - unassert(!pr->iszombie); - cv = &pr->onexit; - wv = &pr->waiters; - } else { - return echild(); + __proc_unlock(); + + // perform blocking operation + uint32_t wi; + uintptr_t sem; + struct PosixThread *pt = _pthread_self(); + pt->pt_blkmask = waitmask; + pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0); + atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, + memory_order_release); + wi = WaitForMultipleObjects(2, (intptr_t[2]){hWaitObject, sem}, 0, -1u); + atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release); + CloseHandle(sem); + + // log warning if handle unexpectedly closed + if (wi & kNtWaitAbandoned) { + wi &= ~kNtWaitAbandoned; + STRACE("wait4 abandoned %u", wi); } - } - // wait for status change - if (options & WNOHANG) return 0; -WaitMore: - deadline = GetNextDeadline(deadline); -SpuriousWakeup: - ++*wv; - pthread_cleanup_push(UnwindWaiterCount, wv); - m = __sig_begin(waitmask); - if ((rc = _check_signal(true)) != -1) { - rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0); + // check for wait() style wakeup + __proc_lock(); + if (!wi && !pr) { + --__proc.waiters; + goto CheckForZombies; + } + + // check if killed or win32 error + if (wi) { + if (pr && --pr->waiters && pr->status == PROC_UNDEAD) { + __proc_free(pr); + } + __proc_unlock(); + if (wi == 1) { + // __sig_cancel() woke our semaphore + continue; + } else { + // neither posix or win32 define i/o error conditions for + // generic wait. failure should only be due to api misuse + return einval(); + } + } + + // handle process exit notification + --pr->waiters; + if (pr->status == PROC_ALIVE) { + __proc_harvest(pr, true); + } + switch (pr->status) { + case PROC_ALIVE: + // exit caused by execve() reparenting + __proc_unlock(); + break; + case PROC_ZOMBIE: + // exit happened and we're the first to know + rc = __proc_reap(pr, wstatus, rusage); + __proc_unlock(); + return rc; + case PROC_UNDEAD: + // exit happened but another thread waited first + if (!pr->waiters) { + __proc_free(pr); + } + __proc_unlock(); + return echild(); + default: + __builtin_unreachable(); + } } - __sig_finish(m); - pthread_cleanup_pop(true); - if (rc == -1) return -1; - if (rc == ETIMEDOUT) goto WaitMore; - if (rc == ECANCELED) return ecanceled(); - if (pr && pr->iszombie) return ReapZombie(pr, wstatus, rusage); - unassert(!rc); // i have to follow my dreams however crazy they seem - if (!pr && (rc = CheckZombies(pid, wstatus, rusage))) return rc; - goto SpuriousWakeup; } textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, struct rusage *opt_out_rusage) { - int rc; - uint64_t m; // no support for WCONTINUED and WUNTRACED yet if (options & ~WNOHANG) return einval(); // XXX: NT doesn't really have process groups. For instance the @@ -156,12 +208,9 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, // just does an "ignore ctrl-c" internally. if (pid == 0) pid = -1; if (pid < -1) pid = -pid; - m = __sig_block(); - __proc_lock(); - pthread_cleanup_push((void *)__proc_unlock, 0); - rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage, - m | 1ull << (SIGCHLD - 1)); - pthread_cleanup_pop(true); + sigset_t m = __sig_block(); + int rc = __proc_wait(pid, opt_out_wstatus, options, opt_out_rusage, + m | 1ull << (SIGCHLD - 1)); __sig_unblock(m); return rc; } diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index ccc96fb990e..54dafe1537b 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -24,6 +24,7 @@ #include "libc/intrin/asancodes.h" #include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" +#include "libc/intrin/getenv.internal.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" @@ -48,6 +49,18 @@ extern unsigned char __tls_add_nt_rax[]; _Alignas(TLS_ALIGNMENT) static char __static_tls[6016]; +static unsigned long ParseMask(const char *str) { + int c; + unsigned long x = 0; + if (str) { + while ((c = *str++)) { + x *= 10; + x += c - '0'; + } + } + return x; +} + /** * Enables thread local storage for main process. * @@ -210,6 +223,13 @@ textstartup void __enable_tls(void) { atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); // TODO(jart): set_tid_address? + // inherit signal mask + if (IsWindows()) { + atomic_store_explicit(&tib->tib_sigmask, + ParseMask(__getenv(environ, "_MASK").s), + memory_order_relaxed); + } + // initialize posix threads _pthread_static.tib = tib; _pthread_static.pt_flags = PT_STATIC; diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index aaa870d4822..d1aa29cd93b 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -109,7 +109,7 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr, if ((resources.handle = WSASocket(f->family, f->type, f->protocol, 0, 0, kNtWsaFlagOverlapped)) == -1) { client = __winsockerr(); - goto WeFailed; + goto Finish; } // accept network connection @@ -118,7 +118,10 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr, ssize_t bytes_received = __winsock_block( resources.handle, 0, !!(f->flags & O_NONBLOCK), f->rcvtimeo, m, sys_accept_nt_start, &(struct AcceptArgs){f->handle, &buffer}); - if (bytes_received == -1) goto WeFailed; + if (bytes_received == -1) { + __imp_closesocket(resources.handle); + goto Finish; + } // create file descriptor for new socket // don't inherit the file open mode bits @@ -138,7 +141,7 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr, memcpy(addr, &buffer.remote.addr, sizeof(*addr)); g_fds.p[client].kind = kFdSocket; -WeFailed: +Finish: pthread_cleanup_pop(false); __sig_unblock(m); if (client == -1 && errno == ECONNRESET) { diff --git a/libc/sock/recv-nt.c b/libc/sock/recv-nt.c index 8c728255332..8d812d17e7a 100644 --- a/libc/sock/recv-nt.c +++ b/libc/sock/recv-nt.c @@ -25,8 +25,14 @@ #include "libc/sock/internal.h" #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" #ifdef __x86_64__ +#define _MSG_OOB 1 +#define _MSG_PEEK 2 +#define _MSG_WAITALL 8 +#define _MSG_DONTWAIT 64 + struct RecvArgs { const struct iovec *iov; size_t iovlen; @@ -44,11 +50,17 @@ static textwindows int sys_recv_nt_start(int64_t handle, textwindows ssize_t sys_recv_nt(int fd, const struct iovec *iov, size_t iovlen, uint32_t flags) { + if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_PEEK | _MSG_WAITALL)) { + return einval(); + } ssize_t rc; struct Fd *f = g_fds.p + fd; sigset_t m = __sig_block(); - rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->rcvtimeo, - m, sys_recv_nt_start, &(struct RecvArgs){iov, iovlen}); + bool nonblock = !(flags & _MSG_WAITALL) && + ((f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT)); + flags &= ~_MSG_DONTWAIT; + rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m, + sys_recv_nt_start, &(struct RecvArgs){iov, iovlen}); __sig_unblock(m); return rc; } diff --git a/libc/sock/recv.c b/libc/sock/recv.c index 39dd14f2ca8..10dbe7473a1 100644 --- a/libc/sock/recv.c +++ b/libc/sock/recv.c @@ -33,7 +33,7 @@ * @param fd is the file descriptor returned by socket() * @param buf is where received network data gets copied * @param size is the byte capacity of buf - * @param flags can have MSG_{WAITALL,PEEK,OOB}, etc. + * @param flags can have `MSG_OOB`, `MSG_PEEK`, `MSG_DONTWAIT`, `MSG_WAITALL` * @return number of bytes received, 0 on remote close, or -1 w/ errno * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. diff --git a/libc/sock/recvfrom-nt.c b/libc/sock/recvfrom-nt.c index c7170767236..ab2dbd5cd36 100644 --- a/libc/sock/recvfrom-nt.c +++ b/libc/sock/recvfrom-nt.c @@ -24,9 +24,15 @@ #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/syscall_fd.internal.h" +#include "libc/sysv/consts/msg.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" #ifdef __x86_64__ +#define _MSG_OOB 1 +#define _MSG_PEEK 2 +#define _MSG_DONTWAIT 64 + struct RecvFromArgs { const struct iovec *iov; size_t iovlen; @@ -48,11 +54,14 @@ textwindows ssize_t sys_recvfrom_nt(int fd, const struct iovec *iov, size_t iovlen, uint32_t flags, void *opt_out_srcaddr, uint32_t *opt_inout_srcaddrsize) { + if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_PEEK)) return einval(); ssize_t rc; struct Fd *f = g_fds.p + fd; sigset_t m = __sig_block(); - rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->rcvtimeo, - m, sys_recvfrom_nt_start, + bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT); + flags &= ~_MSG_DONTWAIT; + rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m, + sys_recvfrom_nt_start, &(struct RecvFromArgs){iov, iovlen, opt_out_srcaddr, opt_inout_srcaddrsize}); __sig_unblock(m); diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index 2b096c60d42..65587bd648a 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -34,15 +34,15 @@ /** * Receives data from network. * + * This function blocks unless MSG_DONTWAIT is passed. In that case, the + * non-error EWOULDBLOCK might be returned. It basically means we didn't + * wait around to learn an amount of bytes were written that we know in + * advance are guaranteed to be atomic. + * * @param fd is the file descriptor returned by socket() * @param buf is where received network data gets copied * @param size is the byte capacity of buf - * @param flags is a bitmask which may contain any of the following: - * - `MSG_DONTWAIT` to force `O_NONBLOCK` behavior for this call - * - `MSG_OOB` is broadly supported (untested by cosmo) - * - `MSG_PEEK` is broadly supported (untested by cosmo) - * - `MSG_WAITALL` is broadly supported (untested by cosmo) - * - `MSG_DONTROUTE` is broadly supported (untested by cosmo) + * @param flags can have `MSG_OOB`, `MSG_PEEK`, and `MSG_DONTWAIT` * @param opt_out_srcaddr receives the binary ip:port of the data's origin * @param opt_inout_srcaddrsize is srcaddr capacity which gets updated * @return number of bytes received, 0 on remote close, or -1 w/ errno diff --git a/libc/sock/send-nt.c b/libc/sock/send-nt.c index 1fb48c8a3bd..b11f1eba7e5 100644 --- a/libc/sock/send-nt.c +++ b/libc/sock/send-nt.c @@ -25,8 +25,13 @@ #include "libc/sock/internal.h" #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" #ifdef __x86_64__ +#define _MSG_OOB 1 +#define _MSG_DONTROUTE 4 +#define _MSG_DONTWAIT 64 + struct SendArgs { const struct iovec *iov; size_t iovlen; @@ -44,11 +49,14 @@ static textwindows int sys_send_nt_start(int64_t handle, textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen, uint32_t flags) { + if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE)) return einval(); ssize_t rc; struct Fd *f = g_fds.p + fd; sigset_t m = __sig_block(); - rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->sndtimeo, - m, sys_send_nt_start, &(struct SendArgs){iov, iovlen}); + bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT); + flags &= ~_MSG_DONTWAIT; + rc = __winsock_block(f->handle, flags, nonblock, f->sndtimeo, m, + sys_send_nt_start, &(struct SendArgs){iov, iovlen}); __sig_unblock(m); return rc; } diff --git a/libc/sock/send.c b/libc/sock/send.c index 31d871953d0..fbc3b7eebd7 100644 --- a/libc/sock/send.c +++ b/libc/sock/send.c @@ -34,7 +34,7 @@ * @param fd is the file descriptor returned by socket() * @param buf is the data to send, which we'll copy if necessary * @param size is the byte-length of buf - * @param flags MSG_OOB, MSG_DONTROUTE, MSG_PARTIAL, MSG_NOSIGNAL, etc. + * @param flags can have `MSG_OOB`, `MSG_DONTROUTE`, and `MSG_DONTWAIT` * @return number of bytes transmitted, or -1 w/ errno * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. diff --git a/libc/sock/sendto-nt.c b/libc/sock/sendto-nt.c index bd6c2a3c9a9..1d2325616a9 100644 --- a/libc/sock/sendto-nt.c +++ b/libc/sock/sendto-nt.c @@ -25,8 +25,13 @@ #include "libc/sock/internal.h" #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" #ifdef __x86_64__ +#define _MSG_OOB 1 +#define _MSG_DONTROUTE 4 +#define _MSG_DONTWAIT 64 + struct SendToArgs { const struct iovec *iov; size_t iovlen; @@ -47,12 +52,14 @@ static textwindows int sys_sendto_nt_start(int64_t handle, textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov, size_t iovlen, uint32_t flags, void *opt_in_addr, uint32_t in_addrsize) { + if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE)) return einval(); ssize_t rc; struct Fd *f = g_fds.p + fd; sigset_t m = __sig_block(); + bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT); + flags &= ~_MSG_DONTWAIT; rc = __winsock_block( - f->handle, flags, !!(f->flags & O_NONBLOCK), f->sndtimeo, m, - sys_sendto_nt_start, + f->handle, flags, nonblock, f->sndtimeo, m, sys_sendto_nt_start, &(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize}); __sig_unblock(m); return rc; diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index 3206be8b12d..a740c714c95 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -43,7 +43,7 @@ * @param fd is the file descriptor returned by socket() * @param buf is the data to send, which we'll copy if necessary * @param size is the byte-length of buf - * @param flags MSG_OOB, MSG_DONTROUTE, MSG_PARTIAL, MSG_NOSIGNAL, etc. + * @param flags can have `MSG_OOB`, `MSG_DONTROUTE`, and `MSG_DONTWAIT` * @param opt_addr is a binary ip:port destination override, which is * mandatory for UDP if connect() wasn't called * @param addrsize is the byte-length of addr's true polymorphic form diff --git a/libc/sock/winsockblock.c b/libc/sock/winsockblock.c index c519669871b..522d9245e05 100644 --- a/libc/sock/winsockblock.c +++ b/libc/sock/winsockblock.c @@ -16,50 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/calls/struct/fd.internal.h" -#include "libc/calls/struct/sigset.internal.h" +#include "libc/calls/struct/sigset.h" #include "libc/errno.h" -#include "libc/intrin/atomic.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/strace.internal.h" #include "libc/nt/enum/wait.h" #include "libc/nt/errors.h" -#include "libc/nt/runtime.h" -#include "libc/nt/struct/iovec.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/thread.h" #include "libc/nt/winsock.h" -#include "libc/runtime/runtime.h" #include "libc/sock/internal.h" -#include "libc/sock/syscall_fd.internal.h" -#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" #ifdef __x86_64__ -struct WinsockBlockResources { - int64_t handle; - struct NtOverlapped *overlap; -}; - -static void UnwindWinsockBlock(void *arg) { - struct WinsockBlockResources *wbr = arg; - uint32_t got, flags; - CancelIoEx(wbr->handle, wbr->overlap); - WSAGetOverlappedResult(wbr->handle, wbr->overlap, &got, true, &flags); - WSACloseEvent(wbr->overlap->hEvent); -} - -static void CancelWinsockBlock(int64_t handle, struct NtOverlapped *overlap) { - if (!CancelIoEx(handle, overlap)) { - unassert(WSAGetLastError() == kNtErrorNotFound); - } -} - textwindows ssize_t __winsock_block(int64_t handle, uint32_t flags, bool nonblock, uint32_t srwtimeout, sigset_t waitmask, @@ -67,31 +38,20 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock, uint32_t *flags, void *arg), void *arg) { - int rc; - int sig = 0; - uint32_t status; - uint32_t exchanged; - int olderror = errno; - bool eagained = false; - bool canceled = false; - int handler_was_called; - struct PosixThread *pt; - RestartOperation: + int rc, sig, reason = 0; + uint32_t status, exchanged; + if (_check_cancel() == -1) return -1; // ECANCELED + if ((sig = __sig_get(waitmask))) goto HandleInterrupt; + struct NtOverlapped overlap = {.hEvent = WSACreateEvent()}; - struct WinsockBlockResources wbr = {handle, &overlap}; - pthread_cleanup_push(UnwindWinsockBlock, &wbr); rc = StartSocketOp(handle, &overlap, &flags, arg); if (rc && WSAGetLastError() == kNtErrorIoPending) { if (nonblock) { - CancelWinsockBlock(handle, &overlap); - eagained = true; - } else if (_check_cancel()) { - CancelWinsockBlock(handle, &overlap); - canceled = true; - } else if ((sig = __sig_get(waitmask))) { - CancelWinsockBlock(handle, &overlap); + CancelIoEx(handle, &overlap); + reason = EAGAIN; } else { + struct PosixThread *pt; pt = _pthread_self(); pt->pt_blkmask = waitmask; pt->pt_iohandle = handle; @@ -101,10 +61,13 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock, status = WSAWaitForMultipleEvents(1, &overlap.hEvent, 0, srwtimeout ? srwtimeout : -1u, 0); atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release); - if (status == kNtWaitTimeout) { - // SO_RCVTIMEO or SO_SNDTIMEO elapsed - CancelWinsockBlock(handle, &overlap); - eagained = true; + if (status) { + if (status == kNtWaitTimeout) { + reason = EAGAIN; // SO_RCVTIMEO or SO_SNDTIMEO elapsed + } else { + reason = WSAGetLastError(); // ENETDOWN or ENOBUFS + } + CancelIoEx(handle, &overlap); } } rc = 0; @@ -114,30 +77,21 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock, ? 0 : -1; } - pthread_cleanup_pop(false); WSACloseEvent(overlap.hEvent); - if (canceled) { - return ecanceled(); - } - if (sig) { - handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); - if (_check_cancel() == -1) return -1; - } else { - handler_was_called = 0; - } if (!rc) { - errno = olderror; return exchanged; } if (WSAGetLastError() == kNtErrorOperationAborted) { - if (eagained) return eagain(); - if (!handler_was_called && (sig = __sig_get(waitmask))) { - handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); - if (_check_cancel() == -1) return -1; + if (reason) { + errno = reason; + return -1; } - if (handler_was_called != 1) { - goto RestartOperation; + if ((sig = __sig_get(waitmask))) { + HandleInterrupt: + int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask); + if (_check_cancel() == -1) return -1; + if (handler_was_called != 1) goto RestartOperation; } return eintr(); } diff --git a/libc/stdio/printargs.c b/libc/stdio/printargs.c index 476c457a3e5..1565111a202 100644 --- a/libc/stdio/printargs.c +++ b/libc/stdio/printargs.c @@ -309,9 +309,9 @@ textstartup void __printargs(const char *prologue) { PRINT(""); PRINT("SIGNAL MASK %#lx", ss); if (ss) { - for (i = 0; i < 32; ++i) { - if (ss & (1u << i)) { - PRINT(" ☼ %G (%d) is masked", i + 1, i + 1); + for (i = 1; i <= NSIG; ++i) { + if (ss & (1ull << (i - 1))) { + PRINT(" ☼ %G (%d) is masked", i, i); } } } else { diff --git a/libc/str/str.h b/libc/str/str.h index 7891a647a3e..797db1e93d5 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -56,17 +56,14 @@ int iswseparator(wint_t); wint_t towlower(wint_t); wint_t towupper(wint_t); -void bzero(void *, size_t) memcpyesque; void *memset(void *, int, size_t) memcpyesque; void *memmove(void *, const void *, size_t) memcpyesque; void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque; void *mempcpy(void *restrict, const void *restrict, size_t) memcpyesque; char *hexpcpy(char *restrict, const void *restrict, size_t) memcpyesque; void *memccpy(void *restrict, const void *restrict, int, size_t) memcpyesque; -void bcopy(const void *, void *, size_t) memcpyesque; void explicit_bzero(void *, size_t); -int bcmp(const void *, const void *, size_t) strlenesque; int memcmp(const void *, const void *, size_t) strlenesque; int timingsafe_bcmp(const void *, const void *, size_t); int timingsafe_memcmp(const void *, const void *, size_t); @@ -75,7 +72,6 @@ size_t strlen(const char *) strlenesque; size_t strnlen(const char *, size_t) strlenesque; size_t strnlen_s(const char *, size_t); char *strchr(const char *, int) strlenesque; -char *index(const char *, int) strlenesque; void *memchr(const void *, int, size_t) strlenesque; char *strchrnul(const char *, int) strlenesque returnsnonnull; void *rawmemchr(const void *, int) strlenesque returnsnonnull; @@ -220,6 +216,17 @@ bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque; char *__join_paths(char *, size_t, const char *, const char *) __wur; #endif /* _COSMO_SOURCE */ +#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) || \ + defined(_POSIX_SOURCE) || \ + (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE + 0 < 200809L) || \ + (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE + 0 < 700) +int bcmp(const void *, const void *, size_t) strlenesque; +void bcopy(const void *, void *, size_t) memcpyesque; +void bzero(void *, size_t) memcpyesque; +char *index(const char *, int) strlenesque; +char *rindex(const char *, int) strlenesque; +#endif + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STR_STR_H_ */ diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 5fece617d62..f7c436d1ae4 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -115,7 +115,12 @@ static int PosixThread(void *arg, int tid) { } // set long jump handler so pthread_exit can bring control back here if (!setjmp(pt->pt_exiter)) { - pthread_sigmask(SIG_SETMASK, &pt->pt_attr.__sigmask, 0); + if (IsWindows()) { + atomic_store_explicit(&__get_tls()->tib_sigmask, pt->pt_attr.__sigmask, + memory_order_release); + } else { + sys_sigprocmask(SIG_SETMASK, &pt->pt_attr.__sigmask, 0); + } rc = pt->pt_start(pt->pt_arg); // ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup unassert(!pt->pt_cleanup); diff --git a/test/libc/calls/stackoverflow1_test.c b/test/libc/calls/stackoverflow1_test.c index d0205797897..6bf8e0e0b0f 100644 --- a/test/libc/calls/stackoverflow1_test.c +++ b/test/libc/calls/stackoverflow1_test.c @@ -27,6 +27,7 @@ #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/runtime/sysconf.h" +#include "libc/stdio/sysparam.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" @@ -58,8 +59,10 @@ void SetUp(void) { // tune down the main process's stack size to a reasonable amount // some operating systems, e.g. freebsd, will do things like have // 500mb RLIMIT_STACK by default, even on machines with 400mb RAM - struct rlimit rl = {2 * 1024 * 1024, 2 * 1024 * 1024}; if (!IsWindows() && !IsXnu()) { + struct rlimit rl; + getrlimit(RLIMIT_STACK, &rl); + rl.rlim_cur = MIN(rl.rlim_cur, 2 * 1024 * 1024); ASSERT_SYS(0, 0, setrlimit(RLIMIT_STACK, &rl)); } diff --git a/examples/stackoverflow.c b/test/libc/calls/stackoverflow3_test.c similarity index 56% rename from examples/stackoverflow.c rename to test/libc/calls/stackoverflow3_test.c index 4e3ad91fa7a..c5dc6c65761 100644 --- a/examples/stackoverflow.c +++ b/test/libc/calls/stackoverflow3_test.c @@ -1,12 +1,21 @@ -#if 0 -/*─────────────────────────────────────────────────────────────────╗ -│ To the extent possible under law, Justine Tunney has waived │ -│ all copyright and related or neighboring rights to this file, │ -│ as it is written in the following disclaimers: │ -│ • http://unlicense.org/ │ -│ • http://creativecommons.org/publicdomain/zero/1.0/ │ -╚─────────────────────────────────────────────────────────────────*/ -#endif +/*-*- 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/struct/sigaction.h" #include "libc/calls/struct/sigaltstack.h" #include "libc/calls/struct/siginfo.h" @@ -29,10 +38,6 @@ * rewrite thread cpu state to call pthread_exit * this method returns gracefully from signal handlers * unfortunately it relies on cpu architecture knowledge - * - * @see test/libc/thread/stackoverflow1_test.c - * @see test/libc/thread/stackoverflow2_test.c - * @see test/libc/thread/stackoverflow3_test.c */ volatile bool smashed_stack; @@ -47,11 +52,12 @@ void Exiter(void *rc) { void CrashHandler(int sig, siginfo_t *si, void *arg) { ucontext_t *ctx = arg; struct sigaltstack ss; + ASSERT_FALSE(smashed_stack); ASSERT_SYS(0, 0, sigaltstack(0, &ss)); ASSERT_EQ(SS_ONSTACK, ss.ss_flags); kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr); smashed_stack = true; - ASSERT_TRUE(__is_stack_overflow(si, ctx)); + // EXPECT_TRUE(__is_stack_overflow(si, ctx)); // // the backtrace will look like this // @@ -67,7 +73,15 @@ void CrashHandler(int sig, siginfo_t *si, void *arg) { ctx->uc_mcontext.PC = (long)Exiter; ctx->uc_mcontext.SP += 32768; ctx->uc_mcontext.SP &= -16; - ctx->uc_mcontext.SP -= 8; +#ifdef __x86_64__ + *(long *)(ctx->uc_mcontext.SP -= 8) = ctx->uc_mcontext.PC; +#elif defined(__aarch64__) + *(long *)(ctx->uc_mcontext.SP -= 8) = ctx->uc_mcontext.regs[30]; + *(long *)(ctx->uc_mcontext.SP -= 8) = ctx->uc_mcontext.regs[29]; + ctx->uc_mcontext.BP = ctx->uc_mcontext.SP; +#else +#error "unsupported architecture" +#endif } int StackOverflow(int f(), int n) { @@ -96,7 +110,7 @@ void *MyPosixThread(void *arg) { return 0; } -int main(int argc, char *argv[]) { +TEST(stackoverflow, standardStack_altStack_thread_teleport) { void *res; pthread_t th; struct sigaltstack ss; @@ -105,6 +119,7 @@ int main(int argc, char *argv[]) { pthread_join(th, &res); ASSERT_EQ((void *)123L, res); ASSERT_TRUE(smashed_stack); + // this should be SS_DISABLE but ShowCrashReports() creates an alt stack ASSERT_SYS(0, 0, sigaltstack(0, &ss)); - ASSERT_EQ(SS_DISABLE, ss.ss_flags); + ASSERT_EQ(0, ss.ss_flags); } diff --git a/test/libc/calls/stackoverflow4_test.c b/test/libc/calls/stackoverflow4_test.c new file mode 100644 index 00000000000..5cd73988f48 --- /dev/null +++ b/test/libc/calls/stackoverflow4_test.c @@ -0,0 +1,86 @@ +/*-*- 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/struct/sigaction.h" +#include "libc/calls/struct/sigaltstack.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" +#include "libc/intrin/kprintf.h" +#include "libc/limits.h" +#include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/sysconf.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/ss.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +/** + * stack overflow recovery technique #4 + * just call pthread_exit() and let the thread die + */ + +volatile bool smashed_stack; + +void CrashHandler(int sig) { + smashed_stack = true; + pthread_exit((void *)123L); +} + +int StackOverflow(int f(), int n) { + if (n < INT_MAX) { + return f(f, n + 1) - 1; + } else { + return INT_MAX; + } +} + +int (*pStackOverflow)(int (*)(), int) = StackOverflow; + +void *MyPosixThread(void *arg) { + struct sigaction sa; + struct sigaltstack ss; + ss.ss_flags = 0; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096; + ss.ss_sp = gc(malloc(ss.ss_size)); + ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important + sigemptyset(&sa.sa_mask); + sa.sa_handler = CrashHandler; + sigaction(SIGBUS, &sa, 0); + sigaction(SIGSEGV, &sa, 0); + exit(pStackOverflow(pStackOverflow, 0)); + return 0; +} + +TEST(stackoverflow, standardStack_altStack_thread_teleport) { + void *res; + pthread_t th; + struct sigaltstack ss; + smashed_stack = false; + pthread_create(&th, 0, MyPosixThread, 0); + pthread_join(th, &res); + ASSERT_EQ((void *)123L, res); + ASSERT_TRUE(smashed_stack); + // this should be SS_DISABLE but ShowCrashReports() creates an alt stack + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + ASSERT_EQ(0, ss.ss_flags); +} diff --git a/tool/build/compile.c b/tool/build/compile.c index abdf7b3a2f9..0382b32d388 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -637,6 +637,8 @@ int Launch(void) { posix_spawnattr_init(&spawnattr); posix_spawnattr_setsigmask(&spawnattr, &savemask); + posix_spawnattr_setflags(&spawnattr, + POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETRLIMIT); SetCpuLimit(cpuquota); SetFszLimit(fszquota); SetMemLimit(memquota); diff --git a/tool/build/runitd.c b/tool/build/runitd.c index e0e14f12218..0885c4b18e5 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -552,7 +552,8 @@ void *ClientWorker(void *arg) { started = timespec_real(); pipe2(client->pipe, O_CLOEXEC); posix_spawnattr_init(&spawnattr); - posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_SETPGROUP); + posix_spawnattr_setflags(&spawnattr, + POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETPGROUP); posix_spawnattr_setsigmask(&spawnattr, &sigmask); posix_spawn_file_actions_init(&spawnfila); posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);