mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Improve crash backtrace reliability
We're now able to pretty print a C++ backtrace upon crashing in pretty much any runtime execution scenario. The default pledge sandbox policy on Linux is now to return EPERM. If you call pledge and have debugging functions linked (e.g. GetSymbolTable) then the symbol table shall get loaded before any security policy is put in place. This change updates build/bootstrap/fixupobj too and fixes some other sneaky build errors.
This commit is contained in:
parent
7d31fc311a
commit
19c81863a3
17 changed files with 103 additions and 64 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -7,6 +7,7 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
|
@ -18,6 +19,17 @@ void crash(long x0, long x1, long x2, //
|
|||
void (*pCrash)(long, long, long, double, double, double) = crash;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// // by default we launch an addr2line subprocess to print backtraces
|
||||
// // with line numbers. you can force it to use the embedded solution
|
||||
// setenv("ADDR2LINE", "", true);
|
||||
|
||||
// // using a seccomp sandbox is another way to force embedded backtraces
|
||||
// pledge("stdio", NULL);
|
||||
|
||||
// enable the crash reporting feature
|
||||
ShowCrashReports();
|
||||
|
||||
// time to die
|
||||
pCrash(1, 2, 3, NAN, NAN, NAN);
|
||||
}
|
||||
|
|
|
@ -24,11 +24,13 @@
|
|||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nexgen32e/vendor.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/pr.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
@ -199,22 +201,22 @@
|
|||
* `__pledge_mode` is available to improve the experience of pledge() on
|
||||
* Linux. It should specify one of the following penalties:
|
||||
*
|
||||
* - `PLEDGE_PENALTY_RETURN_EPERM` causes system calls to just return an
|
||||
* `EPERM` error instead of killing. This is the default on Linux.
|
||||
* This is a gentler solution that allows code to display a friendly
|
||||
* warning. Please note this may lead to weird behaviors if the
|
||||
* software being sandboxed is lazy about checking error results.
|
||||
*
|
||||
* - `PLEDGE_PENALTY_KILL_THREAD` causes the violating thread to be
|
||||
* killed. This is the default on Linux. It's effectively the same as
|
||||
* killing the process, since redbean has no threads. The termination
|
||||
* signal can't be caught and will be either `SIGSYS` or `SIGABRT`.
|
||||
* Consider enabling stderr logging below so you'll know why your
|
||||
* program failed. Otherwise check the system log.
|
||||
* killed. It's effectively the same as killing the process, since
|
||||
* redbean has no threads. The termination signal can't be caught and
|
||||
* will be either `SIGSYS` or `SIGABRT`. Consider enabling stderr
|
||||
* logging below so you'll know why your program failed. Otherwise
|
||||
* check the system log.
|
||||
*
|
||||
* - `PLEDGE_PENALTY_KILL_PROCESS` causes the process and all its
|
||||
* threads to be killed. This is always the case on OpenBSD.
|
||||
*
|
||||
* - `PLEDGE_PENALTY_RETURN_EPERM` causes system calls to just return an
|
||||
* `EPERM` error instead of killing. This is a gentler solution that
|
||||
* allows code to display a friendly warning. Please note this may
|
||||
* lead to weird behaviors if the software being sandboxed is lazy
|
||||
* about checking error results.
|
||||
*
|
||||
* `mode` may optionally bitwise or the following flags:
|
||||
*
|
||||
* - `PLEDGE_STDERR_LOGGING` enables friendly error message logging
|
||||
|
@ -240,6 +242,8 @@
|
|||
int pledge(const char *promises, const char *execpromises) {
|
||||
int e, rc;
|
||||
unsigned long ipromises, iexecpromises;
|
||||
if (_weaken(GetSymbolTable))
|
||||
_weaken(GetSymbolTable)();
|
||||
if (!promises) {
|
||||
// OpenBSD says NULL argument means it doesn't change, i.e.
|
||||
// pledge(0,0) on OpenBSD does nothing. The Cosmopolitan Libc
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
|
||||
// XXX: should be inherited thread local
|
||||
// see also sys_pledge_linux() which is 100% pure
|
||||
int __pledge_mode;
|
||||
int __pledge_mode = PLEDGE_PENALTY_RETURN_EPERM;
|
||||
unsigned long __promises;
|
||||
unsigned long __execpromises;
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
COSMOPOLITAN_C_START_
|
||||
|
||||
enum VendorSignatures {
|
||||
SIG_INTEL = 0x756e6547, // Genu
|
||||
SIG_AMD = 0x68747541, // Auth
|
||||
SIG_INTEL = 0x756e6547, /* Genu */
|
||||
SIG_AMD = 0x68747541, /* Auth */
|
||||
};
|
||||
|
||||
enum ProcessorVendors {
|
||||
|
@ -139,7 +139,7 @@ struct __processor_model {
|
|||
const char *__cpu_march;
|
||||
};
|
||||
|
||||
struct __processor_model __cpu_model;
|
||||
extern struct __processor_model __cpu_model;
|
||||
|
||||
const char *__cpu_march(unsigned);
|
||||
|
||||
|
|
|
@ -37,9 +37,14 @@ static struct {
|
|||
} g_addr2line;
|
||||
|
||||
void GetAddr2linePathInit(void) {
|
||||
char *res;
|
||||
int e = errno;
|
||||
const char *path;
|
||||
if (!(path = getenv("ADDR2LINE"))) {
|
||||
const char *env, *cmd, *path;
|
||||
if ((env = getenv("ADDR2LINE"))) {
|
||||
cmd = env;
|
||||
path = env;
|
||||
} else {
|
||||
cmd = "addr2line";
|
||||
path = ADDR2LINE;
|
||||
}
|
||||
char *buf = g_addr2line.buf;
|
||||
|
@ -48,12 +53,11 @@ void GetAddr2linePathInit(void) {
|
|||
strlcat(buf, "/", PATH_MAX);
|
||||
}
|
||||
strlcat(buf, path, PATH_MAX);
|
||||
}
|
||||
if (*buf) {
|
||||
g_addr2line.res = buf;
|
||||
res = buf;
|
||||
} else {
|
||||
g_addr2line.res = commandv("addr2line", buf, PATH_MAX);
|
||||
res = commandv(cmd, buf, PATH_MAX);
|
||||
}
|
||||
g_addr2line.res = res;
|
||||
errno = e;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
}
|
||||
|
||||
if (!PLEDGED(STDIO) || !PLEDGED(EXEC) || !PLEDGED(EXEC)) {
|
||||
ShowHint("won't print addr2line backtrace because pledge");
|
||||
ShowHint("pledge() sandboxing makes backtraces not as good");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define LIMIT 100
|
||||
|
@ -47,8 +49,11 @@ dontinstrument dontasan int PrintBacktraceUsingSymbols(
|
|||
int fd, const struct StackFrame *bp, struct SymbolTable *st) {
|
||||
size_t gi;
|
||||
intptr_t addr;
|
||||
const char *name;
|
||||
int i, symbol, addend;
|
||||
static char cxxbuf[8192];
|
||||
struct Garbages *garbage;
|
||||
static pthread_spinlock_t lock;
|
||||
const struct StackFrame *frame;
|
||||
(void)gi;
|
||||
if (!bp)
|
||||
|
@ -83,8 +88,15 @@ dontinstrument dontasan int PrintBacktraceUsingSymbols(
|
|||
symbol = 0;
|
||||
addend = 0;
|
||||
}
|
||||
kprintf("%012lx %lx %s%+d\n", frame, addr, __get_symbol_name(st, symbol),
|
||||
addend);
|
||||
if ((name = __get_symbol_name(st, symbol)) && __is_mangled(name)) {
|
||||
pthread_spin_lock(&lock);
|
||||
__demangle(cxxbuf, name, sizeof(cxxbuf));
|
||||
kprintf("%012lx %lx %s%+d\n", frame, addr, cxxbuf, addend);
|
||||
pthread_spin_unlock(&lock);
|
||||
name = cxxbuf;
|
||||
} else {
|
||||
kprintf("%012lx %lx %s%+d\n", frame, addr, name, addend);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "libc/calls/struct/utsname.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/cxxabi.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
|
@ -40,7 +41,6 @@
|
|||
#include "libc/log/internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -178,15 +178,15 @@ static relegated bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
|||
}
|
||||
}
|
||||
|
||||
static relegated char *GetSymbolName(struct SymbolTable *st, int symbol,
|
||||
char **mem, size_t *memsz) {
|
||||
char *s, *t;
|
||||
if ((s = __get_symbol_name(st, symbol)) && //
|
||||
s[0] == '_' && s[1] == 'Z' && //
|
||||
(t = __cxa_demangle(s, *mem, memsz, 0))) {
|
||||
*mem = s = t;
|
||||
}
|
||||
return s;
|
||||
static relegated char *GetSymbolName(struct SymbolTable *st, int symbol) {
|
||||
char *str;
|
||||
static char buf[8192];
|
||||
if (!(str = __get_symbol_name(st, symbol)))
|
||||
return str;
|
||||
if (!__is_mangled(str))
|
||||
return str;
|
||||
__demangle(buf, str, sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
static relegated void __oncrash_impl(int sig, struct siginfo *si,
|
||||
|
@ -241,8 +241,6 @@ static relegated void __oncrash_impl(int sig, struct siginfo *si,
|
|||
: (struct StackFrame *)__builtin_frame_address(0)));
|
||||
if (ctx) {
|
||||
long pc;
|
||||
char *mem = 0;
|
||||
size_t memsz = 0;
|
||||
int addend, symbol;
|
||||
const char *debugbin;
|
||||
const char *addr2line;
|
||||
|
@ -305,7 +303,7 @@ static relegated void __oncrash_impl(int sig, struct siginfo *si,
|
|||
addend -= st->symbols[symbol].x;
|
||||
Append(b, " ");
|
||||
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
||||
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
||||
Append(b, "%s", GetSymbolName(st, symbol));
|
||||
if (addend)
|
||||
Append(b, "%+d", addend);
|
||||
}
|
||||
|
@ -327,7 +325,7 @@ static relegated void __oncrash_impl(int sig, struct siginfo *si,
|
|||
addend -= st->symbols[symbol].x;
|
||||
Append(b, " ");
|
||||
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
||||
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
||||
Append(b, "%s", GetSymbolName(st, symbol));
|
||||
if (addend)
|
||||
Append(b, "%+d", addend);
|
||||
}
|
||||
|
@ -367,13 +365,12 @@ static relegated void __oncrash_impl(int sig, struct siginfo *si,
|
|||
}
|
||||
Append(b, " %016lx fp %lx lr ", fp, pc);
|
||||
if (!AppendFileLine(b, addr2line, debugbin, pc) && st) {
|
||||
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
||||
Append(b, "%s", GetSymbolName(st, symbol));
|
||||
if (addend)
|
||||
Append(b, "%+d", addend);
|
||||
}
|
||||
Append(b, "\n");
|
||||
}
|
||||
free(mem);
|
||||
}
|
||||
b->p[b->n - 1] = '\n';
|
||||
klog(b->p, MIN(b->i, b->n));
|
||||
|
|
|
@ -19,10 +19,18 @@
|
|||
#include "libc/stdckdint.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#ifndef TINYMALLOC_MAX_BYTES
|
||||
#define TINYMALLOC_MAX_BYTES 1073741824
|
||||
#endif
|
||||
|
||||
#ifndef TINYMALLOC_MAX_ALIGN
|
||||
#define TINYMALLOC_MAX_ALIGN 4096
|
||||
#endif
|
||||
|
||||
#ifndef MODE_DBG /* don't interfere with asan dlmalloc hooking */
|
||||
|
||||
_Alignas(65536) static struct {
|
||||
char memory[1024 * 1024 * 1024];
|
||||
_Alignas(TINYMALLOC_MAX_ALIGN) static struct {
|
||||
char memory[TINYMALLOC_MAX_BYTES];
|
||||
unsigned used, last, free;
|
||||
} heap;
|
||||
|
||||
|
@ -59,7 +67,7 @@ void *memalign(size_t align, size_t need) {
|
|||
need = sizeof(unsigned);
|
||||
if (align < sizeof(unsigned))
|
||||
align = sizeof(unsigned);
|
||||
if (align > 65536)
|
||||
if (align > TINYMALLOC_MAX_ALIGN)
|
||||
goto InvalidArgument;
|
||||
if (ckd_add(&need, need, sizeof(unsigned) - 1))
|
||||
goto OutOfMemory;
|
||||
|
@ -86,7 +94,7 @@ void *memalign(size_t align, size_t need) {
|
|||
base &= -align;
|
||||
if (ckd_add(&toto, base, need))
|
||||
goto OutOfMemory;
|
||||
if (toto > sizeof(heap.memory))
|
||||
if (toto > TINYMALLOC_MAX_BYTES)
|
||||
goto OutOfMemory;
|
||||
res = heap.memory + base;
|
||||
((unsigned *)res)[-1] = need;
|
||||
|
@ -139,7 +147,7 @@ void *realloc(void *ptr, size_t need) {
|
|||
need &= -sizeof(unsigned);
|
||||
if (ckd_add(&toto, base, need))
|
||||
goto OutOfMemory;
|
||||
if (toto > sizeof(heap.memory))
|
||||
if (toto > TINYMALLOC_MAX_BYTES)
|
||||
goto OutOfMemory;
|
||||
((unsigned *)mem)[-1] = need;
|
||||
heap.used = toto;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -124,6 +125,7 @@ struct SymbolTable *GetSymbolTable(void) {
|
|||
struct Zipos *z;
|
||||
if (pthread_spin_trylock(&g_lock))
|
||||
return 0;
|
||||
int e = errno;
|
||||
if (!__symtab && !__isworker) {
|
||||
if (_weaken(__zipos_get) && (z = _weaken(__zipos_get)())) {
|
||||
if ((__symtab = GetSymbolTableFromZip(z))) {
|
||||
|
@ -137,6 +139,7 @@ struct SymbolTable *GetSymbolTable(void) {
|
|||
__symtab = GetSymbolTableFromElf();
|
||||
}
|
||||
}
|
||||
errno = e;
|
||||
pthread_spin_unlock(&g_lock);
|
||||
return __symtab;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ Copyright (c) 2007 Hyogeol Lee <hyogeollee@gmail.com>\n\
|
|||
Copyright (c) 2015-2017 Kai Wang <kaiwang27@gmail.com>\n\
|
||||
Copyright (c) 2024 Justine Tunney <jtunney@gmail.com>");
|
||||
// https://github.com/freebsd/freebsd-src/blob/2176c9ab71c85efd90a6c7af4a9e04fe8e3d49ca/contrib/libcxxrt/libelftc_dem_gnu3.c
|
||||
// clang -format off
|
||||
// clang-format off
|
||||
|
||||
/**
|
||||
* @file demangle.c
|
||||
|
@ -321,9 +321,9 @@ static char *
|
|||
vector_str_get_flat(struct cpp_demangle_data *ddata, const struct vector_str *v,
|
||||
size_t *l)
|
||||
{
|
||||
ssize_t elem_pos, elem_size, rtn_size;
|
||||
size_t i;
|
||||
char *rtn;
|
||||
char *rtn, *p;
|
||||
ssize_t rtn_size;
|
||||
|
||||
if (!v || !v->size)
|
||||
return 0;
|
||||
|
@ -334,14 +334,9 @@ vector_str_get_flat(struct cpp_demangle_data *ddata, const struct vector_str *v,
|
|||
if (!(rtn = (char *)alloc(ddata, 1, rtn_size + 1)))
|
||||
return 0;
|
||||
|
||||
elem_pos = 0;
|
||||
for (i = 0; i < v->size; ++i) {
|
||||
elem_size = strlen(v->container[i]);
|
||||
memcpy(rtn + elem_pos, v->container[i], elem_size);
|
||||
elem_pos += elem_size;
|
||||
}
|
||||
|
||||
rtn[rtn_size] = '\0';
|
||||
p = rtn;
|
||||
for (i = 0; i < v->size; ++i)
|
||||
p = stpcpy(p, v->container[i]);
|
||||
|
||||
if (l)
|
||||
*l = rtn_size;
|
||||
|
@ -2878,7 +2873,7 @@ static int
|
|||
cpp_demangle_read_tmpl_args(struct cpp_demangle_data *ddata)
|
||||
{
|
||||
struct vector_str *v;
|
||||
size_t arg_len, idx, limit, size;
|
||||
size_t arg_len, idx, limit;
|
||||
char *arg;
|
||||
|
||||
if (!ddata || *ddata->cur == '\0')
|
||||
|
@ -2907,12 +2902,7 @@ cpp_demangle_read_tmpl_args(struct cpp_demangle_data *ddata)
|
|||
|
||||
if (*ddata->cur == 'E') {
|
||||
++ddata->cur;
|
||||
size = v->size;
|
||||
ASSERT(size > 0);
|
||||
if (!strncmp(v->container[size - 1], ">", 1)) {
|
||||
if (!DEM_PUSH_STR(ddata, ">"))
|
||||
return 0;
|
||||
} else if (!DEM_PUSH_STR(ddata, ">"))
|
||||
if (!DEM_PUSH_STR(ddata, ">"))
|
||||
return 0;
|
||||
ddata->is_tmpl = true;
|
||||
break;
|
||||
|
@ -4070,6 +4060,7 @@ failure(char *buf, const char *org, size_t buflen)
|
|||
* be a partially copied result. In both cases, -1 is returned. The size
|
||||
* of the output is only returned if this routine is fully succesful. To
|
||||
* successfully cover nearly all the test cases from libcxxabi use 65536
|
||||
* and to be able to print 99% of the symbols LLVM's libcxx.a, use 5632.
|
||||
*
|
||||
* It's important to call ismangled() before this, since non-c++ symbols
|
||||
* have a special meaning; for example, "g" will return "__float128". It
|
||||
|
|
|
@ -94,6 +94,12 @@ dontasan int main(int argc, char *argv[]) {
|
|||
struct Dll *e;
|
||||
struct TestAspect *a;
|
||||
|
||||
if (errno) {
|
||||
tinyprint(2, "error: the errno variable was contaminated by constructors\n",
|
||||
NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__ubsan_strict = true;
|
||||
__log_level = kLogInfo;
|
||||
GetOpts(argc, argv);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/landlock.h"
|
||||
#include "libc/calls/pledge.h"
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -337,6 +338,7 @@ TEST(unveil, usedTwice_forbidden_worksWithPledge) {
|
|||
ASSERT_NE(-1, (gotsome = _mapshared(FRAMESIZE)));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
__pledge_mode = PLEDGE_PENALTY_KILL_PROCESS;
|
||||
// install our first seccomp filter
|
||||
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath unveil", 0));
|
||||
ASSERT_SYS(0, 0, mkdir("jail", 0755));
|
||||
|
|
Loading…
Reference in a new issue