summaryrefslogtreecommitdiffstats
path: root/security/sandbox/linux/glue/SandboxCrash.cpp
blob: 8ead16bdf268a573bb477c1c00b3fb11e5a4df44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file needs to be linked into libxul, so it can access the JS
// stack and the crash reporter.  Everything else in this directory
// should be able to be linked into its own shared library, in order
// to be able to isolate sandbox/chromium from ipc/chromium.

#include "SandboxInternal.h"
#include "SandboxLogging.h"

#include <unistd.h>
#include <sys/syscall.h>

#include "mozilla/Unused.h"
#include "mozilla/dom/Exceptions.h"
#include "nsContentUtils.h"
#include "mozilla/StackWalk.h"
#include "nsString.h"
#include "nsThreadUtils.h"

namespace mozilla {

// Log JS stack info in the same place as the sandbox violation
// message.  Useful in case the responsible code is JS and all we have
// are logs and a minidump with the C++ stacks (e.g., on TBPL).
static void
SandboxLogJSStack(void)
{
  if (!NS_IsMainThread()) {
    // This might be a worker thread... or it might be a non-JS
    // thread, or a non-NSPR thread.  There's isn't a good API for
    // dealing with this, yet.
    return;
  }
  if (!nsContentUtils::XPConnect()) {
    // There is no content (e.g., the process is a media plugin), in
    // which case this will probably crash and definitely not work.
    return;
  }
  nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
  // If we got a stack, we must have a current JSContext.  This is icky.  :(
  // Would be better if GetCurrentJSStack() handed out the JSContext it ended up
  // using or something.
  JSContext* cx = frame ? nsContentUtils::GetCurrentJSContext() : nullptr;
  for (int i = 0; frame != nullptr; ++i) {
    nsAutoString fileName, funName;
    int32_t lineNumber;

    // Don't stop unwinding if an attribute can't be read.
    fileName.SetIsVoid(true);
    Unused << frame->GetFilename(cx, fileName);
    lineNumber = 0;
    Unused << frame->GetLineNumber(cx, &lineNumber);
    funName.SetIsVoid(true);
    Unused << frame->GetName(cx, funName);

    if (!funName.IsVoid() || !fileName.IsVoid()) {
      SANDBOX_LOG_ERROR("JS frame %d: %s %s line %d", i,
                        funName.IsVoid() ?
                        "(anonymous)" : NS_ConvertUTF16toUTF8(funName).get(),
                        fileName.IsVoid() ?
                        "(no file)" : NS_ConvertUTF16toUTF8(fileName).get(),
                        lineNumber);
    }

    nsCOMPtr<nsIStackFrame> nextFrame;
    nsresult rv = frame->GetCaller(cx, getter_AddRefs(nextFrame));
    NS_ENSURE_SUCCESS_VOID(rv);
    frame = nextFrame;
  }
}

static void SandboxPrintStackFrame(uint32_t aFrameNumber, void *aPC, void *aSP,
                                   void *aClosure)
{
  char buf[1024];
  MozCodeAddressDetails details;

  MozDescribeCodeAddress(aPC, &details);
  MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
  SANDBOX_LOG_ERROR("frame %s", buf);
}

static void
SandboxLogCStack()
{
  // Skip 3 frames: one for this module, one for the signal handler in
  // libmozsandbox, and one for the signal trampoline.
  //
  // Warning: this might not print any stack frames.  MozStackWalk
  // can't walk past the signal trampoline on ARM (bug 968531), and
  // x86 frame pointer walking may or may not work (bug 1082276).

  MozStackWalk(SandboxPrintStackFrame, /* skip */ 3, /* max */ 0,
               nullptr, 0, nullptr);
  SANDBOX_LOG_ERROR("end of stack.");
}

static void
SandboxCrash(int nr, siginfo_t *info, void *void_context)
{
  pid_t pid = getpid(), tid = syscall(__NR_gettid);
  bool dumped = false;

  if (!dumped) {
    SANDBOX_LOG_ERROR("crash reporter is disabled (or failed);"
                      " trying stack trace:");
    SandboxLogCStack();
  }

  // Do this last, in case it crashes or deadlocks.
  SandboxLogJSStack();

  // Try to reraise, so the parent sees that this process crashed.
  // (If tgkill is forbidden, then seccomp will raise SIGSYS, which
  // also accomplishes that goal.)
  signal(SIGSYS, SIG_DFL);
  syscall(__NR_tgkill, pid, tid, nr);
}

static void __attribute__((constructor))
SandboxSetCrashFunc()
{
  gSandboxCrashFunc = SandboxCrash;
}

} // namespace mozilla