mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-24 14:22:28 +00:00
Write test for stack overflow detection
This commit is contained in:
parent
99e67c348b
commit
cef50f2a6b
4 changed files with 96 additions and 7 deletions
|
@ -92,6 +92,7 @@ static void FreeSigAltStack(void *p) {
|
||||||
* @see callexitontermination()
|
* @see callexitontermination()
|
||||||
*/
|
*/
|
||||||
void ShowCrashReports(void) {
|
void ShowCrashReports(void) {
|
||||||
|
char *sp;
|
||||||
struct sigaltstack ss;
|
struct sigaltstack ss;
|
||||||
/* <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
|
/* <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
|
||||||
kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */
|
kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */
|
||||||
|
@ -105,11 +106,12 @@ void ShowCrashReports(void) {
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
bzero(&ss, sizeof(ss));
|
bzero(&ss, sizeof(ss));
|
||||||
ss.ss_flags = 0;
|
ss.ss_flags = 0;
|
||||||
ss.ss_size = SIGSTKSZ;
|
ss.ss_size = GetStackSize();
|
||||||
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
|
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
|
||||||
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
|
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
|
||||||
if ((ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
if ((sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))) {
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) {
|
||||||
|
ss.ss_sp = sp;
|
||||||
if (!sigaltstack(&ss, &g_oldsigaltstack)) {
|
if (!sigaltstack(&ss, &g_oldsigaltstack)) {
|
||||||
__cxa_atexit(FreeSigAltStack, ss.ss_sp, 0);
|
__cxa_atexit(FreeSigAltStack, ss.ss_sp, 0);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -47,7 +47,6 @@ _Alignas(64) static int rand64_lock;
|
||||||
* @note this function is not intended for cryptography
|
* @note this function is not intended for cryptography
|
||||||
* @note this function passes bigcrush and practrand
|
* @note this function passes bigcrush and practrand
|
||||||
* @note this function takes at minimum 15 cycles
|
* @note this function takes at minimum 15 cycles
|
||||||
* @asyncsignalsafe
|
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
* @vforksafe
|
* @vforksafe
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/intrin/asan.internal.h"
|
#include "libc/intrin/asan.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/log/libfatal.internal.h"
|
#include "libc/log/libfatal.internal.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
|
@ -39,6 +40,14 @@
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
#include "net/http/escape.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) {
|
static bool OutputHasSymbol(const char *output, const char *s) {
|
||||||
return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL"));
|
return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL"));
|
||||||
}
|
}
|
||||||
|
@ -103,6 +112,7 @@ int (*pRodataOverrunCrash)(int) = RodataOverrunCrash;
|
||||||
char *(*pStackOverrunCrash)(int) = StackOverrunCrash;
|
char *(*pStackOverrunCrash)(int) = StackOverrunCrash;
|
||||||
char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash;
|
char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash;
|
||||||
int (*pNpeCrash)(char *) = NpeCrash;
|
int (*pNpeCrash)(char *) = NpeCrash;
|
||||||
|
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||||
|
|
||||||
void SetUp(void) {
|
void SetUp(void) {
|
||||||
ShowCrashReports();
|
ShowCrashReports();
|
||||||
|
@ -130,6 +140,8 @@ void SetUp(void) {
|
||||||
case 8:
|
case 8:
|
||||||
__cxa_finalize(0);
|
__cxa_finalize(0);
|
||||||
exit(pNpeCrash(0));
|
exit(pNpeCrash(0));
|
||||||
|
case 9:
|
||||||
|
exit(pStackOverflow(pStackOverflow, 0));
|
||||||
default:
|
default:
|
||||||
printf("preventing fork recursion: %s\n", __argv[1]);
|
printf("preventing fork recursion: %s\n", __argv[1]);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -428,6 +440,79 @@ TEST(ShowCrashReports, testDivideByZero) {
|
||||||
free(output);
|
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
|
// clang-format off
|
||||||
//
|
//
|
||||||
// test/libc/log/backtrace_test.c:59: ubsan error: 'int' index 10 into 'char [10]' out of bounds
|
// test/libc/log/backtrace_test.c:59: ubsan error: 'int' index 10 into 'char [10]' out of bounds
|
||||||
|
|
|
@ -63,8 +63,11 @@ static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) {
|
||||||
wrote = 0;
|
wrote = 0;
|
||||||
}
|
}
|
||||||
} while (wrote);
|
} while (wrote);
|
||||||
} else if (errno != EINTR) {
|
} else {
|
||||||
return total ? total : -1;
|
WARNF("writev() failed %m");
|
||||||
|
if (errno != EINTR) {
|
||||||
|
return total ? total : -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (i < iovlen);
|
} while (i < iovlen);
|
||||||
return total;
|
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_base = bio->t;
|
||||||
v[1].iov_len = sizeof(bio->t);
|
v[1].iov_len = sizeof(bio->t);
|
||||||
while ((r = readv(bio->fd, v, 2)) == -1) {
|
while ((r = readv(bio->fd, v, 2)) == -1) {
|
||||||
|
WARNF("tls read() error %s", strerror(errno));
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||||
} else if (errno == EAGAIN) {
|
} 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) {
|
} else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) {
|
||||||
return MBEDTLS_ERR_NET_CONN_RESET;
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
||||||
} else {
|
} else {
|
||||||
WARNF("tls read() error %s", strerror(errno));
|
|
||||||
return MBEDTLS_ERR_NET_RECV_FAILED;
|
return MBEDTLS_ERR_NET_RECV_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue