Closed Bug 1114587 Opened 10 years ago Closed 10 years ago

Crash [@ js::AbstractFramePtr::asRematerializedFrame] or [@ js::Debugger::detachAllDebuggersFromGlobal] with heap-use-after-free

Categories

(Core :: JavaScript Engine, defect)

defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla37
Tracking Status
firefox34 --- unaffected
firefox35 --- unaffected
firefox36 --- fixed
firefox37 --- fixed
firefox-esr31 --- unaffected

People

(Reporter: decoder, Assigned: shu)

References

Details

(4 keywords, Whiteboard: [jsbugmon:update,testComment=7,origRev=54e902f5e85d])

Crash Data

Attachments

(2 files)

The following testcase crashes on mozilla-central revision b915a50bc6be (build with --enable-gczeal --enable-optimize="-O2 -g" --enable-address-sanitizer --target=i686-pc-linux-gnu --without-intl-api --enable-posix-nspr-emulation --disable-debug, run with --fuzzing-safe --thread-count=2 --ion-eager --ion-offthread-compile=off):


var lfcode = new Array();
lfcode.push("");
lfcode.push("");
lfcode.push("\
var g = newGlobal();\
g.debuggeeGlobal = this;\
g.eval('(' + function () {\
        dbg = new Debugger(debuggeeGlobal);\
        dbg.onExceptionUnwind = function (frame, exc) {\
            var s = '!';\
            for (var f = frame; f; f = f.older)\
                    s += f.callee.name;\
        };\
    } + ')();');\
Debugger(17) = this;\
");
while (true) {
  var file = lfcode.shift(); if (file == undefined) { break; }
  loadFile(file)
}
function loadFile(lfVarx) {
  if (lfVarx.substr(-3) != ".js" && lfVarx.length != 1) {
    function newFunc(x) { new Function(x)(); }; newFunc(lfVarx); 
  }
}



Backtrace:

==17519==ERROR: AddressSanitizer: heap-use-after-free on address 0xf4e032a4 at pc 0x943ea44 bp 0xffc37518 sp 0xffc3750c
READ of size 4 at 0xf4e032a4 thread T0
    #0 0x943ea43 in js::AbstractFramePtr::asRematerializedFrame() const js/src/jit/RematerializedFrame.h:139
    #1 0x943ea43 in js::AbstractFramePtr::script() const js/src/vm/Stack-inl.h:631
    #2 0x943ea43 in js::Debugger::removeDebuggeeGlobal(js::FreeOp*, js::GlobalObject*, js::detail::HashTable<js::GlobalObject* const, js::HashSet<js::GlobalObject*, js::DefaultHasher<js::GlobalObject*>, js::SystemAllocPolicy>::SetOps, js::SystemAllocPolicy>::Enum*) js/src/vm/Debugger.cpp:3026
    #3 0x943f09b in js::Debugger::detachAllDebuggersFromGlobal(js::FreeOp*, js::GlobalObject*) js/src/vm/Debugger.cpp:2302
    #4 0x8fc7289 in JSCompartment::sweepGlobalObject(js::FreeOp*) js/src/jscompartment.cpp:565
    #5 0x90c68e6 in js::gc::GCRuntime::beginSweepingZoneGroup() js/src/jsgc.cpp:5067
    #6 0x90ca9a5 in js::gc::GCRuntime::beginSweepPhase(bool) js/src/jsgc.cpp:5236
    #7 0x90d610a in js::gc::GCRuntime::incrementalCollectSlice(js::SliceBudget&, JS::gcreason::Reason) js/src/jsgc.cpp:5953
    #8 0x90d7e67 in js::gc::GCRuntime::gcCycle(bool, js::SliceBudget&, js::JSGCInvocationKind, JS::gcreason::Reason) js/src/jsgc.cpp:6156
    #9 0x90d92f7 in js::gc::GCRuntime::collect(bool, js::SliceBudget&, js::JSGCInvocationKind, JS::gcreason::Reason) js/src/jsgc.cpp:6281
    #10 0x9052c3d in js::gc::GCRuntime::gc(js::JSGCInvocationKind, JS::gcreason::Reason) js/src/jsgc.cpp:6327
    #11 0x8f649b9 in js::DestroyContext(JSContext*, js::DestroyContextMode) js/src/jscntxt.cpp:261
    #12 0x8f64585 in JS_DestroyContext(JSContext*) js/src/jsapi.cpp:756
    #13 0x80e9f47 in DestroyContext(JSContext*, bool) js/src/shell/js.cpp:5179
    #14 0x80e9f47 in main js/src/shell/js.cpp:5907
    #15 0xf73c94d2 in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
    #16 0x80d8f24 in _start (js/src/opt32asan/js/src/shell/js+0x80d8f24)

0xf4e032a4 is located 20 bytes inside of 48-byte region [0xf4e03290,0xf4e032c0)
freed by thread T0 here:
    #0 0x80be164 in __interceptor_free /srv/repos/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:64
    #1 0x8d5aba0 in js_free(void*) js/src/opt32asan/js/src/../../dist/include/js/Utility.h:122
    #2 0x8d5aba0 in js::jit::RematerializedFrame::FreeInVector(js::Vector<js::jit::RematerializedFrame*, 0u, js::TempAllocPolicy>&) js/src/jit/RematerializedFrame.cpp:102
    #3 0x96b2b44 in js::jit::JitActivation::clearRematerializedFrames() js/src/vm/Stack.cpp:1480
    #4 0x96b25dc in js::jit::JitActivation::~JitActivation() js/src/vm/Stack.cpp:1421
    #5 0x89b179a in EnterIon(JSContext*, js::jit::EnterJitData&) js/src/jit/Ion.cpp:2451
    #6 0x89b179a in js::jit::IonCannon(JSContext*, js::RunState&) js/src/jit/Ion.cpp:2530
    #7 0x94a7826 in js::RunScript(JSContext*, js::RunState&) js/src/vm/Interpreter.cpp:412
    #8 0x9457190 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value const&, js::ExecuteType, js::AbstractFramePtr, JS::Value*) js/src/vm/Interpreter.cpp:641
    #9 0x94eac0e in js::Execute(JSContext*, JS::Handle<JSScript*>, JSObject&, JS::Value*) js/src/vm/Interpreter.cpp:677
    #10 0x8fa1590 in ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>, JS::Value*) js/src/jsapi.cpp:4310
    #11 0x8fa1a0a in JS_ExecuteScript(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSScript*>) js/src/jsapi.cpp:4332
    #12 0x80f3627 in RunFile(JSContext*, JS::Handle<JSObject*>, char const*, _IO_FILE*, bool) js/src/shell/js.cpp:450
    #13 0x80f3627 in Process(JSContext*, JSObject*, char const*, bool) js/src/shell/js.cpp:583
    #14 0x80e9b09 in ProcessArgs(JSContext*, JSObject*, js::cli::OptionParser*) js/src/shell/js.cpp:5320
    #15 0x80e9b09 in Shell(JSContext*, js::cli::OptionParser*, char**) js/src/shell/js.cpp:5559
    #16 0x80e9b09 in main js/src/shell/js.cpp:5898
    #17 0xf73c94d2 in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226

previously allocated by thread T0 here:
    #0 0x80be53a in __interceptor_calloc /srv/repos/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:90
    #1 0x8d59825 in js_calloc(unsigned int) js/src/opt32asan/js/src/../../dist/include/js/Utility.h:105
    #2 0x8d59825 in _ZL13js_pod_callocIhEPT_j js/src/opt32asan/js/src/../../dist/include/js/Utility.h:569
    #3 0x8d59825 in unsigned char* js::MallocProvider<js::ThreadSafeContext>::pod_calloc<unsigned char>(unsigned int) js/src/vm/MallocProvider.h:106
    #4 0x8d59825 in js::jit::RematerializedFrame::New(JSContext*, unsigned char*, js::jit::InlineFrameIterator&) js/src/jit/RematerializedFrame.cpp:57
    #5 0x8d5a279 in js::jit::RematerializedFrame::RematerializeInlineFrames(JSContext*, unsigned char*, js::jit::InlineFrameIterator&, js::Vector<js::jit::RematerializedFrame*, 0u, js::TempAllocPolicy>&) js/src/jit/RematerializedFrame.cpp:74
    #6 0x96ac168 in js::jit::JitActivation::getRematerializedFrame(JSContext*, js::jit::JitFrameIterator const&, unsigned int) js/src/vm/Stack.cpp:1514
    #7 0x96abb18 in js::FrameIter::ensureHasRematerializedFrame(JSContext*) js/src/vm/Stack.cpp:969
    #8 0x958f5ec in DebuggerFrame_getOlder(JSContext*, unsigned int, JS::Value*) js/src/vm/Debugger.cpp:5354

SUMMARY: AddressSanitizer: heap-use-after-free js/src/jit/RematerializedFrame.h:139 js::AbstractFramePtr::asRematerializedFrame() const



Not marking s-s because this looks like a Debugger problem (which would be sec-moderate at most).
I cannot make an asan build, I will try with valgrind later, but this issue is likely related to Bug 1073033.
Blocks: 1073033
Flags: needinfo?(nicolas.b.pierron)
I can reproduce this issue with a debug build running under valgrind.
This does not seem related to the recent modification of Scalar Replacement, and I can still reproduce this issue by returning earlier from Scalar Replacement and from Sink, which are the only 2 phases which are adding (most of) recover instructions.

Shu, any idea?
No longer blocks: 1073033
Flags: needinfo?(nicolas.b.pierron) → needinfo?(shu)
Sorry Nicolas, my fault here. In bug 1103027 I thought I'd be all clever and
only bail out to baseline for an onExceptionUnwind hook if there's a catchable,
pending exception.

I forgot that the bailout needs to if there's also a RematerializedFrame, since
the Debugger.Frame that holds it as a referent needs to be cleaned up via
DebugEpilogue after the bailout. There's even a comment right there...

Anyways, this patch just undoes the incorrect optimization.
Attachment #8540930 - Flags: review?(nicolas.b.pierron)
Assignee: nobody → shu
Flags: needinfo?(shu)
Comment on attachment 8540930 [details] [diff] [review]
Bail out in-place for debug mode in exception handler even if there's no pending exception, so that the DebugEpilogue is called.

Review of attachment 8540930 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/jit/JitFrames.cpp
@@ +423,1 @@
>          // We need to bail when there is a catchable exception, and we are the

You meant Bug 1108145, right?

nit: I guess you should also revert this comment ;)
Attachment #8540930 - Flags: review?(nicolas.b.pierron) → review+
Whiteboard: [jsbugmon:update,bisect] → [jsbugmon:bisect]
JSBugMon: Cannot process bug: Unable to automatically reproduce, please track manually.
Whiteboard: [jsbugmon:bisect] → [jsbugmon:]
Crash Signature: [@ js::AbstractFramePtr::asRematerializedFrame] → [@ js::AbstractFramePtr::asRematerializedFrame] [@ js::Debugger::detachAllDebuggersFromGlobal]
Keywords: assertioncrash
OS: Linux → All
Hardware: x86 → All
Summary: Crash [@ js::AbstractFramePtr::asRematerializedFrame] with heap-use-after-free → Crash [@ js::AbstractFramePtr::asRematerializedFrame] or [@ js::Debugger::detachAllDebuggersFromGlobal] with heap-use-after-free
// Randomly chosen test: js/src/tests/browser.js
// -- reduced away --
// Randomly chosen test: js/src/jit-test/tests/debug/Frame-identity-04.js
g = newGlobal();
g.eval("function f(n) { if (n) f(n - 1); debugger; }");
dbg = Debugger(g);
dbg.onDebuggerStatement = function(i) {
    for (; i; i = i.older) {}
    x;
};
g.f(1999);

crashes js debug shell on m-c rev 54e902f5e85d with --fuzzing-safe --no-threads --ion-eager at js::Debugger::removeDebuggeeGlobal.

Debug configure options:

CC="clang -Qunused-arguments" CXX="clang++ -Qunused-arguments" AR=ar AUTOCONF=/usr/local/Cellar/autoconf213/2.13/bin/autoconf213 sh /Users/skywalker/trees/mozilla-central/js/src/configure --target=x86_64-apple-darwin12.5.0 --enable-debug --enable-optimize --enable-nspr-build --enable-more-deterministic --with-ccache --enable-gczeal --enable-debug-symbols --disable-tests

autoBisect shows this is probably related to the following changeset:

The first bad revision is:
changeset:   https://hg.mozilla.org/mozilla-central/rev/877e91964ea9
user:        Shu-yu Guo
date:        Mon Dec 15 18:21:08 2014 -0800
summary:     Bug 1108145 - Fix debug mode in-place Ion->Baseline bailout at loop heads. (r=jandem)
Blocks: 1108145
Status: NEW → ASSIGNED
Whiteboard: [jsbugmon:] → [jsbugmon:update,testComment=7,origRev=54e902f5e85d]
https://hg.mozilla.org/mozilla-central/rev/68aa6c741e27
Status: ASSIGNED → RESOLVED
Closed: 10 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla37
Should we backport this patch to 36?
Flags: needinfo?(shu)
Yeah, we should.
Flags: needinfo?(shu)
Comment on attachment 8540930 [details] [diff] [review]
Bail out in-place for debug mode in exception handler even if there's no pending exception, so that the DebugEpilogue is called.

Approval Request Comment
[Feature/regressing bug #]: bug 1103027
[User impact if declined]: Rare crashes, I guess
[Describe test coverage new/current, TBPL]: on m-c
[Risks and why]: Low, since it's just a bug fix.
[String/UUID change made/needed]: None.
Attachment #8540930 - Flags: approval-mozilla-aurora?
Attachment #8540930 - Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Flags: in-testsuite+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: