Skip to content

Commit b3d5ccb

Browse files
committed
stacktrace.cpp: hardened parsing of backtrace output
1 parent 495aec4 commit b3d5ccb

File tree

1 file changed

+63
-24
lines changed

1 file changed

+63
-24
lines changed

cli/stacktrace.cpp

Lines changed: 63 additions & 24 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,92 @@ 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};
61+
bool no_address = false;
5362
for (int i = offset; i < maxdepth; ++i) {
5463
const char * const symbolString = symbolStringList[i];
5564
// TODO: implement parsing for __APPLE__
65+
// 0 test-signalhandler 0x0000000100dbf42c _Z16print_stacktraceP7__sFILEibib + 124
66+
67+
/*
68+
* examples:
69+
* ./test-signalhandler(_Z16print_stacktraceP8_IO_FILEibib+0xa1) [0x55cb65e41464]
70+
* ./test-signalhandler(+0xf9d9) [0x55cb65e3c9d9]
71+
*/
72+
5673
// skip all leading libc symbols so the first symbol is our code
5774
if (omit_above_own && !own_code) {
5875
if (strstr(symbolString, "/libc.so.6") != nullptr)
5976
continue;
6077
own_code = true;
61-
offset = i; // make sure the numbering is continous if we omit frames
78+
offset = i; // make sure the numbering is continuous if we omit frames
6279
}
6380
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
81+
if (demangling) {
82+
const char * const firstBracketName = strchr(symbolString, '(');
83+
if (firstBracketName) {
84+
const char * const plus = strchr(firstBracketName, '+');
85+
if (plus && (plus>(firstBracketName+1))) {
86+
char input_buffer[1024]= {0};
87+
strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1);
88+
size_t length = getArrayLength(demangle_buffer);
89+
int status=0;
90+
// We're violating the specification - passing stack address instead of malloc'ed heap.
91+
// Benefit is that no further heap is required, while there is sufficient stack...
92+
realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success
93+
}
8094
}
8195
}
96+
97+
const char * const firstBracketAddress = strchr(symbolString, '[');
98+
if (!firstBracketAddress) {
99+
no_address = true;
100+
break;
101+
}
102+
const char * const secondBracketAddress = strchr(firstBracketAddress, ']');
103+
if (!secondBracketAddress) {
104+
no_address = true;
105+
break;
106+
}
107+
82108
const int ordinal=i-offset;
83109
fprintf(output, "#%-2d 0x",
84110
ordinal);
111+
const int padLen = [&]() {
112+
const char * const beginAddress = firstBracketAddress+3;
113+
const int addressLen = int(secondBracketAddress-beginAddress);
114+
return (ADDRESSDISPLAYLENGTH-addressLen);
115+
}();
85116
if (padLen>0)
86117
fprintf(output, "%0*d",
87-
padLen, 0);
118+
padLen,
119+
0);
88120
if (realnameString) {
89121
fprintf(output, "%.*s in %s\n",
90-
static_cast<int>(secondBracketAddress - firstBracketAddress - 3), firstBracketAddress+3,
122+
static_cast<int>(secondBracketAddress - firstBracketAddress - 3),
123+
firstBracketAddress+3,
91124
realnameString);
92125
} else {
93126
fprintf(output, "%.*s in %.*s\n",
94-
static_cast<int>(secondBracketAddress - firstBracketAddress - 3), firstBracketAddress+3,
95-
static_cast<int>(firstBracketAddress - symbolString), symbolString);
127+
static_cast<int>(secondBracketAddress - firstBracketAddress - 3),
128+
firstBracketAddress+3,
129+
static_cast<int>(firstBracketAddress - symbolString),
130+
symbolString);
96131
}
97132
}
98133
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) - code matches the documented usage
99134
free(symbolStringList);
100-
#undef ADDRESSDISPLAYLENGTH
135+
136+
if (no_address) {
137+
fputs("Callstack could not be obtained (no address)\n", output);
138+
return;
139+
}
101140
}
102141

103142
#endif // USE_UNIX_BACKTRACE_SUPPORT

0 commit comments

Comments
 (0)