From 126a44dc4911dc2dedd693df337f1971c4f9aebc Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 21 Sep 2024 21:28:35 -0700 Subject: [PATCH] Write more tests attempting to break windows This time I haven't succeeded in breaking anything which is a good sign. --- libc/calls/readwrite-nt.c | 6 +- libc/calls/sig.c | 8 +- test/posix/pipe_write_eagain_test.c | 107 ++++++++++++++++++ test/posix/sa_resethand_test.c | 96 +++++++++++----- test/posix/signal_latency_async_test.c | 148 +++++++++++++++++++++++++ test/posix/signal_latency_test.c | 1 - tool/net/redbean.c | 2 +- 7 files changed, 331 insertions(+), 37 deletions(-) create mode 100644 test/posix/pipe_write_eagain_test.c create mode 100644 test/posix/signal_latency_async_test.c diff --git a/libc/calls/readwrite-nt.c b/libc/calls/readwrite-nt.c index e1c05922380..1c983ca80bd 100644 --- a/libc/calls/readwrite-nt.c +++ b/libc/calls/readwrite-nt.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/createfileflags.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" @@ -170,14 +169,11 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, } // the i/o operation was successfully canceled - if (got_eagain) { - unassert(!got_sig); + if (got_eagain) return eagain(); - } // it's now reasonable to report semaphore creation error if (other_error) { - unassert(!got_sig); errno = __dos2errno(other_error); return -1; } diff --git a/libc/calls/sig.c b/libc/calls/sig.c index 196de339791..8db280e0ce9 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -302,10 +302,10 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { return 0; } - // we can't preempt threads that masked sig or are blocked. we aso - // need to ensure we don't the target thread's stack if many signals - // need to be delivered at once. we also need to make sure two threads - // can't deadlock by killing each other at the same time. + // we can't preempt threads that masked sigs or are blocked. we also + // need to ensure we don't overflow the target thread's stack if many + // signals need to be delivered at once. we also need to make sure two + // threads can't deadlock by killing each other at the same time. if ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) & (1ull << (sig - 1))) || atomic_exchange_explicit(&pt->pt_intoff, 1, memory_order_acquire)) { diff --git a/test/posix/pipe_write_eagain_test.c b/test/posix/pipe_write_eagain_test.c new file mode 100644 index 00000000000..66a1c42eaa1 --- /dev/null +++ b/test/posix/pipe_write_eagain_test.c @@ -0,0 +1,107 @@ +// Copyright 2024 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 +#include +#include +#include +#include +#include + +/** + * @fileoverview Tests that EAGAIN won't corrupt pipe. + * + * This is a real bug when using CancelIoEx() on winsock writes, so we + * need to make sure it doesn't happen on pipes too. + */ + +#define ITERATIONS 100000 +#define ASYMMETRY 3 + +int fds[2]; +int got_read_eagains; +int got_write_eagains; + +void *worker(void *arg) { + for (int expect = 0; expect < ITERATIONS;) { + int number; + ssize_t rc = read(fds[0], &number, sizeof(number)); + if (rc == -1) { + if (errno == EAGAIN) { + ++got_read_eagains; + if (poll(&(struct pollfd){fds[0], POLLIN}, 1, -1) == -1) + exit(11); + continue; + } + perror("read"); + exit(8); + } + size_t got = rc; + if (got != sizeof(int)) + exit(9); + if (expect != number) + exit(10); + ++expect; + } + return 0; +} + +int main(int argc, char *argv[]) { + + if (pipe2(fds, O_NONBLOCK)) + return 1; + + pthread_t th; + if (pthread_create(&th, 0, worker, 0)) + return 2; + + int number = 0; + for (;;) { + int chunk = 0; + int numbers[ASYMMETRY]; + for (;;) { + numbers[chunk] = number + chunk; + if (++chunk == ASYMMETRY) + break; + if (number + chunk == ITERATIONS) + break; + } + for (;;) { + ssize_t rc = write(fds[1], numbers, chunk * sizeof(int)); + if (rc == -1) { + if (errno == EAGAIN) { + ++got_write_eagains; + if (poll(&(struct pollfd){fds[1], POLLOUT}, 1, -1) == -1) + return 10; + continue; + } + return 3; + } + if (rc % sizeof(int)) + return 4; + chunk = rc / sizeof(int); + number += chunk; + break; + } + if (number == ITERATIONS) + break; + } + + if (pthread_join(th, 0)) + return 5; + + if (!got_read_eagains && !got_write_eagains) + return 7; +} diff --git a/test/posix/sa_resethand_test.c b/test/posix/sa_resethand_test.c index c582d90fe19..eed501bf6ea 100644 --- a/test/posix/sa_resethand_test.c +++ b/test/posix/sa_resethand_test.c @@ -1,22 +1,21 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et 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. │ -╚─────────────────────────────────────────────────────────────────────────────*/ +// Copyright 2024 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 #include +#include volatile int gotsig; @@ -24,23 +23,68 @@ void OnSig(int sig) { gotsig = sig; } -int main() { +void test_sa_resethand_raise(void) { struct sigaction sa; sa.sa_handler = OnSig; sa.sa_flags = SA_RESETHAND; sigemptyset(&sa.sa_mask); if (sigaction(SIGUSR1, &sa, 0)) - return 1; + exit(1); if (sigaction(SIGUSR1, 0, &sa)) - return 2; + exit(2); if (sa.sa_handler != OnSig) - return 3; + exit(3); if (raise(SIGUSR1)) - return 4; + exit(4); if (gotsig != SIGUSR1) - return 5; + exit(5); if (sigaction(SIGUSR1, 0, &sa)) - return 6; + exit(6); + if (sa.sa_handler != SIG_DFL) + exit(7); +} + +void test_sa_resethand_pause(void) { + struct sigaction sa; + sa.sa_handler = OnSig; + sa.sa_flags = SA_RESETHAND; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGALRM, &sa, 0)) + exit(10); + ualarm(10000, 0); + if (pause() != -1 || errno != EINTR) + exit(11); + if (gotsig != SIGALRM) + exit(12); + if (sigaction(SIGALRM, 0, &sa)) + exit(13); if (sa.sa_handler != SIG_DFL) - return 7; + exit(14); +} + +void test_sa_resethand_read(void) { + struct sigaction sa; + sa.sa_handler = OnSig; + sa.sa_flags = SA_RESETHAND; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGALRM, &sa, 0)) + exit(20); + int fds[2]; + if (pipe(fds)) + exit(21); + ualarm(10000, 0); + if (read(fds[0], (char[]){0}, 1) != -1 || errno != EINTR) + exit(22); + if (gotsig != SIGALRM) + exit(23); + if (sigaction(SIGALRM, 0, &sa)) + exit(24); + if (sa.sa_handler != SIG_DFL) + exit(25); +} + +int main() { + test_sa_resethand_raise(); + test_sa_resethand_pause(); + test_sa_resethand_read(); } diff --git a/test/posix/signal_latency_async_test.c b/test/posix/signal_latency_async_test.c new file mode 100644 index 00000000000..438f5214cb2 --- /dev/null +++ b/test/posix/signal_latency_async_test.c @@ -0,0 +1,148 @@ +// Copyright 2024 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 +#include +#include +#include +#include +#include +#include + +#define ITERATIONS 10000 + +pthread_t sender_thread; +pthread_t receiver_thread; +struct timespec send_time; +atomic_int sender_got_signal; +double latencies[ITERATIONS]; + +void sender_signal_handler(int signo) { + sender_got_signal = 1; +} + +void receiver_signal_handler(int signo) { + struct timespec receive_time; + clock_gettime(CLOCK_MONOTONIC, &receive_time); + + long sec_diff = receive_time.tv_sec - send_time.tv_sec; + long nsec_diff = receive_time.tv_nsec - send_time.tv_nsec; + double latency_ns = sec_diff * 1e9 + nsec_diff; + + static int iteration = 0; + if (iteration < ITERATIONS) + latencies[iteration++] = latency_ns; + + // Pong sender + if (pthread_kill(sender_thread, SIGUSR2)) + exit(2); + + // Exit if done + if (iteration >= ITERATIONS) + pthread_exit(0); +} + +void *sender_func(void *arg) { + + for (int i = 0; i < ITERATIONS; i++) { + + // Wait a bit sometimes + if (rand() % 2 == 1) { + volatile unsigned v = 0; + for (;;) + if (++v == 4000) + break; + } + + // Ping receiver + clock_gettime(CLOCK_MONOTONIC, &send_time); + if (pthread_kill(receiver_thread, SIGUSR1)) + exit(6); + + // Wait for pong + for (;;) + if (atomic_load_explicit(&sender_got_signal, memory_order_relaxed)) + break; + sender_got_signal = 0; + } + + return 0; +} + +void *receiver_func(void *arg) { + + // Wait for asynchronous signals + volatile unsigned v = 0; + for (;;) + ++v; + + return 0; +} + +int compare(const void *a, const void *b) { + const double *x = a, *y = b; + if (*x < *y) + return -1; + else if (*x > *y) + return 1; + else + return 0; +} + +int main() { + + // Install signal handlers + struct sigaction sa; + sa.sa_handler = receiver_signal_handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGUSR1, &sa, 0); + sa.sa_handler = sender_signal_handler; + sigaction(SIGUSR2, &sa, 0); + + // Create receiver thread first + if (pthread_create(&receiver_thread, 0, receiver_func, 0)) + exit(11); + + // Create sender thread + if (pthread_create(&sender_thread, 0, sender_func, 0)) + exit(12); + + // Wait for threads to finish + if (pthread_join(sender_thread, 0)) + exit(13); + if (pthread_join(receiver_thread, 0)) + exit(14); + + // Compute mean latency + double total_latency = 0; + for (int i = 0; i < ITERATIONS; i++) + total_latency += latencies[i]; + double mean_latency = total_latency / ITERATIONS; + + // Sort latencies to compute percentiles + qsort(latencies, ITERATIONS, sizeof(double), compare); + + double p50 = latencies[(int)(0.50 * ITERATIONS)]; + double p90 = latencies[(int)(0.90 * ITERATIONS)]; + double p95 = latencies[(int)(0.95 * ITERATIONS)]; + double p99 = latencies[(int)(0.99 * ITERATIONS)]; + + printf("Mean latency: %.2f ns\n", mean_latency); + printf("50th percentile latency: %.2f ns\n", p50); + printf("90th percentile latency: %.2f ns\n", p90); + printf("95th percentile latency: %.2f ns\n", p95); + printf("99th percentile latency: %.2f ns\n", p99); +} diff --git a/test/posix/signal_latency_test.c b/test/posix/signal_latency_test.c index 02929aa8c00..9f599f43809 100644 --- a/test/posix/signal_latency_test.c +++ b/test/posix/signal_latency_test.c @@ -22,7 +22,6 @@ #include #include #include -#include "libc/thread/posixthread.internal.h" #define ITERATIONS 10000 diff --git a/tool/net/redbean.c b/tool/net/redbean.c index eeb2e311646..1000ffc2c56 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -2276,7 +2276,7 @@ static struct Asset *GetAssetZip(const char *path, size_t pathlen) { hash = Hash(path, pathlen); for (step = 0;; ++step) { i = (hash + ((step * (step + 1)) >> 1)) & (assets.n - 1); - if (!assets.p[i].hash) + if (i >= assets.n || !assets.p || !assets.p[i].hash) return NULL; if (hash == assets.p[i].hash && pathlen == ZIP_CFILE_NAMESIZE(zmap + assets.p[i].cf) &&