VYPR
High severityNVD Advisory· Published Sep 9, 2020· Updated Aug 4, 2024

CVE-2020-1912

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.

PackageAffected versionsPatched versions
hermes-enginenpm
< 0.5.20.5.2

Affected products

2
  • ghsa-coords
    Range: < 0.5.2
  • Facebook/Hermesv5
    Range: commit prior to 091835377369c8fd5917d9b87acffa721ad2a168

Patches

1
091835377369

Correctly restore whether or not a function is an inner generator

https://github.com/facebook/hermesWill HolenSep 8, 2020via ghsa
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

News mentions

0

No linked articles in our index yet.