escargot icon indicating copy to clipboard operation
escargot copied to clipboard

dynamic-stack-buffer-overflow in Escargot::PointerValue::getTypeTag

Open Ye0nny opened this issue 4 months ago • 0 comments

Escargot

  • OS: Ubuntu 20.04.5 LTS (Linux 5.4.0-144-generic x86_64)
  • Revision : 28451a704f3081b8230753625c736c03ce0a2bfd

Build Steps

cmake -DCMAKE_CXX_FLAGS=-fsanitize=address -DESCARGOT_MODE=debug -DESCARGOT_OUTPUT=shell -GNinja

Describe the bug dynamic-stack-buffer-overflow

Test case

testcase

32 ; 
( ( { [ e. b ] : e = { b } = { b : 32 } } = { } ) => a. b = 0 && a. b ^= b ) 
( { b : 32 } ) ; 
32 ; 
b ;
// poc.js
( ( { [ e. b ] : e = { } } = { } ) => a ) 
( { b : 32 } ) ;

Execution steps & Output

$ ./escargot poc.js
=================================================================
==2069311==ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address 0x7ffd090af208 at pc 0x55a5eb048a51 bp 0x7ffd090ada30 sp 0x7ffd090ada20
READ of size 8 at 0x7ffd090af208 thread T0
    #0 0x55a5eb048a50 in Escargot::PointerValue::getTypeTag() const src/runtime/PointerValue.h:1080
    #1 0x55a5eb047201 in Escargot::PointerValue::isObject() const src/runtime/PointerValue.h:168
    #2 0x55a5eb0512f5 in Escargot::Value::isObject() const src/runtime/ValueInlines.h:558
    #3 0x55a5eb2b8f32 in Escargot::InterpreterSlowPath::getObjectPrecomputedCaseOperation(Escargot::ExecutionState&, Escargot::GetObjectPreComputedCase*, Escargot::Value*, Escargot::ByteCodeBlock*) src/interpreter/ByteCodeInterpreter.cpp:2362
    #4 0x55a5eb2aab09 in Escargot::Interpreter::interpret(Escargot::ExecutionState*, Escargot::ByteCodeBlock*, unsigned long, Escargot::Value*) src/interpreter/ByteCodeInterpreter.cpp:654
    #5 0x55a5eb850c77 in Escargot::Value Escargot::FunctionObjectProcessCallGenerator::processCall<Escargot::ScriptArrowFunctionObject, false, false, false, Escargot::ScriptArrowFunctionObjectThisValueBinder, Escargot::FunctionObjectNewTargetBinder, Escargot::FunctionObjectReturnValueBinder>(Escargot::ExecutionState&, Escargot::ScriptArrowFunctionObject*, Escargot::Value const&, unsigned long, Escargot::Value*, Escargot::Object*) src/runtime/FunctionObjectInlines.h:221
    #6 0x55a5eb8500ae in Escargot::ScriptArrowFunctionObject::call(Escargot::ExecutionState&, Escargot::Value const&, unsigned long, Escargot::Value*) (./escargot/escargot+0xa650ae)
    #7 0x55a5eb2ab871 in Escargot::Interpreter::interpret(Escargot::ExecutionState*, Escargot::ByteCodeBlock*, unsigned long, Escargot::Value*) src/interpreter/ByteCodeInterpreter.cpp:767
    #8 0x55a5eb4741bb in Escargot::Script::execute(Escargot::ExecutionState&, bool, bool) src/parser/Script.cpp:499
    #9 0x55a5eb066c62 in Escargot::ScriptRef::execute(Escargot::ExecutionStateRef*) src/api/EscargotPublic.cpp:4715
    #10 0x55a5eb90803c in operator() src/shell/Shell.cpp:790
    #11 0x55a5eb908067 in _FUN src/shell/Shell.cpp:791
    #12 0x55a5eb911d3d in decltype (((forward<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*)>)({parm#1}))((forward<Escargot::ExecutionStateRef*&>)({parm#3}), (forward<Escargot::ScriptRef*&>)({parm#3}))) Escargot::EvaluatorUtil::ApplyTupleIntoArgumentsOfVariadicTemplateFunction<0ul>::apply<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&, Escargot::ExecutionStateRef*&, Escargot::ScriptRef*&>(Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&, Escargot::ExecutionStateRef*&, Escargot::ScriptRef*&) src/api/EscargotPublic.h:521
    #13 0x55a5eb911349 in decltype (Escargot::EvaluatorUtil::ApplyTupleIntoArgumentsOfVariadicTemplateFunction<0ul>::apply((forward<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*)>)({parm#1}), (forward<std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>)({parm#2}), (get<(1ul)-(1)>)((forward<std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>)({parm#2})), (forward<Escargot::ScriptRef*&>)({parm#3}))) Escargot::EvaluatorUtil::ApplyTupleIntoArgumentsOfVariadicTemplateFunction<1ul>::apply<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&, Escargot::ScriptRef*&>(Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&, Escargot::ScriptRef*&) src/api/EscargotPublic.h:510
    #14 0x55a5eb9107a9 in decltype (Escargot::EvaluatorUtil::ApplyTupleIntoArgumentsOfVariadicTemplateFunction<1ul>::apply((forward<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*)>)({parm#1}), (forward<std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>)({parm#2}), (get<(2ul)-(1)>)((forward<std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>)({parm#2})))) Escargot::EvaluatorUtil::ApplyTupleIntoArgumentsOfVariadicTemplateFunction<2ul>::apply<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>(Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&) src/api/EscargotPublic.h:510
    #15 0x55a5eb90f85c in decltype (Escargot::EvaluatorUtil::ApplyTupleIntoArgumentsOfVariadicTemplateFunction<std::tuple_size<std::decay<std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>::type>::value>::apply((forward<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*)>)({parm#1}), (forward<std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>)({parm#2}))) Escargot::EvaluatorUtil::applyTupleIntoArgumentsOfVariadicTemplateFunction<Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&>(Escargot::ValueRef* (*&)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), std::tuple<Escargot::ExecutionStateRef*, Escargot::ScriptRef*>&) src/api/EscargotPublic.h:531
    #16 0x55a5eb90de70 in Escargot::Evaluator::executeImpl<Escargot::ContextRef, Escargot::ScriptRef*>(Escargot::ContextRef*, Escargot::ValueRef* (*)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), Escargot::ScriptRef*)::{lambda(Escargot::ExecutionStateRef*, void*, void*)#1}::operator()(Escargot::ExecutionStateRef*, void*, void*) const src/api/EscargotPublic.h:612
    #17 0x55a5eb90defe in Escargot::Evaluator::executeImpl<Escargot::ContextRef, Escargot::ScriptRef*>(Escargot::ContextRef*, Escargot::ValueRef* (*)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), Escargot::ScriptRef*)::{lambda(Escargot::ExecutionStateRef*, void*, void*)#1}::_FUN(Escargot::ExecutionStateRef*, void*, void*) src/api/EscargotPublic.h:606
    #18 0x55a5eb062de0 in operator() src/api/EscargotPublic.cpp:1087
    #19 0x55a5eb062e1a in _FUN src/api/EscargotPublic.cpp:1088
    #20 0x55a5eb848dd4 in Escargot::SandBox::run(Escargot::Value (*)(Escargot::ExecutionState&, void*), void*) src/runtime/SandBox.cpp:111
    #21 0x55a5eb063079 in Escargot::Evaluator::executeFunction(Escargot::ContextRef*, Escargot::ValueRef* (*)(Escargot::ExecutionStateRef*, void*, void*), void*, void*) src/api/EscargotPublic.cpp:1089
    #22 0x55a5eb90e100 in Escargot::Evaluator::EvaluatorResult Escargot::Evaluator::executeImpl<Escargot::ContextRef, Escargot::ScriptRef*>(Escargot::ContextRef*, Escargot::ValueRef* (*)(Escargot::ExecutionStateRef*, Escargot::ScriptRef*), Escargot::ScriptRef*) src/api/EscargotPublic.h:614
    #23 0x55a5eb90c69a in execute<Escargot::ScriptRef*, evalScript(Escargot::ContextRef*, Escargot::StringRef*, Escargot::StringRef*, bool, bool)::<lambda(Escargot::ExecutionStateRef*, Escargot::ScriptRef*)> > src/api/EscargotPublic.h:585
    #24 0x55a5eb908838 in evalScript src/shell/Shell.cpp:792
    #25 0x55a5eb90b2db in main src/shell/Shell.cpp:1143
    #26 0x7fe8563b3082 in __libc_start_main ../csu/libc-start.c:308
    #27 0x55a5eb0447fd in _start (./escargot/escargot+0x2597fd)

Address 0x7ffd090af208 is located in stack of thread T0 at offset 2424 in frame
    #0 0x55a5eb2a605b in Escargot::Interpreter::interpret(Escargot::ExecutionState*, Escargot::ByteCodeBlock*, unsigned long, Escargot::Value*) src/interpreter/ByteCodeInterpreter.cpp:226

  This frame has 61 object(s):
    [32, 36) 'c' (line 367)
    [48, 52) 'c' (line 394)
    [64, 68) 'c' (line 424)
    [80, 84) '<unknown>'
    [96, 104) 'idx' (line 293)
    [128, 136) 'idx' (line 326)
    [160, 168) '<unknown>'
    [192, 200) '<unknown>'
    ...
    [1568, 1576) '<unknown>'
    [1600, 1608) '<unknown>'
    [1632, 1640) 'v' (line 1477)
    [1664, 1672) '<unknown>'
    [1696, 1704) 'v' (line 1496)
    [1728, 1736) '<unknown>'
    [1760, 1768) 'state' (line 225)
    [1792, 1800) 'programCounter' (line 225)
    [1824, 1840) '<unknown>'
    [1856, 1872) '<unknown>'
    [1888, 1912) 'spreadArgs' (line 1443) <== Memory access at offset 2424 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: dynamic-stack-buffer-overflow src/runtime/PointerValue.h:1080 in Escargot::PointerValue::getTypeTag() const
Shadow bytes around the buggy address:
  0x10002120ddf0: f2 f2 00 f2 f2 f2 00 00 f2 f2 00 00 f2 f2 00 00
  0x10002120de00: 00 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
  0x10002120de10: 00 00 00 00 ca ca ca ca 00 00 00 00 00 00 cb cb
  0x10002120de20: cb cb cb cb 00 00 00 00 ca ca ca ca 00 00 00 00
  0x10002120de30: 00 00 00 00 00 00 cb cb cb cb cb cb 00 00 00 00
=>0x10002120de40: ca[ca]ca ca 00 00 00 cb cb cb cb cb 00 00 00 00
  0x10002120de50: ca ca ca ca 00 00 00 cb cb cb cb cb 00 00 00 00
  0x10002120de60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002120de70: f1 f1 f1 f1 01 f2 01 f2 00 f2 f2 f2 00 f2 f2 f2
  0x10002120de80: 00 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x10002120de90: 00 00 00 00 00 00 00 00 00 00 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
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2069311==ABORTING

when executed in release mode

Output

Segmentation fault

Expected behavior This code should not crash. Other JS engines (such as V8, JSC, etc.) identify a ReferenceError.

poc.js:1: ReferenceError: Cannot access 'e' before initialization
( ( { [ e. b ] : e = { } } = { } ) => a )
        ^
ReferenceError: Cannot access 'e' before initialization
    at poc.js:1:9
    at poc.js:2:1

Credits: @Ye0nny, @EJueon

Ye0nny avatar Apr 07 '24 10:04 Ye0nny