Closed Bug 1232682 Opened 5 years ago Closed 5 years ago

Crash [@ js::frontend::TokenStream::TokenBuf::findEOLMax] with heap-buffer-overflow


(Core :: JavaScript Engine, defect)

Not set



Tracking Status
firefox46 --- wontfix


(Reporter: decoder, Unassigned)



(Keywords: crash, regression, testcase, Whiteboard: [fuzzblocker] [jsbugmon:])

Crash Data

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

evaluate('function g() { f(); }', {
    columnNumber: true


==32612==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf5224a3a at pc 0x992e05d bp 0xfff888d8 sp 0xfff888cc
READ of size 2 at 0xf5224a3a thread T0
    #0 0x992e05c in js::frontend::TokenStream::TokenBuf::findEOLMax(unsigned int, unsigned int) js/src/frontend/TokenStream.cpp:490
    #1 0x992e05c in js::frontend::TokenStream::reportCompileErrorNumberVA(unsigned int, unsigned int, unsigned int, char*) js/src/frontend/TokenStream.cpp:685
    #2 0x81f7925 in js::frontend::Parser<js::frontend::FullParseHandler>::reportHelper(js::frontend::ParseReportKind, bool, unsigned int, unsigned int, char*) js/src/frontend/Parser.cpp:574
    #3 0x81f7925 in js::frontend::Parser<js::frontend::FullParseHandler>::report(js::frontend::ParseReportKind, bool, js::frontend::ParseNode*, unsigned int, ...) js/src/frontend/Parser.h:598
    #4 0x8226a74 in js::frontend::Parser<js::frontend::FullParseHandler>::functionArguments(js::frontend::YieldHandling, js::frontend::FunctionSyntaxKind, js::frontend::ParseNode*, bool*) js/src/frontend/Parser.cpp:2229
    #5 0x8224f29 in js::frontend::Parser<js::frontend::FullParseHandler>::functionArgsAndBodyGeneric(js::frontend::InHandling, js::frontend::YieldHandling, js::frontend::ParseNode*, JS::Handle<JSFunction*>, js::frontend::FunctionSyntaxKind) js/src/frontend/Parser.cpp:3029
    #6 0x81ea39b in js::frontend::Parser<js::frontend::FullParseHandler>::standaloneLazyFunction(JS::Handle<JSFunction*>, bool, js::GeneratorKind) js/src/frontend/Parser.cpp:2994
    #7 0x988a14e in js::frontend::CompileLazyFunction(JSContext*, JS::Handle<js::LazyScript*>, char16_t const*, unsigned int) js/src/frontend/BytecodeCompiler.cpp:799
    #8 0x8fef92d in JSFunction::createScriptForLazilyInterpretedFunction(JSContext*, JS::Handle<JSFunction*>) js/src/jsfun.cpp:1427
    #9 0x8feea18 in JSFunction::getOrCreateScript(JSContext*) js/src/jsfun.h:389
    #10 0x8feea18 in js::LazyScript::functionDelazifying(JSContext*) const js/src/jsscriptinlines.h:90
    #11 0x8feea18 in JSFunction::createScriptForLazilyInterpretedFunction(JSContext*, JS::Handle<JSFunction*>) js/src/jsfun.cpp:1378
    #12 0x94ee5a8 in JSFunction::getOrCreateScript(JSContext*) js/src/jsfun.h:389
    #13 0x94ee5a8 in js::Invoke(JSContext*, JS::CallArgs const&, js::MaybeConstruct) js/src/vm/Interpreter.cpp:447
    #14 0x94f00cd in js::Invoke(JSContext*, JS::Value const&, JS::Value const&, unsigned int, JS::Value const*, JS::MutableHandle<JS::Value>) js/src/vm/Interpreter.cpp:496
    #15 0x850fd9c in js::jit::DoCallFallback(JSContext*, js::jit::BaselineFrame*, js::jit::ICCall_Fallback*, unsigned int, JS::Value*, JS::MutableHandle<JS::Value>) js/src/jit/BaselineIC.cpp:6160

0xf5224a3a is located 0 bytes to the right of 42-byte region [0xf5224a10,0xf5224a3a)
allocated by thread T0 here:
    #0 0x80f1d94 in __interceptor_malloc /srv/repos/llvm/projects/compiler-rt/lib/asan/
    #1 0x9229d5b in js_malloc(unsigned int) js/src/opt32asan/js/src/../../dist/include/js/Utility.h:221
    #2 0x9229d5b in _ZL13js_pod_mallocIDsEPT_j js/src/opt32asan/js/src/../../dist/include/js/Utility.h:407
    #3 0x9229d5b in char16_t* js::MallocProvider<JS::Zone>::maybe_pod_malloc<char16_t>(unsigned int) js/src/vm/MallocProvider.h:57
    #4 0x9229d5b in char16_t* js::MallocProvider<JS::Zone>::pod_malloc<char16_t>(unsigned int) js/src/vm/MallocProvider.h:90
    #5 0x917ac75 in js::ScriptSource::ensureOwnsSource(js::ExclusiveContext*) js/src/jsscript.cpp:2017
    #6 0x917ac75 in js::ScriptSource::setSourceCopy(js::ExclusiveContext*, JS::SourceBufferHolder&, bool, js::SourceCompressionTask*) js/src/jsscript.cpp:2070
    #7 0x98739ed in BytecodeCompiler::maybeCompressSource() js/src/frontend/BytecodeCompiler.cpp:205
    #8 0x98739ed in BytecodeCompiler::createSourceAndParser() js/src/frontend/BytecodeCompiler.cpp:253
    #9 0x987c297 in BytecodeCompiler::compileScript(JS::Handle<JSObject*>, JS::Handle<JSScript*>) js/src/frontend/BytecodeCompiler.cpp:496
    #10 0x98884f8 in js::frontend::CompileScript(js::ExclusiveContext*, js::LifoAlloc*, JS::Handle<JSObject*>, JS::Handle<js::ScopeObject*>, JS::Handle<JSScript*>, JS::ReadOnlyCompileOptions const&, JS::SourceBufferHolder&, JSString*, js::SourceCompressionTask*, js::ScriptSourceObject**) js/src/frontend/BytecodeCompiler.cpp:738
    #11 0x8f29fc6 in Compile(JSContext*, JS::ReadOnlyCompileOptions const&, SyntacticScopeOption, JS::SourceBufferHolder&, JS::MutableHandle<JSScript*>) js/src/jsapi.cpp:4017
    #12 0x8f2ac66 in Compile(JSContext*, JS::ReadOnlyCompileOptions const&, SyntacticScopeOption, char16_t const*, unsigned int, JS::MutableHandle<JSScript*>) js/src/jsapi.cpp:4027
    #13 0x8f2ac66 in JS::Compile(JSContext*, JS::ReadOnlyCompileOptions const&, char16_t const*, unsigned int, JS::MutableHandle<JSScript*>) js/src/jsapi.cpp:4086
    #14 0x81437ab in Evaluate(JSContext*, unsigned int, JS::Value*) js/src/shell/js.cpp:1365
    #29 0x8141c8b in Load(JSContext*, unsigned int, JS::Value*) js/src/shell/js.cpp:955

SUMMARY: AddressSanitizer: heap-buffer-overflow js/src/frontend/TokenStream.cpp:490 js::frontend::TokenStream::TokenBuf::findEOLMax(unsigned int, unsigned int)
Shadow bytes around the buggy address:
  0x3ea44930: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 03 fa
=>0x3ea44940: fa fa 00 00 00 00 00[02]fa fa fd fd fd fd fd fd
  0x3ea44950: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd

I assume this is a shell-only problem with the evaluate function failing to check the columnNumber property, so not marking s-s. Marking as fuzzblocker though because the signature looks like a security bug.
Whiteboard: [jsbugmon:update,bisect][fuzzblocker] → [fuzzblocker] [jsbugmon:bisect]
JSBugMon: Cannot process bug: Error: Failed to isolate test from comment
Whiteboard: [fuzzblocker] [jsbugmon:bisect] → [fuzzblocker] [jsbugmon:]
JSBugMon: Bisection requested, failed due to error: Error: Failed to isolate test from comment
Christian, there is something going wrong with jsbugmon, see comment 1 and comment 2.
Flags: needinfo?(choller)
Most likely JSBugMon is unable to isolate the test from comment 0 because it throws some kind of error even with syntax checking only.

This looks like a simple shell-only bug to me, please assign some developer to look into it. I doubt it's worth bisecting etc.
Flags: needinfo?(choller)
OK I can take a look.
Flags: needinfo?(jdemooij)
Seems to be a regression from bug 1083913, where we added the columnNumber option to evaluate. Here's a simpler testcase that crashes more reliably:

  evaluate('function g() { with(this){} }', {columnNumber: 999999999});

Basically JSScript sourceStart/sourceEnd become huge values and we crash trying to get the source.

Looks like the API/frontend expects us to pass the whole line, including the part before our start column. Or something.
Flags: needinfo?(jdemooij) → needinfo?(jimb)
Group: javascript-core-security
Closed: 5 years ago
Flags: needinfo?(jimb)
Resolution: --- → DUPLICATE
Duplicate of bug: 1161312
Group: javascript-core-security
You need to log in before you can comment on or make changes to this bug.