CVE-2022-40138 (C++, published 2022-10-11T02:15Z)
Description: value: An integer conversion error in Hermes bytecode generation, prior to commit 6aa825e480d48127b480b08d13adf70033237097, could have been used to perform Out-Of-Bounds operations and subsequently execute arbitrary code. Note that this is only exploitable in cases where Hermes is used to execute untrusted JavaScript. Hence, most React Native applications are not affected.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-681 \u2014 Incorrect Conversion between Numeric Types
When converting from one data type to another, such as long to integer, data can be omitted or translated in a way that produces unexpected values. If the resulting values are used in a sensitive context, then dangerous behaviors may occur.
Repository: https://github.com/facebook/hermes
Files changed:
include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h
include/hermes/CompilerDriver/CompilerDriver.h
include/hermes/VM/CodeBlock.h
lib/BCGen/HBC/BytecodeGenerator.cpp
lib/BCGen/HBC/BytecodeProviderFromSrc.cpp
lib/BCGen/HBC/HBC.cpp
lib/BCGen/HBC/ISel.cpp
lib/CompilerDriver/CompilerDriver.cpp
lib/VM/Callable.cpp
lib/VM/CodeBlock.cpp
lib/VM/Debugger/Debugger.cpp
lib/VM/Interpreter.cpp
lib/VM/Operations.cpp
test/hermes/far-environment-access-eval.js
test/hermes/far-environment-access.js
Commit message: Re-sync with internal repository (#822)

Co-authored-by: Facebook Community Bot <6422482+facebook-github-bot@users.noreply.github.com>

Vulnerable code:
#define DEFINE_OPERAND_TYPE(name, ctype)          \
  void emit##name(param_t value) {                \
    assert(                                       \
        (((param_t)(ctype)value) == value ||      \
         std::is_floating_point<ctype>::value) && \
        "Value does not fit in " #ctype);         \
    emitOperand(value, sizeof(ctype));            \
  void lazyCompileImpl(Runtime &runtime);
  void lazyCompile(Runtime &runtime) {
      lazyCompileImpl(runtime);
  void lazyCompile(Runtime &) {}
  auto bytecode = createBCProviderFromSrc(
      hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts));
    result.bytecodeProvider = hbc::BCProviderFromSrc::createBCProviderFromSrc(
        hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), genOptions));
    selfHandle->getCodeBlock(runtime)->lazyCompile(runtime);
void CodeBlock::lazyCompileImpl(Runtime &runtime) {
        codeBlock->lazyCompile(runtime_);
  newCodeBlock->lazyCompile(*this);
        CAPTURE_IP(calleeBlock->lazyCompile(runtime));
        CAPTURE_IP(calleeBlock->lazyCompile(runtime));
    cb->lazyCompile(runtime);

Fixed code:
  /// A flag indicating that an encoding error happened, in which case the
  /// bytecode is wrong and shouldn't be executed.
  bool encodingError_{};

  /// Returns whether an encoding error happened during bytecode emission.
  bool hasEncodingError() const {
    return encodingError_;
  }

#define DEFINE_OPERAND_TYPE(name, ctype)                  \
  void emit##name(param_t value) {                        \
    encodingError_ |= ((param_t)(ctype)value) != value && \
        !std::is_floating_point<ctype>::value;            \
    emitOperand(value, sizeof(ctype));                    \
  /// An error occured in the backend during/after IR lowering.
  BackendError,
  ExecutionStatus lazyCompileImpl(Runtime &runtime);
  ExecutionStatus lazyCompile(Runtime &runtime) {
      return lazyCompileImpl(runtime);
    return ExecutionStatus::RETURNED;
  ExecutionStatus lazyCompile(Runtime &) {
    return ExecutionStatus::RETURNED;
  }
  assert(
      !BFG->hasEncodingError() && "Error should have been reported already.");
  auto BM = hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts);
  if (context->getSourceErrorManager().getErrorCount() > 0) {
    return {nullptr, getErrorString()};
  }
  auto bytecode = createBCProviderFromSrc(std::move(BM));
    if (funcGen->hasEncodingError()) {
      M->getContext().getSourceErrorManager().error(
          F.getSourceRange().Start, "Error encoding bytecode");
      return nullptr;
    }
  if (!BM) {
    return {};
  }

  if (std::numeric_limits<uint8_t>::max() < delta) {
    F_->getContext().getSourceErrorManager().error(
        Inst->getLocation(), "Variable environment is out-of-reach");
  }
    auto BM =
        hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), genOptions);
    if (auto N = context->getSourceErrorManager().getErrorCount()) {
      llvh::errs() << "Emitted " << N << " errors in the backend. exiting.\n";
      return BackendError;
    }
    result.bytecodeProvider =
        hbc::BCProviderFromSrc::createBCProviderFromSrc(std::move(BM));
    if (auto N = M.getContext().getSourceErrorManager().getErrorCount()) {
      llvh::errs() << "Emitted " << N << " errors in the backend. exiting.\n";
      return BackendError;
    }

    if (LLVM_UNLIKELY(
            selfHandle->getCodeBlock(runtime)->lazyCompile(runtime) ==
            ExecutionStatus::EXCEPTION)) {
      return ExecutionStatus::EXCEPTION;
    }
#include "hermes/Support/SimpleDiagHandler.h"
ExecutionStatus CodeBlock::lazyCompileImpl(Runtime &runtime) {
  SourceErrorManager &manager = lazyData->context->getSourceErrorManager();
  SimpleDiagHandlerRAII outputManager{manager};
  if (manager.getErrorCount()) {
    // Raise a SyntaxError to be consistent with eval().
    return runtime.raiseSyntaxError(
        llvh::StringRef{outputManager.getErrorString()});
  }

  assert(bcModule && "No errors, yet no bcModule");


  return ExecutionStatus::RETURNED;
        if (LLVM_UNLIKELY(
                codeBlock->lazyCompile(runtime_) ==
                ExecutionStatus::EXCEPTION)) {
          // TODO: how to better handle this?
          runtime_.clearThrownValue();
        }
  if (LLVM_UNLIKELY(
          newCodeBlock->lazyCompile(*this) == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
        CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime));
        if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
          goto exception;
        }
        CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime));
        if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
          goto exception;
        }
    if (LLVM_UNLIKELY(cb->lazyCompile(runtime) == ExecutionStatus::EXCEPTION)) {
      return ExecutionStatus::EXCEPTION;
    }
/**
 * Copyright (c) Meta Platforms, Inc. and 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 -O -target=HBC %s 2>&1 | %FileCheck --match-full-lines %s
// RUN: %hermes -O -target=HBC %s -lazy 2>&1 | %FileCheck --match-full-lines %s
// XFAIL: windows
// UNSUPPORTED: ubsan

// Hermes has a 256 limit on the number of scopes nesting. This test ensures
// that the compiler will handle applications that require more scopes
// gracefully -- i.e., it won't crash, nor emit bad bytecode.

try {
    eval('"use strict";\n' +
         '\n' +
         'function sink(x) { return x; }\n' +
         'function foo(){\n' +
         '  var var0 = sink("1");\n' +
         '  var var1 = sink("2");\n' +
         '  var var2 = sink("3");\n' +
         '  return ()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=> [var0, var1, var2];\n' +
         '}\n' +
         '\n' +
         'print(foo()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()());\n')
} catch (e) {
  print(e);
}

// CHECK: SyntaxError: {{[0-9]+}}:{{[0-9]+}}:Variable environment is out-of-reach
/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// RUN: (%hermesc -O -target=HBC %s --dump-bytecode 2>&1 || true) | %FileCheck --match-full-lines %s
// RUN: (%hermes -O -target=HBC %s 2>&1 || true) | %FileCheck --match-full-lines %s
// RUN: %hermes -O -target=HBC %s -lazy 2>&1 | %FileCheck --match-full-lines %s --check-prefix=LAZY
// XFAIL: windows
// UNSUPPORTED: ubsan

// Hermes has a 256 limit on the number of scopes nesting. This test ensures
// that the compiler will handle applications that require more scopes
// gracefully -- i.e., it won't crash, nor emit bad bytecode.

"use strict";

function sink(x) { return x; }
function foo(){
  var var0 = sink("1");
  var var1 = sink("2");
  var var2 = sink("3");
  return ()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=>()=> [var0, var1, var2];
}

try {
  print(foo()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()());
} catch (e) {
  print(e);
}


// CHECK: {{.*}}far-environment-access.js:{{[0-9]+}}:{{[0-9]+}}: error: Variable environment is out-of-reach
// LAZY: SyntaxError: {{[0-9]+}}:{{[0-9]+}}:Variable environment is out-of-reach