1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/debug/stack_trace.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <signal.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 #include <sys/syscall.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include <algorithm>
22 #include <array>
23 #include <map>
24 #include <memory>
25 #include <ostream>
26 #include <string>
27 #include <tuple>
28 #include <vector>
29
30 #include "base/containers/heap_array.h"
31 #include "base/containers/span.h"
32 #include "base/containers/span_writer.h"
33 #include "base/memory/raw_ptr.h"
34 #include "base/strings/cstring_view.h"
35 #include "build/build_config.h"
36
37 // Controls whether `dladdr(...)` is used to print the callstack. This is
38 // only used on iOS Official build where `backtrace_symbols(...)` prints
39 // misleading symbols (as the binary is stripped).
40 #if BUILDFLAG(IS_IOS) && defined(OFFICIAL_BUILD)
41 #define HAVE_DLADDR
42 #include <dlfcn.h>
43 #endif
44
45 // Surprisingly, uClibc defines __GLIBC__ in some build configs, but
46 // execinfo.h and backtrace(3) are really only present in glibc and in macOS
47 // libc.
48 #if BUILDFLAG(IS_APPLE) || \
49 (defined(__GLIBC__) && !defined(__UCLIBC__) && !defined(__AIX))
50 #define HAVE_BACKTRACE
51 #include <execinfo.h>
52 #endif
53
54 // Controls whether to include code to demangle C++ symbols.
55 #if !defined(USE_SYMBOLIZE) && defined(HAVE_BACKTRACE) && !defined(HAVE_DLADDR)
56 #define DEMANGLE_SYMBOLS
57 #endif
58
59 #if defined(DEMANGLE_SYMBOLS)
60 #include <cxxabi.h>
61 #endif
62
63 #if BUILDFLAG(IS_APPLE)
64 #include <AvailabilityMacros.h>
65 #endif
66
67 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
68 #include <sys/prctl.h>
69
70 #include "base/debug/proc_maps_linux.h"
71 #endif
72
73 #include "base/cfi_buildflags.h"
74 #include "base/debug/debugger.h"
75 #include "base/debug/debugging_buildflags.h"
76 #include "base/debug/stack_trace.h"
77 #include "base/files/scoped_file.h"
78 #include "base/logging.h"
79 #include "base/memory/free_deleter.h"
80 #include "base/memory/singleton.h"
81 #include "base/numerics/safe_conversions.h"
82 #include "base/posix/eintr_wrapper.h"
83 #include "base/strings/string_number_conversions.h"
84 #include "base/strings/string_util.h"
85 #include "build/build_config.h"
86
87 #if defined(USE_SYMBOLIZE)
88 #include "base/third_party/symbolize/symbolize.h" // nogncheck
89
90 #if BUILDFLAG(ENABLE_STACK_TRACE_LINE_NUMBERS)
91 #include "base/debug/dwarf_line_no.h" // nogncheck
92 #endif
93
94 #endif
95
96 namespace base::debug {
97
98 namespace {
99
100 volatile sig_atomic_t in_signal_handler = 0;
101
102 #if !BUILDFLAG(IS_NACL)
103 bool (*try_handle_signal)(int, siginfo_t*, void*) = nullptr;
104 #endif
105
106 #if defined(DEMANGLE_SYMBOLS)
107 // The prefix used for mangled symbols, per the Itanium C++ ABI:
108 // http://www.codesourcery.com/cxx-abi/abi.html#mangling
109 const char kMangledSymbolPrefix[] = "_Z";
110
111 // Characters that can be used for symbols, generated by Ruby:
112 // (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
113 const char kSymbolCharacters[] =
114 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
115
116 // Demangles C++ symbols in the given text. Example:
117 //
118 // "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
119 // =>
120 // "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
DemangleSymbols(std::string * text)121 void DemangleSymbols(std::string* text) {
122 // Note: code in this function is NOT async-signal safe (std::string uses
123 // malloc internally).
124
125 std::string::size_type search_from = 0;
126 while (search_from < text->size()) {
127 // Look for the start of a mangled symbol, from search_from.
128 std::string::size_type mangled_start =
129 text->find(kMangledSymbolPrefix, search_from);
130 if (mangled_start == std::string::npos) {
131 break; // Mangled symbol not found.
132 }
133
134 // Look for the end of the mangled symbol.
135 std::string::size_type mangled_end =
136 text->find_first_not_of(kSymbolCharacters, mangled_start);
137 if (mangled_end == std::string::npos) {
138 mangled_end = text->size();
139 }
140 std::string mangled_symbol =
141 text->substr(mangled_start, mangled_end - mangled_start);
142
143 // Try to demangle the mangled symbol candidate.
144 int status = 0;
145 std::unique_ptr<char, base::FreeDeleter> demangled_symbol(
146 abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, 0, &status));
147 if (status == 0) { // Demangling is successful.
148 // Remove the mangled symbol.
149 text->erase(mangled_start, mangled_end - mangled_start);
150 // Insert the demangled symbol.
151 text->insert(mangled_start, demangled_symbol.get());
152 // Next time, we'll start right after the demangled symbol we inserted.
153 search_from = mangled_start + strlen(demangled_symbol.get());
154 } else {
155 // Failed to demangle. Retry after the "_Z" we just found.
156 search_from = mangled_start + 2;
157 }
158 }
159 }
160 #endif // defined(DEMANGLE_SYMBOLS)
161
162 class BacktraceOutputHandler {
163 public:
164 virtual void HandleOutput(const char* output) = 0;
165
166 protected:
167 virtual ~BacktraceOutputHandler() = default;
168 };
169
170 #if defined(HAVE_BACKTRACE)
OutputPointer(const void * pointer,BacktraceOutputHandler * handler)171 void OutputPointer(const void* pointer, BacktraceOutputHandler* handler) {
172 // This should be more than enough to store a 64-bit number in hex:
173 // 16 hex digits + 1 for null-terminator.
174 char buf[17] = { '\0' };
175 handler->HandleOutput("0x");
176 internal::itoa_r(reinterpret_cast<intptr_t>(pointer), 16, 12, buf);
177 handler->HandleOutput(buf);
178 }
179
180 #if defined(HAVE_DLADDR) || defined(USE_SYMBOLIZE)
OutputValue(size_t value,BacktraceOutputHandler * handler)181 void OutputValue(size_t value, BacktraceOutputHandler* handler) {
182 // Max unsigned 64-bit number in decimal has 20 digits (18446744073709551615).
183 // Hence, 30 digits should be more than enough to represent it in decimal
184 // (including the null-terminator).
185 char buf[30] = { '\0' };
186 internal::itoa_r(static_cast<intptr_t>(value), 10, 1, buf);
187 handler->HandleOutput(buf);
188 }
189 #endif // defined(HAVE_DLADDR) || defined(USE_SYMBOLIZE)
190
191 #if defined(USE_SYMBOLIZE)
OutputFrameId(size_t frame_id,BacktraceOutputHandler * handler)192 void OutputFrameId(size_t frame_id, BacktraceOutputHandler* handler) {
193 handler->HandleOutput("#");
194 OutputValue(frame_id, handler);
195 }
196 #endif // defined(USE_SYMBOLIZE)
197
ProcessBacktrace(span<const void * const> traces,cstring_view prefix_string,BacktraceOutputHandler * handler)198 void ProcessBacktrace(span<const void* const> traces,
199 cstring_view prefix_string,
200 BacktraceOutputHandler* handler) {
201 // NOTE: This code MUST be async-signal safe (it's used by in-process
202 // stack dumping signal handler). NO malloc or stdio is allowed here.
203
204 // Don't exceed kMaxTraces or GetDwarfCompileUnitOffsets can go OOB.
205 traces = traces.first(std::min(traces.size(), StackTrace::kMaxTraces));
206
207 #if defined(USE_SYMBOLIZE)
208 #if BUILDFLAG(ENABLE_STACK_TRACE_LINE_NUMBERS)
209 std::array<uint64_t, StackTrace::kMaxTraces> cu_offsets = {};
210 GetDwarfCompileUnitOffsets(traces.data(), cu_offsets.data(), traces.size());
211 #endif
212
213 for (size_t i = 0; i < traces.size(); ++i) {
214 if (!prefix_string.empty())
215 handler->HandleOutput(prefix_string.c_str());
216
217 OutputFrameId(i, handler);
218 handler->HandleOutput(" ");
219 OutputPointer(traces[i], handler);
220 handler->HandleOutput(" ");
221
222 std::array<char, 1024> buf = {};
223
224 // Subtract by one as return address of function may be in the next function
225 // when a function is annotated as noreturn.
226 //
227 // SAFETY: The pointer here is not dereferenced, it is a program counter and
228 // it is used to look up an object file/function. It is treated as a
229 // `uintptr_t` inside Symbolize().
230 const void* address =
231 UNSAFE_BUFFERS(static_cast<const char*>(traces[i]) - 1);
232 if (google::Symbolize(const_cast<void*>(address), buf.data(), buf.size())) {
233 handler->HandleOutput(buf.data());
234 #if BUILDFLAG(ENABLE_STACK_TRACE_LINE_NUMBERS)
235 // Only output the source line number if the offset was found. Otherwise,
236 // it takes far too long in debug mode when there are lots of symbols.
237 if (GetDwarfSourceLineNumber(address, cu_offsets[i], buf.data(),
238 buf.size())) {
239 handler->HandleOutput(" [");
240 handler->HandleOutput(buf.data());
241 handler->HandleOutput("]");
242 }
243 #endif
244 } else {
245 handler->HandleOutput("<unknown>");
246 }
247
248 handler->HandleOutput("\n");
249 }
250 #else
251 bool printed = false;
252
253 // Below part is async-signal unsafe (uses malloc), so execute it only
254 // when we are not executing the signal handler.
255 if (in_signal_handler == 0 &&
256 IsValueInRangeForNumericType<int>(traces.size())) {
257 #if defined(HAVE_DLADDR)
258 Dl_info dl_info;
259 for (size_t i = 0; i < traces.size(); ++i) {
260 if (!prefix_string.empty()) {
261 handler->HandleOutput(prefix_string.c_str());
262 }
263
264 OutputValue(i, handler);
265 handler->HandleOutput(" ");
266
267 const bool dl_info_found = dladdr(traces[i], &dl_info) != 0;
268 if (dl_info_found) {
269 // SAFETY: dl_info::dli_fname is a NUL-terminated cstring.
270 auto dli_fname = UNSAFE_BUFFERS(base::cstring_view(dl_info.dli_fname));
271 if (size_t last_sep = dli_fname.rfind('/');
272 last_sep != base::cstring_view::npos) {
273 dli_fname.remove_prefix(last_sep + 1u);
274 }
275 handler->HandleOutput(dli_fname.c_str());
276 } else {
277 handler->HandleOutput("???");
278 }
279 handler->HandleOutput(" ");
280 OutputPointer(traces[i], handler);
281
282 handler->HandleOutput("\n");
283 }
284 printed = true;
285 #else // defined(HAVE_DLADDR)
286 auto trace_symbols =
287 // SAFETY: backtrace_symbols returns an allocated array of the same size
288 // as the input array, which is traces.size().
289 UNSAFE_BUFFERS(base::HeapArray<char*, FreeDeleter>::FromOwningPointer(
290 backtrace_symbols(const_cast<void* const*>(traces.data()),
291 static_cast<int>(traces.size())),
292 traces.size()));
293 if (!trace_symbols.empty()) {
294 for (char* s : trace_symbols) {
295 auto trace_symbol = std::string(s);
296 DemangleSymbols(&trace_symbol);
297 if (!prefix_string.empty())
298 handler->HandleOutput(prefix_string.c_str());
299 handler->HandleOutput(trace_symbol.c_str());
300 handler->HandleOutput("\n");
301 }
302
303 printed = true;
304 }
305 #endif // defined(HAVE_DLADDR)
306 }
307
308 if (!printed) {
309 for (const void* const trace : traces) {
310 handler->HandleOutput(" [");
311 OutputPointer(trace, handler);
312 handler->HandleOutput("]\n");
313 }
314 }
315 #endif // defined(USE_SYMBOLIZE)
316 }
317 #endif // defined(HAVE_BACKTRACE)
318
PrintToStderr(const char * output)319 void PrintToStderr(const char* output) {
320 // NOTE: This code MUST be async-signal safe (it's used by in-process
321 // stack dumping signal handler). NO malloc or stdio is allowed here.
322 std::ignore = HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output)));
323 }
324
325 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
AlarmSignalHandler(int signal,siginfo_t * info,void * void_context)326 void AlarmSignalHandler(int signal, siginfo_t* info, void* void_context) {
327 // We have seen rare cases on AMD linux where the default signal handler
328 // either does not run or a thread (Probably an AMD driver thread) prevents
329 // the termination of the gpu process. We catch this case when the alarm fires
330 // and then call exit_group() to kill all threads of the process. This has
331 // resolved the zombie gpu process issues we have seen on our context lost
332 // test.
333 // Note that many different calls were tried to kill the process when it is in
334 // this state. Only 'exit_group' was found to cause termination and it is
335 // speculated that only this works because only this exit kills all threads in
336 // the process (not simply the current thread).
337 // See: http://crbug.com/1396451.
338 PrintToStderr(
339 "Warning: Default signal handler failed to terminate process.\n");
340 PrintToStderr("Calling exit_group() directly to prevent timeout.\n");
341 // See: https://man7.org/linux/man-pages/man2/exit_group.2.html
342 syscall(SYS_exit_group, EXIT_FAILURE);
343 }
344 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||
345 // BUILDFLAG(IS_CHROMEOS)
346
StackDumpSignalHandler(int signal,siginfo_t * info,void * void_context)347 void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
348 // NOTE: This code MUST be async-signal safe.
349 // NO malloc or stdio is allowed here.
350
351 #if !BUILDFLAG(IS_NACL)
352 // Give a registered callback a chance to recover from this signal
353 //
354 // V8 uses guard regions to guarantee memory safety in WebAssembly. This means
355 // some signals might be expected if they originate from Wasm code while
356 // accessing the guard region. We give V8 the chance to handle and recover
357 // from these signals first.
358 if (try_handle_signal != nullptr &&
359 try_handle_signal(signal, info, void_context)) {
360 // The first chance handler took care of this. The SA_RESETHAND flag
361 // replaced this signal handler upon entry, but we want to stay
362 // installed. Thus, we reinstall ourselves before returning.
363 struct sigaction action;
364 memset(&action, 0, sizeof(action));
365 action.sa_flags = static_cast<int>(SA_RESETHAND | SA_SIGINFO);
366 action.sa_sigaction = &StackDumpSignalHandler;
367 sigemptyset(&action.sa_mask);
368
369 sigaction(signal, &action, nullptr);
370 return;
371 }
372 #endif
373
374 // Do not take the "in signal handler" code path on Mac in a DCHECK-enabled
375 // build, as this prevents seeing a useful (symbolized) stack trace on a crash
376 // or DCHECK() failure. While it may not be fully safe to run the stack symbol
377 // printing code, in practice it's better to provide meaningful stack traces -
378 // and the risk is low given we're likely crashing already.
379 #if !BUILDFLAG(IS_APPLE) || !DCHECK_IS_ON()
380 // Record the fact that we are in the signal handler now, so that the rest
381 // of StackTrace can behave in an async-signal-safe manner.
382 in_signal_handler = 1;
383 #endif
384
385 if (BeingDebugged())
386 BreakDebugger();
387
388 PrintToStderr("Received signal ");
389 char buf[1024] = { 0 };
390 internal::itoa_r(signal, 10, 0, buf);
391 PrintToStderr(buf);
392 if (signal == SIGBUS) {
393 if (info->si_code == BUS_ADRALN)
394 PrintToStderr(" BUS_ADRALN ");
395 else if (info->si_code == BUS_ADRERR)
396 PrintToStderr(" BUS_ADRERR ");
397 else if (info->si_code == BUS_OBJERR)
398 PrintToStderr(" BUS_OBJERR ");
399 else
400 PrintToStderr(" <unknown> ");
401 } else if (signal == SIGFPE) {
402 if (info->si_code == FPE_FLTDIV)
403 PrintToStderr(" FPE_FLTDIV ");
404 else if (info->si_code == FPE_FLTINV)
405 PrintToStderr(" FPE_FLTINV ");
406 else if (info->si_code == FPE_FLTOVF)
407 PrintToStderr(" FPE_FLTOVF ");
408 else if (info->si_code == FPE_FLTRES)
409 PrintToStderr(" FPE_FLTRES ");
410 else if (info->si_code == FPE_FLTSUB)
411 PrintToStderr(" FPE_FLTSUB ");
412 else if (info->si_code == FPE_FLTUND)
413 PrintToStderr(" FPE_FLTUND ");
414 else if (info->si_code == FPE_INTDIV)
415 PrintToStderr(" FPE_INTDIV ");
416 else if (info->si_code == FPE_INTOVF)
417 PrintToStderr(" FPE_INTOVF ");
418 else
419 PrintToStderr(" <unknown> ");
420 } else if (signal == SIGILL) {
421 if (info->si_code == ILL_BADSTK)
422 PrintToStderr(" ILL_BADSTK ");
423 else if (info->si_code == ILL_COPROC)
424 PrintToStderr(" ILL_COPROC ");
425 else if (info->si_code == ILL_ILLOPN)
426 PrintToStderr(" ILL_ILLOPN ");
427 else if (info->si_code == ILL_ILLADR)
428 PrintToStderr(" ILL_ILLADR ");
429 else if (info->si_code == ILL_ILLTRP)
430 PrintToStderr(" ILL_ILLTRP ");
431 else if (info->si_code == ILL_PRVOPC)
432 PrintToStderr(" ILL_PRVOPC ");
433 else if (info->si_code == ILL_PRVREG)
434 PrintToStderr(" ILL_PRVREG ");
435 else
436 PrintToStderr(" <unknown> ");
437 } else if (signal == SIGSEGV) {
438 if (info->si_code == SEGV_MAPERR)
439 PrintToStderr(" SEGV_MAPERR ");
440 else if (info->si_code == SEGV_ACCERR)
441 PrintToStderr(" SEGV_ACCERR ");
442 #if defined(ARCH_CPU_X86_64) && \
443 (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS))
444 else if (info->si_code == SI_KERNEL)
445 PrintToStderr(" SI_KERNEL");
446 #endif
447 else
448 PrintToStderr(" <unknown> ");
449 }
450 if (signal == SIGBUS || signal == SIGFPE ||
451 signal == SIGILL || signal == SIGSEGV) {
452 internal::itoa_r(reinterpret_cast<intptr_t>(info->si_addr), 16, 12, buf);
453 PrintToStderr(buf);
454 }
455 PrintToStderr("\n");
456
457 #if BUILDFLAG(CFI_ENFORCEMENT_TRAP)
458 if (signal == SIGILL && info->si_code == ILL_ILLOPN) {
459 PrintToStderr(
460 "CFI: Most likely a control flow integrity violation; for more "
461 "information see:\n");
462 PrintToStderr(
463 "https://www.chromium.org/developers/testing/control-flow-integrity\n");
464 }
465 #endif // BUILDFLAG(CFI_ENFORCEMENT_TRAP)
466
467 #if defined(ARCH_CPU_X86_64) && \
468 (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS))
469 if (signal == SIGSEGV && info->si_code == SI_KERNEL) {
470 PrintToStderr(
471 " Possibly a General Protection Fault, can be due to a non-canonical "
472 "address dereference. See \"Intel 64 and IA-32 Architectures Software "
473 "Developer’s Manual\", Volume 1, Section 3.3.7.1.\n");
474 }
475 #endif
476
477 debug::StackTrace().Print();
478
479 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
480 #if ARCH_CPU_X86_FAMILY
481 ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context);
482 auto gregs = base::span(context->uc_mcontext.gregs);
483
484 struct Register {
485 const char* label;
486 greg_t value;
487 };
488 const auto registers = std::to_array<Register>({
489 #if ARCH_CPU_32_BITS
490 {" gs: ", gregs[REG_GS]}, {" fs: ", gregs[REG_FS]},
491 {" es: ", gregs[REG_ES]}, {" ds: ", gregs[REG_DS]},
492 {" edi: ", gregs[REG_EDI]}, {" esi: ", gregs[REG_ESI]},
493 {" ebp: ", gregs[REG_EBP]}, {" esp: ", gregs[REG_ESP]},
494 {" ebx: ", gregs[REG_EBX]}, {" edx: ", gregs[REG_EDX]},
495 {" ecx: ", gregs[REG_ECX]}, {" eax: ", gregs[REG_EAX]},
496 {" trp: ", gregs[REG_TRAPNO]}, {" err: ", gregs[REG_ERR]},
497 {" ip: ", gregs[REG_EIP]}, {" cs: ", gregs[REG_CS]},
498 {" efl: ", gregs[REG_EFL]}, {" usp: ", gregs[REG_UESP]},
499 {" ss: ", gregs[REG_SS]},
500 #elif ARCH_CPU_64_BITS
501 {" r8: ", gregs[REG_R8]}, {" r9: ", gregs[REG_R9]},
502 {" r10: ", gregs[REG_R10]}, {" r11: ", gregs[REG_R11]},
503 {" r12: ", gregs[REG_R12]}, {" r13: ", gregs[REG_R13]},
504 {" r14: ", gregs[REG_R14]}, {" r15: ", gregs[REG_R15]},
505 {" di: ", gregs[REG_RDI]}, {" si: ", gregs[REG_RSI]},
506 {" bp: ", gregs[REG_RBP]}, {" bx: ", gregs[REG_RBX]},
507 {" dx: ", gregs[REG_RDX]}, {" ax: ", gregs[REG_RAX]},
508 {" cx: ", gregs[REG_RCX]}, {" sp: ", gregs[REG_RSP]},
509 {" ip: ", gregs[REG_RIP]}, {" efl: ", gregs[REG_EFL]},
510 {" cgf: ", gregs[REG_CSGSFS]}, {" erf: ", gregs[REG_ERR]},
511 {" trp: ", gregs[REG_TRAPNO]}, {" msk: ", gregs[REG_OLDMASK]},
512 {" cr2: ", gregs[REG_CR2]},
513 #endif // ARCH_CPU_32_BITS
514 });
515
516 #if ARCH_CPU_32_BITS
517 const int kRegisterPadding = 8;
518 #elif ARCH_CPU_64_BITS
519 const int kRegisterPadding = 16;
520 #endif
521
522 for (size_t i = 0; i < std::size(registers); i++) {
523 PrintToStderr(registers[i].label);
524 internal::itoa_r(registers[i].value, 16, kRegisterPadding, buf);
525 PrintToStderr(buf);
526
527 if ((i + 1) % 4 == 0)
528 PrintToStderr("\n");
529 }
530 PrintToStderr("\n");
531 #endif // ARCH_CPU_X86_FAMILY
532 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
533
534 PrintToStderr("[end of stack trace]\n");
535
536 if (::signal(signal, SIG_DFL) == SIG_ERR) {
537 _exit(EXIT_FAILURE);
538 }
539
540 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
541 // Set an alarm to trigger in case the default handler does not terminate
542 // the process. See 'AlarmSignalHandler' for more details.
543 struct sigaction action;
544 memset(&action, 0, sizeof(action));
545 action.sa_flags = static_cast<int>(SA_RESETHAND);
546 action.sa_sigaction = &AlarmSignalHandler;
547 sigemptyset(&action.sa_mask);
548 sigaction(SIGALRM, &action, nullptr);
549 // 'alarm' function is signal handler safe.
550 // https://man7.org/linux/man-pages/man7/signal-safety.7.html
551 // This delay is set to be long enough for the real signal handler to fire but
552 // shorter than chrome's process watchdog timer.
553 constexpr unsigned int kAlarmSignalDelaySeconds = 5;
554 alarm(kAlarmSignalDelaySeconds);
555
556 // The following is mostly from
557 // third_party/crashpad/crashpad/util/posix/signals.cc as re-raising signals
558 // is complicated.
559
560 // If we can raise a signal with siginfo on this platform, do so. This ensures
561 // that we preserve the siginfo information for asynchronous signals (i.e.
562 // signals that do not re-raise autonomously), such as signals delivered via
563 // kill() and asynchronous hardware faults such as SEGV_MTEAERR, which would
564 // otherwise be lost when re-raising the signal via raise().
565 long retval = syscall(SYS_rt_tgsigqueueinfo, getpid(), syscall(SYS_gettid),
566 info->si_signo, info);
567 if (retval == 0) {
568 return;
569 }
570
571 // Kernels without commit 66dd34ad31e5 ("signal: allow to send any siginfo to
572 // itself"), which was first released in kernel version 3.9, did not permit a
573 // process to send arbitrary signals to itself, and will reject the
574 // rt_tgsigqueueinfo syscall with EPERM. If that happens, follow the non-Linux
575 // code path. Any other errno is unexpected and will cause us to exit.
576 if (errno != EPERM) {
577 _exit(EXIT_FAILURE);
578 }
579 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||
580 // BUILDFLAG(IS_CHROMEOS)
581
582 // Explicitly re-raise the signal even if it might have re-raised itself on
583 // return. Because signal handlers normally execute with their signal blocked,
584 // this raise() cannot immediately deliver the signal. Delivery is deferred
585 // until the signal handler returns and the signal becomes unblocked. The
586 // re-raised signal will appear with the same context as where it was
587 // initially triggered.
588 if (raise(signal) != 0) {
589 _exit(EXIT_FAILURE);
590 }
591 }
592
593 class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
594 public:
595 PrintBacktraceOutputHandler() = default;
596
597 PrintBacktraceOutputHandler(const PrintBacktraceOutputHandler&) = delete;
598 PrintBacktraceOutputHandler& operator=(const PrintBacktraceOutputHandler&) =
599 delete;
600
HandleOutput(const char * output)601 void HandleOutput(const char* output) override {
602 // NOTE: This code MUST be async-signal safe (it's used by in-process
603 // stack dumping signal handler). NO malloc or stdio is allowed here.
604 PrintToStderr(output);
605 }
606 };
607
608 class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
609 public:
StreamBacktraceOutputHandler(std::ostream * os)610 explicit StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {
611 }
612
613 StreamBacktraceOutputHandler(const StreamBacktraceOutputHandler&) = delete;
614 StreamBacktraceOutputHandler& operator=(const StreamBacktraceOutputHandler&) =
615 delete;
616
HandleOutput(const char * output)617 void HandleOutput(const char* output) override { (*os_) << output; }
618
619 private:
620 raw_ptr<std::ostream> os_;
621 };
622
WarmUpBacktrace()623 void WarmUpBacktrace() {
624 // Warm up stack trace infrastructure. It turns out that on the first
625 // call glibc initializes some internal data structures using pthread_once,
626 // and even backtrace() can call malloc(), leading to hangs.
627 //
628 // Example stack trace snippet (with tcmalloc):
629 //
630 // #8 0x0000000000a173b5 in tc_malloc
631 // at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161
632 // #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517
633 // #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262
634 // #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
635 // #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1")
636 // at dl-open.c:639
637 // #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89
638 // #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
639 // #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48
640 // #16 __GI___libc_dlopen_mode at dl-libc.c:165
641 // #17 0x00007ffff61ef8f5 in init
642 // at ../sysdeps/x86_64/../ia64/backtrace.c:53
643 // #18 0x00007ffff6aad400 in pthread_once
644 // at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104
645 // #19 0x00007ffff61efa14 in __GI___backtrace
646 // at ../sysdeps/x86_64/../ia64/backtrace.c:104
647 // #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace
648 // at base/debug/stack_trace_posix.cc:175
649 // #21 0x00000000007a4ae5 in
650 // base::(anonymous namespace)::StackDumpSignalHandler
651 // at base/process_util_posix.cc:172
652 // #22 <signal handler called>
653 StackTrace stack_trace;
654 }
655
656 #if defined(USE_SYMBOLIZE)
657
658 // class SandboxSymbolizeHelper.
659 //
660 // The purpose of this class is to prepare and install a "file open" callback
661 // needed by the stack trace symbolization code
662 // (base/third_party/symbolize/symbolize.h) so that it can function properly
663 // in a sandboxed process. The caveat is that this class must be instantiated
664 // before the sandboxing is enabled so that it can get the chance to open all
665 // the object files that are loaded in the virtual address space of the current
666 // process.
667 class SandboxSymbolizeHelper {
668 public:
669 // Returns the singleton instance.
GetInstance()670 static SandboxSymbolizeHelper* GetInstance() {
671 return Singleton<SandboxSymbolizeHelper,
672 LeakySingletonTraits<SandboxSymbolizeHelper>>::get();
673 }
674
675 SandboxSymbolizeHelper(const SandboxSymbolizeHelper&) = delete;
676 SandboxSymbolizeHelper& operator=(const SandboxSymbolizeHelper&) = delete;
677
678 private:
679 friend struct DefaultSingletonTraits<SandboxSymbolizeHelper>;
680
SandboxSymbolizeHelper()681 SandboxSymbolizeHelper()
682 : is_initialized_(false) {
683 Init();
684 }
685
~SandboxSymbolizeHelper()686 ~SandboxSymbolizeHelper() {
687 UnregisterCallback();
688 CloseObjectFiles();
689 }
690
691 // Returns a O_RDONLY file descriptor for |file_path| if it was opened
692 // successfully during the initialization. The file is repositioned at
693 // offset 0.
694 // IMPORTANT: This function must be async-signal-safe because it can be
695 // called from a signal handler (symbolizing stack frames for a crash).
GetFileDescriptor(const char * file_path)696 int GetFileDescriptor(const char* file_path) {
697 int fd = -1;
698
699 #if !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
700 if (file_path) {
701 // The assumption here is that iterating over std::map<std::string,
702 // base::ScopedFD> does not allocate dynamic memory, hence it is
703 // async-signal-safe.
704 for (const auto& filepath_fd : modules_) {
705 if (strcmp(filepath_fd.first.c_str(), file_path) == 0) {
706 // POSIX.1-2004 requires an implementation to guarantee that dup()
707 // is async-signal-safe.
708 fd = HANDLE_EINTR(dup(filepath_fd.second.get()));
709 break;
710 }
711 }
712 // POSIX.1-2004 requires an implementation to guarantee that lseek()
713 // is async-signal-safe.
714 if (fd >= 0 && lseek(fd, 0, SEEK_SET) < 0) {
715 // Failed to seek.
716 fd = -1;
717 }
718 }
719 #endif // !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
720
721 return fd;
722 }
723
724 // Searches for the object file (from /proc/self/maps) that contains
725 // the specified pc. If found, sets |start_address| to the start address
726 // of where this object file is mapped in memory, sets the module base
727 // address into |base_address|, copies the object file name into
728 // |out_file_name|, and attempts to open the object file. If the object
729 // file is opened successfully, returns the file descriptor. Otherwise,
730 // returns -1.
731 // IMPORTANT: This function must be async-signal-safe because it can be
732 // called from a signal handler (symbolizing stack frames for a crash).
OpenObjectFileContainingPc(uint64_t pc,uint64_t & start_address,uint64_t & base_address,char * file_path_ptr,size_t file_path_size)733 static int OpenObjectFileContainingPc(uint64_t pc,
734 uint64_t& start_address,
735 uint64_t& base_address,
736 char* file_path_ptr,
737 size_t file_path_size) {
738 auto file_path =
739 // SAFETY: This function is given as a function pointer to
740 // google::InstallSymbolizeOpenObjectFileCallback. It provides
741 // `file_path_size` as the size of the string in `file_path_ptr`,
742 // including a NUL terminator. Via code inspection we can see that
743 // `file_path_ptr` can be null, in which case `file_path_size` is zero.
744 UNSAFE_BUFFERS(base::span(file_path_ptr, file_path_size));
745
746 // This method can only be called after the singleton is instantiated.
747 // This is ensured by the following facts:
748 // * This is the only static method in this class, it is private, and
749 // the class has no friends (except for the DefaultSingletonTraits).
750 // The compiler guarantees that it can only be called after the
751 // singleton is instantiated.
752 // * This method is used as a callback for the stack tracing code and
753 // the callback registration is done in the constructor, so logically
754 // it cannot be called before the singleton is created.
755 SandboxSymbolizeHelper* instance = GetInstance();
756
757 // Cannot use STL iterators here, since debug iterators use locks.
758 // NOLINTNEXTLINE(modernize-loop-convert)
759 for (size_t i = 0; i < instance->regions_.size(); ++i) {
760 const MappedMemoryRegion& region = instance->regions_[i];
761 // We overwrite the file_path with the if `pc` is within a
762 // MemoryMappedRegion.
763 if (region.start <= pc && pc < region.end) {
764 start_address = region.start;
765 base_address = region.base;
766 if (!file_path.empty()) {
767 strlcpy(file_path, region.path);
768 }
769 return instance->GetFileDescriptor(region.path.c_str());
770 }
771 }
772 return -1;
773 }
774
775 // This class is copied from
776 // third_party/crashpad/crashpad/util/linux/scoped_pr_set_dumpable.h.
777 // It aims at ensuring the process is dumpable before opening /proc/self/mem.
778 // If the process is already dumpable, this class doesn't do anything.
779 class ScopedPrSetDumpable {
780 public:
781 // Uses `PR_SET_DUMPABLE` to make the current process dumpable.
782 //
783 // Restores the dumpable flag to its original value on destruction. If the
784 // original value couldn't be determined, the destructor attempts to
785 // restore the flag to 0 (non-dumpable).
ScopedPrSetDumpable()786 explicit ScopedPrSetDumpable() {
787 int result = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
788 was_dumpable_ = result > 0;
789
790 if (!was_dumpable_) {
791 std::ignore = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
792 }
793 }
794
795 ScopedPrSetDumpable(const ScopedPrSetDumpable&) = delete;
796 ScopedPrSetDumpable& operator=(const ScopedPrSetDumpable&) = delete;
797
~ScopedPrSetDumpable()798 ~ScopedPrSetDumpable() {
799 if (!was_dumpable_) {
800 std::ignore = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
801 }
802 }
803
804 private:
805 bool was_dumpable_;
806 };
807
808 // Set the base address for each memory region by reading ELF headers in
809 // process memory.
SetBaseAddressesForMemoryRegions()810 void SetBaseAddressesForMemoryRegions() {
811 base::ScopedFD mem_fd;
812 {
813 ScopedPrSetDumpable s;
814 mem_fd = base::ScopedFD(
815 HANDLE_EINTR(open("/proc/self/mem", O_RDONLY | O_CLOEXEC)));
816 if (!mem_fd.is_valid()) {
817 return;
818 }
819 }
820
821 auto safe_memcpy = [&mem_fd](void* dst, uintptr_t src, size_t size) {
822 return HANDLE_EINTR(pread(mem_fd.get(), dst, size,
823 static_cast<off_t>(src))) == ssize_t(size);
824 };
825
826 uintptr_t cur_base = 0;
827 for (auto& r : regions_) {
828 ElfW(Ehdr) ehdr;
829 static_assert(SELFMAG <= sizeof(ElfW(Ehdr)), "SELFMAG too large");
830 if ((r.permissions & MappedMemoryRegion::READ) &&
831 safe_memcpy(&ehdr, r.start, sizeof(ElfW(Ehdr))) &&
832 memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
833 switch (ehdr.e_type) {
834 case ET_EXEC:
835 cur_base = 0;
836 break;
837 case ET_DYN:
838 // Find the segment containing file offset 0. This will correspond
839 // to the ELF header that we just read. Normally this will have
840 // virtual address 0, but this is not guaranteed. We must subtract
841 // the virtual address from the address where the ELF header was
842 // mapped to get the base address.
843 //
844 // If we fail to find a segment for file offset 0, use the address
845 // of the ELF header as the base address.
846 cur_base = r.start;
847 for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
848 ElfW(Phdr) phdr;
849 if (safe_memcpy(&phdr, r.start + ehdr.e_phoff + i * sizeof(phdr),
850 sizeof(phdr)) &&
851 phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
852 cur_base = r.start - phdr.p_vaddr;
853 break;
854 }
855 }
856 break;
857 default:
858 // ET_REL or ET_CORE. These aren't directly executable, so they
859 // don't affect the base address.
860 break;
861 }
862 }
863
864 r.base = cur_base;
865 }
866 }
867
868 // Parses /proc/self/maps in order to compile a list of all object file names
869 // for the modules that are loaded in the current process.
870 // Returns true on success.
CacheMemoryRegions()871 bool CacheMemoryRegions() {
872 // Reads /proc/self/maps.
873 std::string contents;
874 if (!ReadProcMaps(&contents)) {
875 LOG(ERROR) << "Failed to read /proc/self/maps";
876 return false;
877 }
878
879 // Parses /proc/self/maps.
880 if (!ParseProcMaps(contents, ®ions_)) {
881 LOG(ERROR) << "Failed to parse the contents of /proc/self/maps";
882 return false;
883 }
884
885 SetBaseAddressesForMemoryRegions();
886
887 is_initialized_ = true;
888 return true;
889 }
890
891 // Opens all object files and caches their file descriptors.
OpenSymbolFiles()892 void OpenSymbolFiles() {
893 // Pre-opening and caching the file descriptors of all loaded modules is
894 // not safe for production builds. Hence it is only done in non-official
895 // builds. For more details, take a look at: http://crbug.com/341966.
896 #if !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
897 // Open the object files for all read-only executable regions and cache
898 // their file descriptors.
899 std::vector<MappedMemoryRegion>::const_iterator it;
900 for (it = regions_.begin(); it != regions_.end(); ++it) {
901 const MappedMemoryRegion& region = *it;
902 // Only interesed in read-only executable regions.
903 if ((region.permissions & MappedMemoryRegion::READ) ==
904 MappedMemoryRegion::READ &&
905 (region.permissions & MappedMemoryRegion::WRITE) == 0 &&
906 (region.permissions & MappedMemoryRegion::EXECUTE) ==
907 MappedMemoryRegion::EXECUTE) {
908 if (region.path.empty()) {
909 // Skip regions with empty file names.
910 continue;
911 }
912 if (region.path[0] == '[') {
913 // Skip pseudo-paths, like [stack], [vdso], [heap], etc ...
914 continue;
915 }
916 if (base::EndsWith(region.path, " (deleted)",
917 base::CompareCase::SENSITIVE)) {
918 // Skip deleted files.
919 continue;
920 }
921 // Avoid duplicates.
922 if (modules_.find(region.path) == modules_.end()) {
923 int fd = open(region.path.c_str(), O_RDONLY | O_CLOEXEC);
924 if (fd >= 0) {
925 modules_.emplace(region.path, base::ScopedFD(fd));
926 } else {
927 PLOG(WARNING) << "Failed to open file: " << region.path;
928 }
929 }
930 }
931 }
932 #endif // !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
933 }
934
935 // Initializes and installs the symbolization callback.
Init()936 void Init() {
937 if (CacheMemoryRegions()) {
938 OpenSymbolFiles();
939 google::InstallSymbolizeOpenObjectFileCallback(
940 &OpenObjectFileContainingPc);
941 }
942 }
943
944 // Unregister symbolization callback.
UnregisterCallback()945 void UnregisterCallback() {
946 if (is_initialized_) {
947 google::InstallSymbolizeOpenObjectFileCallback(nullptr);
948 is_initialized_ = false;
949 }
950 }
951
952 // Closes all file descriptors owned by this instance.
CloseObjectFiles()953 void CloseObjectFiles() {
954 #if !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
955 modules_.clear();
956 #endif // !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
957 }
958
959 // Set to true upon successful initialization.
960 bool is_initialized_;
961
962 #if !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
963 // Mapping from file name to file descriptor. Includes file descriptors
964 // for all successfully opened object files and the file descriptor for
965 // /proc/self/maps. This code is not safe for production builds.
966 std::map<std::string, base::ScopedFD> modules_;
967 #endif // !defined(OFFICIAL_BUILD) || !defined(NO_UNWIND_TABLES)
968
969 // Cache for the process memory regions. Produced by parsing the contents
970 // of /proc/self/maps cache.
971 std::vector<MappedMemoryRegion> regions_;
972 };
973 #endif // USE_SYMBOLIZE
974
975 } // namespace
976
EnableInProcessStackDumping()977 bool EnableInProcessStackDumping() {
978 #if defined(USE_SYMBOLIZE)
979 SandboxSymbolizeHelper::GetInstance();
980 #endif // USE_SYMBOLIZE
981
982 // When running in an application, our code typically expects SIGPIPE
983 // to be ignored. Therefore, when testing that same code, it should run
984 // with SIGPIPE ignored as well.
985 struct sigaction sigpipe_action;
986 memset(&sigpipe_action, 0, sizeof(sigpipe_action));
987 sigpipe_action.sa_handler = SIG_IGN;
988 sigemptyset(&sigpipe_action.sa_mask);
989 bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0);
990
991 // Avoid hangs during backtrace initialization, see above.
992 WarmUpBacktrace();
993
994 struct sigaction action;
995 memset(&action, 0, sizeof(action));
996 action.sa_flags = static_cast<int>(SA_RESETHAND | SA_SIGINFO);
997 action.sa_sigaction = &StackDumpSignalHandler;
998 sigemptyset(&action.sa_mask);
999
1000 success &= (sigaction(SIGILL, &action, nullptr) == 0);
1001 success &= (sigaction(SIGABRT, &action, nullptr) == 0);
1002 success &= (sigaction(SIGFPE, &action, nullptr) == 0);
1003 success &= (sigaction(SIGBUS, &action, nullptr) == 0);
1004 success &= (sigaction(SIGSEGV, &action, nullptr) == 0);
1005 // On Linux, SIGSYS is reserved by the kernel for seccomp-bpf sandboxing.
1006 #if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
1007 success &= (sigaction(SIGSYS, &action, nullptr) == 0);
1008 #endif // !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
1009
1010 return success;
1011 }
1012
1013 #if !BUILDFLAG(IS_NACL)
SetStackDumpFirstChanceCallback(bool (* handler)(int,siginfo_t *,void *))1014 bool SetStackDumpFirstChanceCallback(bool (*handler)(int, siginfo_t*, void*)) {
1015 DCHECK(try_handle_signal == nullptr || handler == nullptr);
1016 try_handle_signal = handler;
1017
1018 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
1019 defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) || \
1020 defined(UNDEFINED_SANITIZER)
1021 struct sigaction installed_handler;
1022 CHECK_EQ(sigaction(SIGSEGV, NULL, &installed_handler), 0);
1023 // If the installed handler does not point to StackDumpSignalHandler, then
1024 // allow_user_segv_handler is 0.
1025 if (installed_handler.sa_sigaction != StackDumpSignalHandler) {
1026 LOG(WARNING)
1027 << "WARNING: sanitizers are preventing signal handler installation. "
1028 << "WebAssembly trap handlers are disabled.\n";
1029 return false;
1030 }
1031 #endif
1032 return true;
1033 }
1034 #endif
1035
CollectStackTrace(span<const void * > trace)1036 size_t CollectStackTrace(span<const void*> trace) {
1037 // NOTE: This code MUST be async-signal safe (it's used by in-process
1038 // stack dumping signal handler). NO malloc or stdio is allowed here.
1039
1040 #if defined(NO_UNWIND_TABLES) && BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
1041 // If we do not have unwind tables, then try tracing using frame pointers.
1042 return base::debug::TraceStackFramePointers(trace, 0);
1043 #elif defined(HAVE_BACKTRACE)
1044 // Though the backtrace API man page does not list any possible negative
1045 // return values, we take no chance.
1046 return base::saturated_cast<size_t>(
1047 backtrace(const_cast<void**>(trace.data()),
1048 base::saturated_cast<int>(trace.size())));
1049 #else
1050 return 0;
1051 #endif
1052 }
1053
1054 // static
PrintMessageWithPrefix(cstring_view prefix_string,cstring_view message)1055 void StackTrace::PrintMessageWithPrefix(cstring_view prefix_string,
1056 cstring_view message) {
1057 // NOTE: This code MUST be async-signal safe (it's used by in-process
1058 // stack dumping signal handler). NO malloc or stdio is allowed here.
1059 if (!prefix_string.empty()) {
1060 PrintToStderr(prefix_string.c_str());
1061 }
1062 PrintToStderr(message.c_str());
1063 }
1064
PrintWithPrefixImpl(cstring_view prefix_string) const1065 void StackTrace::PrintWithPrefixImpl(cstring_view prefix_string) const {
1066 // NOTE: This code MUST be async-signal safe (it's used by in-process
1067 // stack dumping signal handler). NO malloc or stdio is allowed here.
1068 #if defined(HAVE_BACKTRACE)
1069 PrintBacktraceOutputHandler handler;
1070 ProcessBacktrace(addresses(), prefix_string, &handler);
1071 #endif
1072 }
1073
1074 #if defined(HAVE_BACKTRACE)
OutputToStreamWithPrefixImpl(std::ostream * os,cstring_view prefix_string) const1075 void StackTrace::OutputToStreamWithPrefixImpl(
1076 std::ostream* os,
1077 cstring_view prefix_string) const {
1078 StreamBacktraceOutputHandler handler(os);
1079 ProcessBacktrace(addresses(), prefix_string, &handler);
1080 }
1081 #endif
1082
1083 namespace internal {
1084
1085 // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
1086 // Modified to use bounds-checked containers.
itoa_r(intptr_t i,int base,size_t padding,base::span<char> buf)1087 void itoa_r(intptr_t i, int base, size_t padding, base::span<char> buf) {
1088 // Make sure we can write at least one NUL byte.
1089 if (buf.empty()) {
1090 return;
1091 }
1092
1093 if (base < 2 || base > 16) {
1094 buf[0u] = '\000';
1095 return;
1096 }
1097
1098 auto writer = base::SpanWriter(buf);
1099 size_t start = 0u;
1100
1101 uintptr_t j = static_cast<uintptr_t>(i);
1102
1103 // Handle negative numbers (only for base 10).
1104 if (i < 0 && base == 10) {
1105 // This does "j = -i" while avoiding integer overflow.
1106 j = static_cast<uintptr_t>(-(i + 1)) + 1;
1107
1108 // Make sure we can write the '-' character.
1109 if (!writer.Write('-')) {
1110 buf[0u] = '\000';
1111 return;
1112 }
1113 start += 1u; // The number starts after the sign.
1114 }
1115
1116 // Loop until we have converted the entire number. Output at least one
1117 // character (i.e. '0').
1118 constexpr std::string_view digits = "0123456789abcdef";
1119 do {
1120 // Output the next digit.
1121 if (!writer.Write(digits[j % static_cast<uintptr_t>(base)])) {
1122 buf[0] = '\000';
1123 return;
1124 }
1125 j /= static_cast<uintptr_t>(base);
1126
1127 if (padding > 0)
1128 padding--;
1129 } while (j > 0 || padding > 0);
1130
1131 // Terminate the output with a NUL character.
1132 if (!writer.Write('\000')) {
1133 buf[0] = '\000';
1134 return;
1135 }
1136
1137 // Conversion to ASCII actually resulted in the digits being in reverse order.
1138 // We can't easily generate them in forward order, as we can't tell the number
1139 // of characters needed until we are done converting. So, now, we reverse the
1140 // string (except for the possible "-" sign and the NUL terminator).
1141 std::ranges::reverse(buf.first(writer.num_written() - 1u).subspan(start));
1142 }
1143
1144 } // namespace internal
1145
1146 } // namespace base::debug
1147