Skip to content

Commit 19c8186

Browse files
committed
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.
1 parent 7d31fc3 commit 19c8186

File tree

17 files changed

+103
-64
lines changed

17 files changed

+103
-64
lines changed

build/bootstrap/ape.aarch64

56 Bytes
Binary file not shown.

build/bootstrap/ape.elf

0 Bytes
Binary file not shown.

build/bootstrap/ape.macho

0 Bytes
Binary file not shown.

build/bootstrap/fixupobj

-39.2 KB
Binary file not shown.

examples/crashreport2.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
88
╚─────────────────────────────────────────────────────────────────*/
99
#endif
10+
#include "libc/calls/calls.h"
1011
#include "libc/math.h"
1112
#include "libc/runtime/runtime.h"
1213

@@ -18,6 +19,17 @@ void crash(long x0, long x1, long x2, //
1819
void (*pCrash)(long, long, long, double, double, double) = crash;
1920

2021
int main(int argc, char *argv[]) {
22+
23+
// // by default we launch an addr2line subprocess to print backtraces
24+
// // with line numbers. you can force it to use the embedded solution
25+
// setenv("ADDR2LINE", "", true);
26+
27+
// // using a seccomp sandbox is another way to force embedded backtraces
28+
// pledge("stdio", NULL);
29+
30+
// enable the crash reporting feature
2131
ShowCrashReports();
32+
33+
// time to die
2234
pCrash(1, 2, 3, NAN, NAN, NAN);
2335
}

libc/calls/pledge.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
#include "libc/calls/syscall-sysv.internal.h"
2525
#include "libc/dce.h"
2626
#include "libc/errno.h"
27-
#include "libc/intrin/kprintf.h"
2827
#include "libc/intrin/promises.internal.h"
2928
#include "libc/intrin/strace.internal.h"
29+
#include "libc/intrin/weaken.h"
3030
#include "libc/nexgen32e/vendor.internal.h"
3131
#include "libc/runtime/runtime.h"
32+
#include "libc/runtime/symbols.internal.h"
33+
#include "libc/runtime/zipos.internal.h"
3234
#include "libc/sysv/consts/pr.h"
3335
#include "libc/sysv/errfuns.h"
3436

@@ -199,22 +201,22 @@
199201
* `__pledge_mode` is available to improve the experience of pledge() on
200202
* Linux. It should specify one of the following penalties:
201203
*
204+
* - `PLEDGE_PENALTY_RETURN_EPERM` causes system calls to just return an
205+
* `EPERM` error instead of killing. This is the default on Linux.
206+
* This is a gentler solution that allows code to display a friendly
207+
* warning. Please note this may lead to weird behaviors if the
208+
* software being sandboxed is lazy about checking error results.
209+
*
202210
* - `PLEDGE_PENALTY_KILL_THREAD` causes the violating thread to be
203-
* killed. This is the default on Linux. It's effectively the same as
204-
* killing the process, since redbean has no threads. The termination
205-
* signal can't be caught and will be either `SIGSYS` or `SIGABRT`.
206-
* Consider enabling stderr logging below so you'll know why your
207-
* program failed. Otherwise check the system log.
211+
* killed. It's effectively the same as killing the process, since
212+
* redbean has no threads. The termination signal can't be caught and
213+
* will be either `SIGSYS` or `SIGABRT`. Consider enabling stderr
214+
* logging below so you'll know why your program failed. Otherwise
215+
* check the system log.
208216
*
209217
* - `PLEDGE_PENALTY_KILL_PROCESS` causes the process and all its
210218
* threads to be killed. This is always the case on OpenBSD.
211219
*
212-
* - `PLEDGE_PENALTY_RETURN_EPERM` causes system calls to just return an
213-
* `EPERM` error instead of killing. This is a gentler solution that
214-
* allows code to display a friendly warning. Please note this may
215-
* lead to weird behaviors if the software being sandboxed is lazy
216-
* about checking error results.
217-
*
218220
* `mode` may optionally bitwise or the following flags:
219221
*
220222
* - `PLEDGE_STDERR_LOGGING` enables friendly error message logging
@@ -240,6 +242,8 @@
240242
int pledge(const char *promises, const char *execpromises) {
241243
int e, rc;
242244
unsigned long ipromises, iexecpromises;
245+
if (_weaken(GetSymbolTable))
246+
_weaken(GetSymbolTable)();
243247
if (!promises) {
244248
// OpenBSD says NULL argument means it doesn't change, i.e.
245249
// pledge(0,0) on OpenBSD does nothing. The Cosmopolitan Libc

libc/intrin/promises.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121

2222
// XXX: should be inherited thread local
2323
// see also sys_pledge_linux() which is 100% pure
24-
int __pledge_mode;
24+
int __pledge_mode = PLEDGE_PENALTY_RETURN_EPERM;
2525
unsigned long __promises;
2626
unsigned long __execpromises;

libc/intrin/x86.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
COSMOPOLITAN_C_START_
44

55
enum VendorSignatures {
6-
SIG_INTEL = 0x756e6547, // Genu
7-
SIG_AMD = 0x68747541, // Auth
6+
SIG_INTEL = 0x756e6547, /* Genu */
7+
SIG_AMD = 0x68747541, /* Auth */
88
};
99

1010
enum ProcessorVendors {
@@ -139,7 +139,7 @@ struct __processor_model {
139139
const char *__cpu_march;
140140
};
141141

142-
struct __processor_model __cpu_model;
142+
extern struct __processor_model __cpu_model;
143143

144144
const char *__cpu_march(unsigned);
145145

libc/log/addr2linepath.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,14 @@ static struct {
3737
} g_addr2line;
3838

3939
void GetAddr2linePathInit(void) {
40+
char *res;
4041
int e = errno;
41-
const char *path;
42-
if (!(path = getenv("ADDR2LINE"))) {
42+
const char *env, *cmd, *path;
43+
if ((env = getenv("ADDR2LINE"))) {
44+
cmd = env;
45+
path = env;
46+
} else {
47+
cmd = "addr2line";
4348
path = ADDR2LINE;
4449
}
4550
char *buf = g_addr2line.buf;
@@ -48,12 +53,11 @@ void GetAddr2linePathInit(void) {
4853
strlcat(buf, "/", PATH_MAX);
4954
}
5055
strlcat(buf, path, PATH_MAX);
51-
}
52-
if (*buf) {
53-
g_addr2line.res = buf;
56+
res = buf;
5457
} else {
55-
g_addr2line.res = commandv("addr2line", buf, PATH_MAX);
58+
res = commandv(cmd, buf, PATH_MAX);
5659
}
60+
g_addr2line.res = res;
5761
errno = e;
5862
}
5963

libc/log/backtrace2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
6666
}
6767

6868
if (!PLEDGED(STDIO) || !PLEDGED(EXEC) || !PLEDGED(EXEC)) {
69-
ShowHint("won't print addr2line backtrace because pledge");
69+
ShowHint("pledge() sandboxing makes backtraces not as good");
7070
return -1;
7171
}
7272

0 commit comments

Comments
 (0)