summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc333
1 files changed, 333 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
new file mode 100644
index 0000000000..4988ef1eb8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc
@@ -0,0 +1,333 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker.cc: Generic stackwalker.
+//
+// See stackwalker.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include "google_breakpad/processor/stackwalker.h"
+
+#include <assert.h>
+
+#include "common/scoped_ptr.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/dump_context.h"
+#include "google_breakpad/processor/stack_frame.h"
+#include "google_breakpad/processor/stack_frame_symbolizer.h"
+#include "google_breakpad/processor/system_info.h"
+#include "processor/linked_ptr.h"
+#include "processor/logging.h"
+#include "processor/stackwalker_ppc.h"
+#include "processor/stackwalker_ppc64.h"
+#include "processor/stackwalker_sparc.h"
+#include "processor/stackwalker_x86.h"
+#include "processor/stackwalker_amd64.h"
+#include "processor/stackwalker_arm.h"
+#include "processor/stackwalker_arm64.h"
+#include "processor/stackwalker_mips.h"
+
+namespace google_breakpad {
+
+const int Stackwalker::kRASearchWords = 40;
+
+// This default is just a sanity check: a large enough value
+// that allow capturing unbounded recursion traces, yet provide a
+// guardrail against stack walking bugs. The stack walking invariants
+// guarantee that the unwinding process is strictly monotonic and
+// practically bounded by the size of the stack memory range.
+uint32_t Stackwalker::max_frames_ = 1 << 20; // 1M
+bool Stackwalker::max_frames_set_ = false;
+
+uint32_t Stackwalker::max_frames_scanned_ = 1 << 14; // 16k
+
+Stackwalker::Stackwalker(const SystemInfo* system_info,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer)
+ : system_info_(system_info),
+ memory_(memory),
+ modules_(modules),
+ unloaded_modules_(NULL),
+ frame_symbolizer_(frame_symbolizer) {
+ assert(frame_symbolizer_);
+}
+
+void InsertSpecialAttentionModule(
+ StackFrameSymbolizer::SymbolizerResult symbolizer_result,
+ const CodeModule* module,
+ vector<const CodeModule*>* modules) {
+ if (!module) {
+ return;
+ }
+ assert(symbolizer_result == StackFrameSymbolizer::kError ||
+ symbolizer_result == StackFrameSymbolizer::kWarningCorruptSymbols);
+ bool found = false;
+ vector<const CodeModule*>::iterator iter;
+ for (iter = modules->begin(); iter != modules->end(); ++iter) {
+ if (*iter == module) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ BPLOG(INFO) << ((symbolizer_result == StackFrameSymbolizer::kError) ?
+ "Couldn't load symbols for: " :
+ "Detected corrupt symbols for: ")
+ << module->debug_file() << "|" << module->debug_identifier();
+ modules->push_back(module);
+ }
+}
+
+bool Stackwalker::Walk(
+ CallStack* stack,
+ vector<const CodeModule*>* modules_without_symbols,
+ vector<const CodeModule*>* modules_with_corrupt_symbols) {
+ BPLOG_IF(ERROR, !stack) << "Stackwalker::Walk requires |stack|";
+ assert(stack);
+ stack->Clear();
+
+ BPLOG_IF(ERROR, !modules_without_symbols) << "Stackwalker::Walk requires "
+ << "|modules_without_symbols|";
+ BPLOG_IF(ERROR, !modules_without_symbols) << "Stackwalker::Walk requires "
+ << "|modules_with_corrupt_symbols|";
+ assert(modules_without_symbols);
+ assert(modules_with_corrupt_symbols);
+
+ // Begin with the context frame, and keep getting callers until there are
+ // no more.
+
+ // Keep track of the number of scanned or otherwise dubious frames seen
+ // so far, as the caller may have set a limit.
+ uint32_t scanned_frames = 0;
+
+ // Take ownership of the pointer returned by GetContextFrame.
+ scoped_ptr<StackFrame> frame(GetContextFrame());
+
+ while (frame.get()) {
+ // frame already contains a good frame with properly set instruction and
+ // frame_pointer fields. The frame structure comes from either the
+ // context frame (above) or a caller frame (below).
+
+ // Resolve the module information, if a module map was provided.
+ StackFrameSymbolizer::SymbolizerResult symbolizer_result =
+ frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
+ system_info_,
+ frame.get());
+ switch (symbolizer_result) {
+ case StackFrameSymbolizer::kInterrupt:
+ BPLOG(INFO) << "Stack walk is interrupted.";
+ return false;
+ break;
+ case StackFrameSymbolizer::kError:
+ InsertSpecialAttentionModule(symbolizer_result, frame->module,
+ modules_without_symbols);
+ break;
+ case StackFrameSymbolizer::kWarningCorruptSymbols:
+ InsertSpecialAttentionModule(symbolizer_result, frame->module,
+ modules_with_corrupt_symbols);
+ break;
+ case StackFrameSymbolizer::kNoError:
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ // Keep track of the number of dubious frames so far.
+ switch (frame.get()->trust) {
+ case StackFrame::FRAME_TRUST_NONE:
+ case StackFrame::FRAME_TRUST_SCAN:
+ case StackFrame::FRAME_TRUST_CFI_SCAN:
+ scanned_frames++;
+ break;
+ default:
+ break;
+ }
+
+ // Add the frame to the call stack. Relinquish the ownership claim
+ // over the frame, because the stack now owns it.
+ stack->frames_.push_back(frame.release());
+ if (stack->frames_.size() > max_frames_) {
+ // Only emit an error message in the case where the limit
+ // reached is the default limit, not set by the user.
+ if (!max_frames_set_)
+ BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames.";
+ break;
+ }
+
+ // Get the next frame and take ownership.
+ bool stack_scan_allowed = scanned_frames < max_frames_scanned_;
+ frame.reset(GetCallerFrame(stack, stack_scan_allowed));
+ }
+
+ return true;
+}
+
+// static
+Stackwalker* Stackwalker::StackwalkerForCPU(
+ const SystemInfo* system_info,
+ DumpContext* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ const CodeModules* unloaded_modules,
+ StackFrameSymbolizer* frame_symbolizer) {
+ if (!context) {
+ BPLOG(ERROR) << "Can't choose a stackwalker implementation without context";
+ return NULL;
+ }
+
+ Stackwalker* cpu_stackwalker = NULL;
+
+ uint32_t cpu = context->GetContextCPU();
+ switch (cpu) {
+ case MD_CONTEXT_X86:
+ cpu_stackwalker = new StackwalkerX86(system_info,
+ context->GetContextX86(),
+ memory, modules, frame_symbolizer);
+ break;
+
+ case MD_CONTEXT_PPC:
+ cpu_stackwalker = new StackwalkerPPC(system_info,
+ context->GetContextPPC(),
+ memory, modules, frame_symbolizer);
+ break;
+
+ case MD_CONTEXT_PPC64:
+ cpu_stackwalker = new StackwalkerPPC64(system_info,
+ context->GetContextPPC64(),
+ memory, modules, frame_symbolizer);
+ break;
+
+ case MD_CONTEXT_AMD64:
+ cpu_stackwalker = new StackwalkerAMD64(system_info,
+ context->GetContextAMD64(),
+ memory, modules, frame_symbolizer);
+ break;
+
+ case MD_CONTEXT_SPARC:
+ cpu_stackwalker = new StackwalkerSPARC(system_info,
+ context->GetContextSPARC(),
+ memory, modules, frame_symbolizer);
+ break;
+
+ case MD_CONTEXT_MIPS:
+ case MD_CONTEXT_MIPS64:
+ cpu_stackwalker = new StackwalkerMIPS(system_info,
+ context->GetContextMIPS(),
+ memory, modules, frame_symbolizer);
+ break;
+
+ case MD_CONTEXT_ARM:
+ {
+ int fp_register = -1;
+ if (system_info->os_short == "ios")
+ fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
+ cpu_stackwalker = new StackwalkerARM(system_info,
+ context->GetContextARM(),
+ fp_register, memory, modules,
+ frame_symbolizer);
+ break;
+ }
+
+ case MD_CONTEXT_ARM64:
+ cpu_stackwalker = new StackwalkerARM64(system_info,
+ context->GetContextARM64(),
+ memory, modules,
+ frame_symbolizer);
+ break;
+ }
+
+ BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
+ ", can't choose a stackwalker "
+ "implementation";
+ if (cpu_stackwalker) {
+ cpu_stackwalker->unloaded_modules_ = unloaded_modules;
+ }
+ return cpu_stackwalker;
+}
+
+// CONSIDER: check stack alignment?
+bool Stackwalker::TerminateWalk(uint64_t caller_ip,
+ uint64_t caller_sp,
+ uint64_t callee_sp,
+ bool first_unwind) const {
+ // Treat an instruction address less than 4k as end-of-stack.
+ // (using InstructionAddressSeemsValid() here is very tempting,
+ // but we need to handle JITted code)
+ if (caller_ip < (1 << 12)) {
+ return true;
+ }
+
+ // NOTE: The stack address range is implicitly checked
+ // when the stack memory is accessed.
+
+ // The stack pointer should monotonically increase. For first unwind
+ // we allow caller_sp == callee_sp to account for architectures where
+ // the return address is stored in a register (so it's possible to have
+ // leaf functions which don't move the stack pointer)
+ if (first_unwind ? (caller_sp < callee_sp) : (caller_sp <= callee_sp)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool Stackwalker::InstructionAddressSeemsValid(uint64_t address) const {
+ StackFrame frame;
+ frame.instruction = address;
+ StackFrameSymbolizer::SymbolizerResult symbolizer_result =
+ frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
+ system_info_, &frame);
+
+ if (!frame.module) {
+ // not inside any loaded module
+ return false;
+ }
+
+ if (!frame_symbolizer_->HasImplementation()) {
+ // No valid implementation to symbolize stack frame, but the address is
+ // within a known module.
+ return true;
+ }
+
+ if (symbolizer_result != StackFrameSymbolizer::kNoError &&
+ symbolizer_result != StackFrameSymbolizer::kWarningCorruptSymbols) {
+ // Some error occurred during symbolization, but the address is within a
+ // known module
+ return true;
+ }
+
+ return !frame.function_name.empty();
+}
+
+} // namespace google_breakpad