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