CVE-2020-1912 (C++, published 2020-09-09T19:15Z)
Description: value: 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.
Severity: MEDIUM (CVSS v3 base score: 8.1)
Weakness: CWE-787 \u2014 Out-of-bounds Write
The product writes data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/facebook/hermes
Files changed:
include/hermes/IR/IR.h
include/hermes/IRGen/IRGen.h
lib/BCGen/HBC/BytecodeGenerator.cpp
lib/IRGen/ESTreeIRGen-func.cpp
lib/IRGen/ESTreeIRGen.cpp
test/BCGen/HBC/es6/generator.js
test/IRGen/es6/generator.js
test/IRGen/lazy-function-in-generator.js
Commit message: Correctly restore whether or not a function is an inner generator

Summary:
If a generator was large enough to be lazily compiled, we would lose
that information when reconstituting the function's context. This meant
the function was generated as a regular function instead of a generator.

#utd-hermes-ignore-android

Reviewed By: tmikov

Differential Revision: D23580247

fbshipit-source-id: af5628bf322cbdc7c7cdfbb5f8d0756328518ea1


Vulnerable code:
  // The type of node (such as a FunctionDeclaration or FunctionExpression).
  auto *innerFn = genES5Function(
      genAnonymousLabelName(originalName.isValid() ? originalName.str() : ""),
      lazyClosureAlias,
      functionNode,
      true);

  auto *func = genES5Function(lazyData->originalName, parentVar, node);
// CHECK-LABEL: Function<?anon_1_args>(1 params, 7 registers, 0 symbols):
//CHECK-NEXT:  %1 = CreateGeneratorInst %?anon_1_useResult()
//CHECK-LABEL:function ?anon_1_useResult()
//CHECK-NEXT:  %1 = CreateGeneratorInst %?anon_2_loop()
//CHECK-LABEL:function ?anon_2_loop(x)
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_4_simple2()
//CHECK-LABEL:function ?anon_4_simple2()
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_5_yieldStar()
//CHECK-LABEL:function ?anon_5_yieldStar()
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_6_destr()
//CHECK-LABEL:function ?anon_6_destr(?anon_2_param)
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_7_initializer()
//CHECK-LABEL:function ?anon_7_initializer(x)

Fixed code:
  /// The type of node (such as a FunctionDeclaration or FunctionExpression).
  /// Whether or not this is the inner function of a generator
  bool isGeneratorInnerFunction;
  /// Whether or not this is the inner function of a generator.
  bool isGeneratorInnerFunction;

      lazyData->isGeneratorInnerFunction =
          F->getLazySource().isGeneratorInnerFunction;
      lazySource.isGeneratorInnerFunction = isGeneratorInnerFunction;

    // 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);

  auto *func = genES5Function(
      lazyData->originalName,
      parentVar,
      node,
      lazyData->isGeneratorInnerFunction);
// CHECK-LABEL: Function<?anon_0_args>(1 params, 7 registers, 0 symbols):
//CHECK-NEXT:  %1 = CreateGeneratorInst %?anon_0_useResult()
//CHECK-LABEL:function ?anon_0_useResult()
//CHECK-NEXT:  %1 = CreateGeneratorInst %?anon_0_loop()
//CHECK-LABEL:function ?anon_0_loop(x)
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_0_simple2()
//CHECK-LABEL:function ?anon_0_simple2()
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_0_yieldStar()
//CHECK-LABEL:function ?anon_0_yieldStar()
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_0_destr()
//CHECK-LABEL:function ?anon_0_destr(?anon_2_param)
//CHECK-NEXT:  %0 = CreateGeneratorInst %?anon_0_initializer()
//CHECK-LABEL:function ?anon_0_initializer(x)
/**
 * 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());