Closed Bug 1637977 Opened 4 years ago Closed 4 years ago

ThreadSanitizer: data race [@ emplace<>] vs. [@ isNothing] with memory corruption

Categories

(Core :: JavaScript Engine, defect)

x86_64
Linux
defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla78
Tracking Status
firefox-esr68 --- unaffected
firefox76 --- unaffected
firefox77 --- unaffected
firefox78 --- fixed

People

(Reporter: decoder, Assigned: iain)

References

(Blocks 1 open bug, Regression)

Details

(7 keywords, Whiteboard: [bugmon:update,bisect][post-critsmash-triage][sec-survey])

Attachments

(2 files)

The following testcase crashes on mozilla-central revision 20200514-8af03d77567d (tsan-opt build, run with --fuzzing-safe --cpu-count=2 --ion-offthread-compile=off):

See attachment.

Backtrace:

==================
WARNING: ThreadSanitizer: data race (pid=29033)
  Read of size 1 at 0x55dcc2e73210 by thread T7:
    #0 isNothing /builds/worker/workspace/obj-build/dist/include/mozilla/Maybe.h:457:46 (js+0x13c9661)
    #1 Pointer js/src/new-regexp/regexp-shim.h:183:16 (js+0x13c9661)
    #2 v8::internal::RegExpCaseFolding::IgnoreSet() js/src/new-regexp/special-case.cc:53:14 (js+0x13c9661)
    #3 v8::internal::CharacterRange::AddCaseEquivalents(v8::internal::Isolate*, v8::internal::Zone*, v8::internal::ZoneList<v8::internal::CharacterRange>*, bool) js/src/new-regexp/regexp-compiler-tonode.cc:1175:20 (js+0x122c454)
    #4 MakeCaseIndependent js/src/new-regexp/regexp-compiler.cc:2480:9 (js+0x1258586)
    #5 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitText(v8::internal::TextNode*) js/src/new-regexp/regexp-compiler.cc:3634:11 (js+0x1258586)
    #6 v8::internal::TextNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238907)
    #7 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x1257959)
    #8 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitChoice(v8::internal::ChoiceNode*) js/src/new-regexp/regexp-compiler.cc:3649:7 (js+0x1257959)
    #9 v8::internal::ChoiceNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x12387c7)
    #10 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125762e)
    #11 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAction(v8::internal::ActionNode*) js/src/new-regexp/regexp-compiler.cc:3642:5 (js+0x125762e)
    #12 v8::internal::ActionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238787)
    #13 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125762e)
    #14 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAction(v8::internal::ActionNode*) js/src/new-regexp/regexp-compiler.cc:3642:5 (js+0x125762e)
    #15 v8::internal::ActionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238787)
    #16 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125838e)
    #17 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAssertion(v8::internal::AssertionNode*) js/src/new-regexp/regexp-compiler.cc:3692:5 (js+0x125838e)
    #18 v8::internal::AssertionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x12388c7)
    #19 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125762e)
    #20 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAction(v8::internal::ActionNode*) js/src/new-regexp/regexp-compiler.cc:3642:5 (js+0x125762e)
    #21 v8::internal::ActionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238787)
    #22 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x1244029)
    #23 v8::internal::AnalyzeRegExp(v8::internal::Isolate*, bool, v8::internal::RegExpNode*) js/src/new-regexp/regexp-compiler.cc:3712:12 (js+0x1244029)
    #24 js::irregexp::CompilePattern(JSContext*, JS::MutableHandle<js::RegExpShared*>, JS::Handle<JSLinearString*>, js::RegExpShared::CodeKind) js/src/new-regexp/RegExpAPI.cpp:514:16 (js+0x1215f75)
    #25 js::RegExpShared::compileIfNecessary(JSContext*, JS::MutableHandle<js::RegExpShared*>, JS::Handle<JSLinearString*>, js::RegExpShared::CodeKind) js/src/vm/RegExpObject.cpp:1033:12 (js+0x693ba4)
    #26 js::RegExpShared::execute(JSContext*, JS::MutableHandle<js::RegExpShared*>, JS::Handle<JSLinearString*>, unsigned long, js::VectorMatchPairs*) js/src/vm/RegExpObject.cpp:1048:8 (js+0x693c8c)
    #27 ExecuteRegExpImpl js/src/builtin/RegExp.cpp:148:7 (js+0x30342b)
    #28 ExecuteRegExp(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSString*>, int, js::VectorMatchPairs*) js/src/builtin/RegExp.cpp:1015:7 (js+0x30342b)
    #29 js::RegExpTester(JSContext*, unsigned int, JS::Value*) js/src/builtin/RegExp.cpp:1187:7 (js+0x30311a)
    #30 CallJSNative js/src/vm/Interpreter.cpp:493:13 (js+0x36fa39)
    [...]
    #89 JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:411:10 (js+0x4cedf9)
    #90 WorkerMain(WorkerInput*) js/src/shell/js.cpp:4234:5 (js+0x219e91)
    #91 callMain<0> js/src/threading/Thread.h:217:5 (js+0x21a220)
    #92 js::detail::ThreadTrampoline<void (&)(WorkerInput*), WorkerInput*&>::Start(void*) js/src/threading/Thread.h:206:11 (js+0x21a220)

  Previous write of size 1 at 0x55dcc2e73210 by thread T8:
    #0 emplace<> /builds/worker/workspace/obj-build/dist/include/mozilla/Maybe.h:779:11 (js+0x13c9682)
    #1 Pointer js/src/new-regexp/regexp-shim.h:184:14 (js+0x13c9682)
    #2 v8::internal::RegExpCaseFolding::IgnoreSet() js/src/new-regexp/special-case.cc:53:14 (js+0x13c9682)
    #3 v8::internal::CharacterRange::AddCaseEquivalents(v8::internal::Isolate*, v8::internal::Zone*, v8::internal::ZoneList<v8::internal::CharacterRange>*, bool) js/src/new-regexp/regexp-compiler-tonode.cc:1175:20 (js+0x122c454)
    #4 MakeCaseIndependent js/src/new-regexp/regexp-compiler.cc:2480:9 (js+0x1258586)
    #5 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitText(v8::internal::TextNode*) js/src/new-regexp/regexp-compiler.cc:3634:11 (js+0x1258586)
    #6 v8::internal::TextNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238907)
    #7 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x1257959)
    #8 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitChoice(v8::internal::ChoiceNode*) js/src/new-regexp/regexp-compiler.cc:3649:7 (js+0x1257959)
    #9 v8::internal::ChoiceNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x12387c7)
    #10 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125762e)
    #11 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAction(v8::internal::ActionNode*) js/src/new-regexp/regexp-compiler.cc:3642:5 (js+0x125762e)
    #12 v8::internal::ActionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238787)
    #13 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125762e)
    #14 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAction(v8::internal::ActionNode*) js/src/new-regexp/regexp-compiler.cc:3642:5 (js+0x125762e)
    #15 v8::internal::ActionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238787)
    #16 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125838e)
    #17 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAssertion(v8::internal::AssertionNode*) js/src/new-regexp/regexp-compiler.cc:3692:5 (js+0x125838e)
    #18 v8::internal::AssertionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x12388c7)
    #19 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x125762e)
    #20 v8::internal::Analysis<v8::internal::(anonymous namespace)::AssertionPropagator, v8::internal::(anonymous namespace)::EatsAtLeastPropagator>::VisitAction(v8::internal::ActionNode*) js/src/new-regexp/regexp-compiler.cc:3642:5 (js+0x125762e)
    #21 v8::internal::ActionNode::Accept(v8::internal::NodeVisitor*) js/src/new-regexp/regexp-compiler.cc:686:1 (js+0x1238787)
    #22 EnsureAnalyzed js/src/new-regexp/regexp-compiler.cc:3607:11 (js+0x1244029)
    #23 v8::internal::AnalyzeRegExp(v8::internal::Isolate*, bool, v8::internal::RegExpNode*) js/src/new-regexp/regexp-compiler.cc:3712:12 (js+0x1244029)
    #24 js::irregexp::CompilePattern(JSContext*, JS::MutableHandle<js::RegExpShared*>, JS::Handle<JSLinearString*>, js::RegExpShared::CodeKind) js/src/new-regexp/RegExpAPI.cpp:514:16 (js+0x1215f75)
    #25 js::RegExpShared::compileIfNecessary(JSContext*, JS::MutableHandle<js::RegExpShared*>, JS::Handle<JSLinearString*>, js::RegExpShared::CodeKind) js/src/vm/RegExpObject.cpp:1033:12 (js+0x693ba4)
    #26 js::RegExpShared::execute(JSContext*, JS::MutableHandle<js::RegExpShared*>, JS::Handle<JSLinearString*>, unsigned long, js::VectorMatchPairs*) js/src/vm/RegExpObject.cpp:1048:8 (js+0x693c8c)
    #27 ExecuteRegExpImpl js/src/builtin/RegExp.cpp:148:7 (js+0x30342b)
    #28 ExecuteRegExp(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSString*>, int, js::VectorMatchPairs*) js/src/builtin/RegExp.cpp:1015:7 (js+0x30342b)
    #29 js::RegExpTester(JSContext*, unsigned int, JS::Value*) js/src/builtin/RegExp.cpp:1187:7 (js+0x30311a)
    #30 CallJSNative js/src/vm/Interpreter.cpp:493:13 (js+0x36fa39)
    [...]
    #89 JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>, JS::MutableHandle<JS::Value>) js/src/vm/CompilationAndEvaluation.cpp:411:10 (js+0x4cedf9)
    #90 WorkerMain(WorkerInput*) js/src/shell/js.cpp:4234:5 (js+0x219e91)
    #91 callMain<0> js/src/threading/Thread.h:217:5 (js+0x21a220)
    #92 js::detail::ThreadTrampoline<void (&)(WorkerInput*), WorkerInput*&>::Start(void*) js/src/threading/Thread.h:206:11 (js+0x21a220)

  Location is global 'v8::internal::RegExpCaseFolding::IgnoreSet()::set' of size 208 at 0x55dcc2e73148 (js+0x0000032ad210)

  Thread T7 (tid=29055, running) created by main thread at:
    #0 pthread_create /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:967:3 (js+0x1756ab)
    #1 js::Thread::create(void* (*)(void*), void*) js/src/threading/posix/PosixThread.cpp:52:7 (js+0x47ab31)
    #2 bool js::Thread::init<void (&)(WorkerInput*), WorkerInput*&>(void (&)(WorkerInput*), WorkerInput*&) js/src/threading/Thread.h:90:12 (js+0x219762)
    #3 EvalInWorker(JSContext*, unsigned int, JS::Value*) js/src/shell/js.cpp:4308:29 (js+0x2067f1)
    [...]
    #14 main js/src/shell/js.cpp:11885:12 (js+0x1ee552)

  Thread T8 (tid=29057, running) created by main thread at:
    #0 pthread_create /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:967:3 (js+0x1756ab)
    #1 js::Thread::create(void* (*)(void*), void*) js/src/threading/posix/PosixThread.cpp:52:7 (js+0x47ab31)
    #2 bool js::Thread::init<void (&)(WorkerInput*), WorkerInput*&>(void (&)(WorkerInput*), WorkerInput*&) js/src/threading/Thread.h:90:12 (js+0x219762)
    #3 EvalInWorker(JSContext*, unsigned int, JS::Value*) js/src/shell/js.cpp:4308:29 (js+0x2067f1)
    [...]
    #14 main js/src/shell/js.cpp:11885:12 (js+0x1ee552)

SUMMARY: ThreadSanitizer: data race /builds/worker/workspace/obj-build/dist/include/mozilla/Maybe.h:457:46 in isNothing
==================

In ASan, this shows up in various ways, including doube-free, heap-use-after-free, heap-buffer-overflow and as a negative-size-param to memmove. In debug builds I managed to trigger

Assertion failure: (run->mRegionsMask[elm] & (1U << bit)) == 0 (Double-free?), at memory/build/mozjemalloc.cpp:2203

The attached testcase reproduces the issue intermittently and is only half-reduced.

Attached file Testcase
Flags: needinfo?(iireland)
Regressed by: 1634135
Has Regression Range: --- → yes

V8's LazyInstance is roughly equivalent to lazy_static! in Rust. It's used here: https://searchfox.org/mozilla-central/source/js/src/new-regexp/special-case.cc#49-54.

The current implementation uses a lazily initialized Maybe<T>, but the initialization can race with offthread parsing. This patch wraps the Maybe in an ExclusiveData.

Assignee: nobody → iireland
Status: NEW → ASSIGNED
Group: javascript-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 4 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla78
Flags: needinfo?(iireland)
Flags: qe-verify-
Whiteboard: [bugmon:update,bisect] → [bugmon:update,bisect][post-critsmash-triage]

As part of a security bug pattern analysis, we are requesting your help with a high level analysis of this bug. It is our hope to develop static analysis (or potentially runtime/dynamic analysis) in the future to identify classes of bugs.

Please visit this google form to reply.

Flags: needinfo?(iireland)
Whiteboard: [bugmon:update,bisect][post-critsmash-triage] → [bugmon:update,bisect][post-critsmash-triage][sec-survey]
Flags: needinfo?(iireland)
Group: core-security-release

Bugmon Analysis:
Unable to reproduce bug using build nearest the original: mozilla-central 20200708094217-34fb169ef962.
Without a baseline, bugmon is unable to analyze this bug.
Removing bugmon keyword as no further action possible.
Please review the bug and re-add the keyword for further analysis.

Keywords: bugmon
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: