Write test for stack overflow detection

This commit is contained in:
Justine Tunney 2022-05-23 15:52:26 -07:00
parent 99e67c348b
commit cef50f2a6b
4 changed files with 96 additions and 7 deletions

View file

@ -92,6 +92,7 @@ static void FreeSigAltStack(void *p) {
* @see callexitontermination()
*/
void ShowCrashReports(void) {
char *sp;
struct sigaltstack ss;
/* <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */
@ -105,11 +106,12 @@ void ShowCrashReports(void) {
if (!IsWindows()) {
bzero(&ss, sizeof(ss));
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
ss.ss_size = GetStackSize();
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
if ((ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) {
if ((sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) {
ss.ss_sp = sp;
if (!sigaltstack(&ss, &g_oldsigaltstack)) {
__cxa_atexit(FreeSigAltStack, ss.ss_sp, 0);
} else {

View file

@ -47,7 +47,6 @@ _Alignas(64) static int rand64_lock;
* @note this function is not intended for cryptography
* @note this function passes bigcrush and practrand
* @note this function takes at minimum 15 cycles
* @asyncsignalsafe
* @threadsafe
* @vforksafe
*/

View file

@ -23,6 +23,7 @@
#include "libc/fmt/conv.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/log/libfatal.internal.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
@ -39,6 +40,14 @@
#include "libc/x/x.h"
#include "net/http/escape.h"
int StackOverflow(int f(), int n) {
if (n < INT_MAX) {
return f(f, n + 1) - 1;
} else {
return INT_MAX;
}
}
static bool OutputHasSymbol(const char *output, const char *s) {
return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL"));
}
@ -103,6 +112,7 @@ int (*pRodataOverrunCrash)(int) = RodataOverrunCrash;
char *(*pStackOverrunCrash)(int) = StackOverrunCrash;
char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash;
int (*pNpeCrash)(char *) = NpeCrash;
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
void SetUp(void) {
ShowCrashReports();
@ -130,6 +140,8 @@ void SetUp(void) {
case 8:
__cxa_finalize(0);
exit(pNpeCrash(0));
case 9:
exit(pStackOverflow(pStackOverflow, 0));
default:
printf("preventing fork recursion: %s\n", __argv[1]);
exit(1);
@ -428,6 +440,79 @@ TEST(ShowCrashReports, testDivideByZero) {
free(output);
}
TEST(ShowCrashReports, testStackOverflow) {
if (IsXnu()) return; // TODO(jart): fix me
if (IsWindows()) return; // TODO(jart): fix me
if (IsFreebsd()) return; // TODO(jart): fix me
if (IsOpenbsd()) return; // TODO(jart): fix me
size_t got;
ssize_t rc;
int ws, pid, fds[2];
char *output, buf[512];
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
ASSERT_NE(-1, (pid = vfork()));
if (!pid) {
dup2(fds[1], 1);
dup2(fds[1], 2);
execv(GetProgramExecutableName(),
(char *const[]){GetProgramExecutableName(), "9", "--strace", 0});
_exit(127);
}
close(fds[1]);
output = 0;
appends(&output, "");
for (;;) {
rc = read(fds[0], buf, sizeof(buf));
if (rc == -1) {
ASSERT_EQ(EINTR, errno);
continue;
}
if ((got = rc)) {
appendd(&output, buf, got);
} else {
break;
}
}
close(fds[0]);
ASSERT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFEXITED(ws));
// kprintf("exit status %d\n", WEXITSTATUS(ws));
assert(128 + SIGSEGV == WEXITSTATUS(ws) || 77 == WEXITSTATUS(ws));
/* NULL is stopgap until we can copy symbol tablces into binary */
#ifdef __FNO_OMIT_FRAME_POINTER__
if (!OutputHasSymbol(output, "StackOverflow")) {
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
#endif
// ShowCrashReports() handled it
if (!strstr(output, gc(xasprintf("%d", pid)))) {
fprintf(stderr, "ERROR: crash report didn't have pid\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
if (!strstr(output, "SIGSEGV")) {
fprintf(stderr, "ERROR: crash report didn't have signal name\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
if (!IsTiny()) {
if (!strstr(output, "Stack Overflow")) {
fprintf(stderr, "ERROR: crash report didn't have 'Stack Overflow'\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
} else {
if (!strstr(output, "SEGV_MAPERR")) {
fprintf(stderr, "ERROR: crash report didn't have 'SEGV_MAPERR'\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
}
free(output);
}
// clang-format off
//
// test/libc/log/backtrace_test.c:59: ubsan error: 'int' index 10 into 'char [10]' out of bounds

View file

@ -63,8 +63,11 @@ static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) {
wrote = 0;
}
} while (wrote);
} else if (errno != EINTR) {
return total ? total : -1;
} else {
WARNF("writev() failed %m");
if (errno != EINTR) {
return total ? total : -1;
}
}
} while (i < iovlen);
return total;
@ -121,6 +124,7 @@ static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
v[1].iov_base = bio->t;
v[1].iov_len = sizeof(bio->t);
while ((r = readv(bio->fd, v, 2)) == -1) {
WARNF("tls read() error %s", strerror(errno));
if (errno == EINTR) {
return MBEDTLS_ERR_SSL_WANT_READ;
} else if (errno == EAGAIN) {
@ -128,7 +132,6 @@ static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
} else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) {
return MBEDTLS_ERR_NET_CONN_RESET;
} else {
WARNF("tls read() error %s", strerror(errno));
return MBEDTLS_ERR_NET_RECV_FAILED;
}
}