CVE-2020-1912
Description
An out-of-bounds read/write vulnerability when executing lazily compiled inner generator functions in Facebook Hermes prior to commit 091835377369c8fd5917d9b87acffa721ad2a168 allows attackers to potentially execute arbitrary code via crafted JavaScript. Note that this is only exploitable if the application using Hermes permits evaluation of untrusted JavaScript. Hence, most React Native applications are not affected.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Facebook Hermes prior to commit 09183537 has an out-of-bounds read/write in lazily compiled inner generator functions, potentially allowing arbitrary code execution via crafted JavaScript.
The vulnerability is an out-of-bounds read/write in Facebook Hermes, a JavaScript engine, occurring when executing lazily compiled inner generator functions. The root cause is a flaw in how Hermes handles the compilation and execution of generator functions, specifically when they are lazily compiled, leading to memory corruption [1][2].
An attacker can exploit this by providing crafted JavaScript that triggers the vulnerable code path. However, exploitation requires that the application using Hermes allows evaluation of untrusted JavaScript. In typical React Native applications, this is not the case because they do not execute arbitrary JavaScript from untrusted sources [1][3].
Successful exploitation could allow an attacker to execute arbitrary code within the context of the Hermes engine, potentially leading to full compromise of the application's process [1].
Facebook has fixed this vulnerability in commit 091835377369c8fd5917d9b87acffa721ad2a168. Users should update Hermes to a version that includes this commit. Most React Native applications are not affected because they do not evaluate untrusted JavaScript [2][3].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
hermes-enginenpm | < 0.5.2 | 0.5.2 |
Affected products
2- Facebook/Hermesv5Range: commit prior to 091835377369c8fd5917d9b87acffa721ad2a168
Patches
1091835377369Correctly restore whether or not a function is an inner generator
8 files changed · +64 −21
include/hermes/IRGen/IRGen.h+3 −0 modified@@ -42,6 +42,9 @@ struct LazyCompilationData { /// The type of function, e.g. statement or expression. ESTree::NodeKind nodeKind; + /// Whether or not this is the inner function of a generator. + bool isGeneratorInnerFunction; + /// Whether or not the function is strict. bool strictMode; };
include/hermes/IR/IR.h+3 −1 modified@@ -336,8 +336,10 @@ class SerializedScope { #ifndef HERMESVM_LEAN /// The source of a lazy AST node. struct LazySource { - // The type of node (such as a FunctionDeclaration or FunctionExpression). + /// The type of node (such as a FunctionDeclaration or FunctionExpression). ESTree::NodeKind nodeKind{ESTree::NodeKind::Empty}; + /// Whether or not this is the inner function of a generator + bool isGeneratorInnerFunction; /// The source buffer id in which this function can be find. uint32_t bufferId{0}; /// The range of the function within the buffer (the whole function node, not
lib/BCGen/HBC/BytecodeGenerator.cpp+2 −0 modified@@ -278,6 +278,8 @@ std::unique_ptr<BytecodeModule> BytecodeModuleGenerator::generate() { auto lazyData = llvh::make_unique<LazyCompilationData>(); lazyData->parentScope = F->getLazyScope(); lazyData->nodeKind = F->getLazySource().nodeKind; + lazyData->isGeneratorInnerFunction = + F->getLazySource().isGeneratorInnerFunction; lazyData->bufferId = F->getLazySource().bufferId; lazyData->originalName = F->getOriginalOrInferredName(); lazyData->closureAlias = F->getLazyClosureAlias()
lib/IRGen/ESTreeIRGen.cpp+5 −1 modified@@ -349,7 +349,11 @@ std::pair<Function *, Function *> ESTreeIRGen::doLazyFunction( !llvh::isa<ESTree::ArrowFunctionExpressionNode>(node) && "lazy compilation not supported for arrow functions"); - auto *func = genES5Function(lazyData->originalName, parentVar, node); + auto *func = genES5Function( + lazyData->originalName, + parentVar, + node, + lazyData->isGeneratorInnerFunction); addLexicalDebugInfo(func, topLevel, lexicalScopeChain); return {func, topLevel}; }
lib/IRGen/ESTreeIRGen-func.cpp+10 −6 modified@@ -215,6 +215,7 @@ Function *ESTreeIRGen::genES5Function( auto &lazySource = newFunction->getLazySource(); lazySource.bufferId = bodyBlock->bufferId; lazySource.nodeKind = getLazyFunctionKind(functionNode); + lazySource.isGeneratorInnerFunction = isGeneratorInnerFunction; lazySource.functionRange = functionNode->getSourceRange(); // Set the function's .length. @@ -302,14 +303,17 @@ Function *ESTreeIRGen::genGeneratorFunction( ESTree::isStrict(functionNode->strictness), /* insertBefore */ nullptr); - auto *innerFn = genES5Function( - genAnonymousLabelName(originalName.isValid() ? originalName.str() : ""), - lazyClosureAlias, - functionNode, - true); - { FunctionContext outerFnContext{this, outerFn, functionNode->getSemInfo()}; + + // Build the inner function. This must be done in the outerFnContext + // since it's lexically considered a child function. + auto *innerFn = genES5Function( + genAnonymousLabelName(originalName.isValid() ? originalName.str() : ""), + lazyClosureAlias, + functionNode, + true); + emitFunctionPrologue( functionNode, Builder.createBasicBlock(outerFn),
test/BCGen/HBC/es6/generator.js+1 −1 modified@@ -74,7 +74,7 @@ function *args() { // CHECK-NEXT: CreateGenerator r1, r0, 4 // CHECK-NEXT: Ret r1 -// CHECK-LABEL: Function<?anon_1_args>(1 params, 7 registers, 0 symbols): +// CHECK-LABEL: Function<?anon_0_args>(1 params, 7 registers, 0 symbols): // CHECK-NEXT: Offset in debug table: source 0x{{.*}}, lexical 0x0000 // CHECK-NEXT: StartGenerator // CHECK-NEXT: CreateEnvironment r0
test/IRGen/es6/generator.js+12 −12 modified@@ -47,11 +47,11 @@ function *useResult() { //CHECK-NEXT:frame = [x] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StoreFrameInst undefined : undefined, [x] -//CHECK-NEXT: %1 = CreateGeneratorInst %?anon_1_useResult() +//CHECK-NEXT: %1 = CreateGeneratorInst %?anon_0_useResult() //CHECK-NEXT: %2 = ReturnInst %1 : object //CHECK-NEXT:function_end -//CHECK-LABEL:function ?anon_1_useResult() +//CHECK-LABEL:function ?anon_0_useResult() //CHECK-NEXT:frame = [x] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StartGeneratorInst @@ -86,11 +86,11 @@ function *loop(x) { //CHECK-NEXT:frame = [i] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StoreFrameInst undefined : undefined, [i] -//CHECK-NEXT: %1 = CreateGeneratorInst %?anon_2_loop() +//CHECK-NEXT: %1 = CreateGeneratorInst %?anon_0_loop() //CHECK-NEXT: %2 = ReturnInst %1 : object //CHECK-NEXT:function_end -//CHECK-LABEL:function ?anon_2_loop(x) +//CHECK-LABEL:function ?anon_0_loop(x) //CHECK-NEXT:frame = [i, x] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StartGeneratorInst @@ -139,11 +139,11 @@ var simple2 = function*() { //CHECK-LABEL:function simple2() //CHECK-NEXT:frame = [] //CHECK-NEXT:%BB0: -//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_4_simple2() +//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_0_simple2() //CHECK-NEXT: %1 = ReturnInst %0 : object //CHECK-NEXT:function_end -//CHECK-LABEL:function ?anon_4_simple2() +//CHECK-LABEL:function ?anon_0_simple2() //CHECK-NEXT:frame = [] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StartGeneratorInst @@ -172,11 +172,11 @@ var yieldStar = function*() { //CHECK-LABEL:function yieldStar() //CHECK-NEXT:frame = [] //CHECK-NEXT:%BB0: -//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_5_yieldStar() +//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_0_yieldStar() //CHECK-NEXT: %1 = ReturnInst %0 : object //CHECK-NEXT:function_end -//CHECK-LABEL:function ?anon_5_yieldStar() +//CHECK-LABEL:function ?anon_0_yieldStar() //CHECK-NEXT:frame = [] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StartGeneratorInst @@ -281,13 +281,13 @@ var destr = function*([x]) { //CHECK-LABEL:function destr() //CHECK-NEXT:frame = [] //CHECK-NEXT:%BB0: -//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_6_destr() +//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_0_destr() //CHECK-NEXT: %1 = LoadPropertyInst %0 : object, "next" : string //CHECK-NEXT: %2 = CallInst %1, %0 : object //CHECK-NEXT: %3 = ReturnInst %0 : object //CHECK-NEXT:function_end -//CHECK-LABEL:function ?anon_6_destr(?anon_2_param) +//CHECK-LABEL:function ?anon_0_destr(?anon_2_param) //CHECK-NEXT:frame = [x] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StartGeneratorInst @@ -355,13 +355,13 @@ var initializer = function*(x = foo()) { //CHECK-LABEL:function initializer() //CHECK-NEXT:frame = [] //CHECK-NEXT:%BB0: -//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_7_initializer() +//CHECK-NEXT: %0 = CreateGeneratorInst %?anon_0_initializer() //CHECK-NEXT: %1 = LoadPropertyInst %0 : object, "next" : string //CHECK-NEXT: %2 = CallInst %1, %0 : object //CHECK-NEXT: %3 = ReturnInst %0 : object //CHECK-NEXT:function_end -//CHECK-LABEL:function ?anon_7_initializer(x) +//CHECK-LABEL:function ?anon_0_initializer(x) //CHECK-NEXT:frame = [x] //CHECK-NEXT:%BB0: //CHECK-NEXT: %0 = StartGeneratorInst
test/IRGen/lazy-function-in-generator.js+28 −0 added@@ -0,0 +1,28 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: %hermes -lazy %s | %FileCheck %s --match-full-lines + +// Make sure we can correctly resolve scopes through lazily compiled +// functions in lazily compiled generators. +function f() { + var f_var = 10; + function* g() { + var g_var = 32; + function h() { + /* Some text to pad out the function so that it won't be eagerly compiled + * for being too short. Lorem ipsum dolor sit amet, consectetur adipiscing + * elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + */ + return f_var + g_var; + } + yield h(); + } + return g().next().value; +} +// CHECK: 42 +print(f());
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-pf27-929j-9pmmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-1912ghsaADVISORY
- github.com/facebook/hermes/commit/091835377369c8fd5917d9b87acffa721ad2a168ghsax_refsource_CONFIRMWEB
- www.facebook.com/security/advisories/cve-2020-1912ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.