Skip to content

Commit 633540e

Browse files
committed
stacktrace.cpp: hardened parsing of backtrace output [skip ci]
1 parent d4db9f0 commit 633540e

1 file changed

Lines changed: 50 additions & 25 deletions

File tree

cli/stacktrace.cpp

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,17 @@
3131
void print_stacktrace(FILE* output, int start_idx, bool demangling, int maxdepth, bool omit_above_own)
3232
{
3333
// 32 vs. 64bit
34-
#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8)
34+
static constexpr auto ADDRESSDISPLAYLENGTH = (sizeof(long) == 8) ? 12 : 8;
35+
3536
void *callstackArray[32]= {nullptr}; // the less resources the better...
3637
const int currentdepth = backtrace(callstackArray, static_cast<int>(getArrayLength(callstackArray)));
38+
if (currentdepth == 0) {
39+
fputs("Callstack could not be obtained (backtrace)\n", output);
40+
return;
41+
}
42+
if (currentdepth == getArrayLength(callstackArray)) {
43+
fputs("Callstack might be truncated\n", output);
44+
}
3745
// set offset to 1 to omit the printing function itself
3846
int offset=start_idx+1; // some entries on top are within our own exception handling code or libc
3947
if (maxdepth<0)
@@ -43,61 +51,78 @@ void print_stacktrace(FILE* output, int start_idx, bool demangling, int maxdepth
4351

4452
char **symbolStringList = backtrace_symbols(callstackArray, currentdepth);
4553
if (!symbolStringList) {
46-
fputs("Callstack could not be obtained\n", output);
54+
fputs("Callstack could not be obtained (backtrace_symbols)\n", output);
4755
return;
4856
}
4957

5058
fputs("Callstack:\n", output);
5159
bool own_code = false;
5260
char demangle_buffer[2048]= {0};
53-
for (int i = offset; i < maxdepth; ++i) {
61+
for (int i = 0; i < maxdepth; ++i) {
5462
const char * const symbolString = symbolStringList[i];
5563
// TODO: implement parsing for __APPLE__
64+
// 0 test-signalhandler 0x0000000100dbf42c _Z16print_stacktraceP7__sFILEibib + 124
65+
66+
/*
67+
* examples:
68+
* ./test-signalhandler(_Z16print_stacktraceP8_IO_FILEibib+0xa1) [0x55cb65e41464]
69+
* ./test-signalhandler(+0xf9d9) [0x55cb65e3c9d9]
70+
*/
71+
5672
// skip all leading libc symbols so the first symbol is our code
5773
if (omit_above_own && !own_code) {
5874
if (strstr(symbolString, "/libc.so.6") != nullptr)
5975
continue;
6076
own_code = true;
61-
offset = i; // make sure the numbering is continous if we omit frames
77+
offset = i; // make sure the numbering is continuous if we omit frames
6278
}
6379
const char * realnameString = nullptr;
64-
const char * const firstBracketName = strchr(symbolString, '(');
65-
const char * const firstBracketAddress = strchr(symbolString, '[');
66-
const char * const secondBracketAddress = strchr(firstBracketAddress, ']');
67-
const char * const beginAddress = firstBracketAddress+3;
68-
const int addressLen = int(secondBracketAddress-beginAddress);
69-
const int padLen = (ADDRESSDISPLAYLENGTH-addressLen);
70-
if (demangling && firstBracketName) {
71-
const char * const plus = strchr(firstBracketName, '+');
72-
if (plus && (plus>(firstBracketName+1))) {
73-
char input_buffer[1024]= {0};
74-
strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1);
75-
size_t length = getArrayLength(demangle_buffer);
76-
int status=0;
77-
// We're violating the specification - passing stack address instead of malloc'ed heap.
78-
// Benefit is that no further heap is required, while there is sufficient stack...
79-
realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success
80+
if (demangling) {
81+
const char * const firstBracketName = strchr(symbolString, '(');
82+
if (firstBracketName) {
83+
const char * const plus = strchr(firstBracketName, '+');
84+
if (plus && (plus>(firstBracketName+1))) {
85+
char input_buffer[1024]= {0};
86+
strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1);
87+
size_t length = getArrayLength(demangle_buffer);
88+
int status=0;
89+
// We're violating the specification - passing stack address instead of malloc'ed heap.
90+
// Benefit is that no further heap is required, while there is sufficient stack...
91+
realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success
92+
}
8093
}
8194
}
8295
const int ordinal=i-offset;
8396
fprintf(output, "#%-2d 0x",
8497
ordinal);
98+
99+
const char * const firstBracketAddress = strchr(symbolString, '[');// TODO: check
100+
const char * const secondBracketAddress = strchr(firstBracketAddress, ']'); // TODO: check
101+
const int padLen = [&]() {
102+
// TODO: handle firstBracketAddress being NULL
103+
const char * const beginAddress = firstBracketAddress+3;
104+
const int addressLen = int(secondBracketAddress-beginAddress);
105+
return (ADDRESSDISPLAYLENGTH-addressLen);
106+
}();
85107
if (padLen>0)
86108
fprintf(output, "%0*d",
87-
padLen, 0);
109+
padLen,
110+
0);
88111
if (realnameString) {
89112
fprintf(output, "%.*s in %s\n",
90-
static_cast<int>(secondBracketAddress - firstBracketAddress - 3), firstBracketAddress+3,
113+
static_cast<int>(secondBracketAddress - firstBracketAddress - 3),
114+
firstBracketAddress+3,
91115
realnameString);
92116
} else {
93117
fprintf(output, "%.*s in %.*s\n",
94-
static_cast<int>(secondBracketAddress - firstBracketAddress - 3), firstBracketAddress+3,
95-
static_cast<int>(firstBracketAddress - symbolString), symbolString);
118+
static_cast<int>(secondBracketAddress - firstBracketAddress - 3),
119+
firstBracketAddress+3,
120+
static_cast<int>(firstBracketAddress - symbolString),
121+
symbolString);
96122
}
97123
}
98124
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) - code matches the documented usage
99125
free(symbolStringList);
100-
#undef ADDRESSDISPLAYLENGTH
101126
}
102127

103128
#endif // USE_UNIX_BACKTRACE_SUPPORT

0 commit comments

Comments
 (0)