CVE-2022-40138: Incorrect Conversion between Numeric Types (CWE-681)
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.
CWE description: 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.
CVSS v3 base score: 9.8
Published: 2022-10-11T02:15Z
Repository: https://github.com/facebook/hermes
Language: C++
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 date: 2022-09-27 08:14:30 -0700
Commit message: Re-sync with internal repository (#822)

Co-authored-by: Facebook Community Bot <6422482+facebook-github-bot@users.noreply.github.com>
Diff: diff --git a/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h b/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h
index dd838aaa36..a7c7d60e6f 100644
--- a/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h
+++ b/include/hermes/BCGen/HBC/BytecodeInstructionGenerator.h
@@ -43,11 +43,20 @@ enum Operator {
 class BytecodeInstructionGenerator {
  protected:
   /// A list of opcodes.
   std::vector<opcode_atom_t> opcodes_{};
 
+  /// A flag indicating that an encoding error happened, in which case the
+  /// bytecode is wrong and shouldn't be executed.
+  bool encodingError_{};
+
  public:
+  /// Returns whether an encoding error happened during bytecode emission.
+  bool hasEncodingError() const {
+    return encodingError_;
+  }
+
   /// Returns the current location of the bytecode stream.
   offset_t getCurrentLocation() {
     return opcodes_.size();
   }
 
@@ -74,17 +83,15 @@ class BytecodeInstructionGenerator {
 // void emitReg8(param_t value) { ... }
 // ...
 // We also assert that the value can fit into ctype.
 // For integer values, ((param_t)(ctype)value) == value will do the job;
 // We also want doubles to pass the check unconditionally.
-#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));            \
+#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));                    \
   }
 #include "hermes/BCGen/HBC/BytecodeList.def"
 
 // Generate instruction emitters, such as:
 // void emitJmp(param_t t1) { ...; }
diff --git a/include/hermes/CompilerDriver/CompilerDriver.h b/include/hermes/CompilerDriver/CompilerDriver.h
index 30d6c40cbf..0e3719cc89 100644
--- a/include/hermes/CompilerDriver/CompilerDriver.h
+++ b/include/hermes/CompilerDriver/CompilerDriver.h
@@ -30,10 +30,12 @@ enum CompileStatus {
   InputFileError,
   /// An output file could not be written.
   OutputFileError,
   /// An error occured during optimization.
   OptimizationFailed,
+  /// An error occured in the backend during/after IR lowering.
+  BackendError,
 };
 
 /// Information about a bytecode file that is loaded into a buffer.
 struct BytecodeBufferInfo {
   /// True if the buffer is mmapped
diff --git a/include/hermes/VM/CodeBlock.h b/include/hermes/VM/CodeBlock.h
index a8d47903df..01d4893835 100644
--- a/include/hermes/VM/CodeBlock.h
+++ b/include/hermes/VM/CodeBlock.h
@@ -61,11 +61,11 @@ class CodeBlock final
   /// cache.
   const uint32_t writePropCacheOffset_;
 
 #ifndef HERMESVM_LEAN
   /// Compiles a lazy CodeBlock. Intended to be called from lazyCompile.
-  void lazyCompileImpl(Runtime &runtime);
+  ExecutionStatus lazyCompileImpl(Runtime &runtime);
 #endif
 
   /// Helper function for getting start and end locations.
   /// Given an SMLoc, returns the source coordinates of it in the lazy function.
   /// \param start if true, return the start coordinates, else end coordinates.
@@ -235,21 +235,24 @@ class CodeBlock final
     // null bytecode_ indicates that this is a lazy code block.
     return !bytecode_;
   }
 
   /// Compiles this CodeBlock, if it's lazy and not already compiled.
-  void lazyCompile(Runtime &runtime) {
+  ExecutionStatus lazyCompile(Runtime &runtime) {
     if (LLVM_UNLIKELY(isLazy())) {
-      lazyCompileImpl(runtime);
+      return lazyCompileImpl(runtime);
     }
+    return ExecutionStatus::RETURNED;
   }
 #else
   /// Checks whether this function is lazily compiled.
   bool isLazy() const {
     return false;
   }
-  void lazyCompile(Runtime &) {}
+  ExecutionStatus lazyCompile(Runtime &) {
+    return ExecutionStatus::RETURNED;
+  }
 #endif
 
   /// Get the start location of this function, if it's lazy.
   SourceErrorManager::SourceCoords getLazyFunctionStartLoc() const {
     return getLazyFunctionLoc(true);
diff --git a/lib/BCGen/HBC/BytecodeGenerator.cpp b/lib/BCGen/HBC/BytecodeGenerator.cpp
index 58124b9052..4d30144f80 100644
--- a/lib/BCGen/HBC/BytecodeGenerator.cpp
+++ b/lib/BCGen/HBC/BytecodeGenerator.cpp
@@ -215,10 +215,12 @@ void BytecodeModuleGenerator::setFunctionGenerator(
     Function *F,
     unique_ptr<BytecodeFunctionGenerator> BFG) {
   assert(
       functionGenerators_.find(F) == functionGenerators_.end() &&
       "Adding same function twice.");
+  assert(
+      !BFG->hasEncodingError() && "Error should have been reported already.");
   functionGenerators_[F] = std::move(BFG);
 }
 
 unsigned BytecodeModuleGenerator::getStringID(llvh::StringRef str) const {
   return stringTable_.getStringID(str);
diff --git a/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp b/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp
index 1262630baa..8553eaad75 100644
--- a/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp
+++ b/lib/BCGen/HBC/BytecodeProviderFromSrc.cpp
@@ -234,12 +234,15 @@ BCProviderFromSrc::createBCProviderFromSrcImpl(
   BytecodeGenerationOptions opts{compileFlags.format};
   opts.optimizationEnabled = !!runOptimizationPasses;
   opts.staticBuiltinsEnabled =
       context->getOptimizationSettings().staticBuiltins;
   opts.verifyIR = compileFlags.verifyIR;
-  auto bytecode = createBCProviderFromSrc(
-      hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts));
+  auto BM = hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts);
+  if (context->getSourceErrorManager().getErrorCount() > 0) {
+    return {nullptr, getErrorString()};
+  }
+  auto bytecode = createBCProviderFromSrc(std::move(BM));
   bytecode->singleFunction_ = isSingleFunctionExpression(parsed.getValue());
   return {std::move(bytecode), std::string{}};
 }
 
 BCProviderLazy::BCProviderLazy(hbc::BytecodeFunction *bytecodeFunction)
diff --git a/lib/BCGen/HBC/HBC.cpp b/lib/BCGen/HBC/HBC.cpp
index c8dc018a17..0c75f8c26d 100644
--- a/lib/BCGen/HBC/HBC.cpp
+++ b/lib/BCGen/HBC/HBC.cpp
@@ -311,10 +311,15 @@ std::unique_ptr<BytecodeModule> hbc::generateBytecodeModule(
       hbciSel.populateDebugCache(debugCache);
       hbciSel.generate(sourceMapGen);
       debugCache = hbciSel.getDebugCache();
     }
 
+    if (funcGen->hasEncodingError()) {
+      M->getContext().getSourceErrorManager().error(
+          F.getSourceRange().Start, "Error encoding bytecode");
+      return nullptr;
+    }
     BMGen.setFunctionGenerator(&F, std::move(funcGen));
   }
 
   return BMGen.generate();
 }
@@ -333,10 +338,14 @@ std::unique_ptr<BytecodeModule> hbc::generateBytecode(
       options,
       segment,
       sourceMapGen,
       std::move(baseBCProvider));
 
+  if (!BM) {
+    return {};
+  }
+
   if (options.format == OutputFormatKind::EmitBundle) {
     assert(BM != nullptr);
     BytecodeSerializer BS{OS, options};
     BS.serialize(*BM, sourceHash);
   }
diff --git a/lib/BCGen/HBC/ISel.cpp b/lib/BCGen/HBC/ISel.cpp
index 03968879cf..e1343ad7ef 100644
--- a/lib/BCGen/HBC/ISel.cpp
+++ b/lib/BCGen/HBC/ISel.cpp
@@ -1335,10 +1335,14 @@ void HBCISel::generateHBCResolveEnvironment(
   assert(
       curScopeDepth && curScopeDepth.getValue() >= instScopeDepth.getValue() &&
       "Cannot access variables in inner scopes");
   int32_t delta = curScopeDepth.getValue() - instScopeDepth.getValue();
   assert(delta > 0 && "HBCResolveEnvironment for current scope");
+  if (std::numeric_limits<uint8_t>::max() < delta) {
+    F_->getContext().getSourceErrorManager().error(
+        Inst->getLocation(), "Variable environment is out-of-reach");
+  }
   BCFGen_->emitGetEnvironment(encodeValue(Inst), delta - 1);
 }
 void HBCISel::generateHBCStoreToEnvironmentInst(
     HBCStoreToEnvironmentInst *Inst,
     BasicBlock *next) {
diff --git a/lib/CompilerDriver/CompilerDriver.cpp b/lib/CompilerDriver/CompilerDriver.cpp
index 5388be7b27..9cf24d6ff2 100644
--- a/lib/CompilerDriver/CompilerDriver.cpp
+++ b/lib/CompilerDriver/CompilerDriver.cpp
@@ -1692,13 +1692,19 @@ CompileResult generateBytecodeForExecution(
     Module &M,
     const BytecodeGenerationOptions &genOptions) {
   std::shared_ptr<Context> context = M.shareContext();
   CompileResult result{Success};
   if (cl::BytecodeFormat == cl::BytecodeFormatKind::HBC) {
-    result.bytecodeProvider = hbc::BCProviderFromSrc::createBCProviderFromSrc(
-        hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), genOptions));
+    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));
   } else {
     llvm_unreachable("Invalid bytecode kind for execution");
     result = InvalidFlags;
   }
   return result;
@@ -1733,10 +1739,15 @@ CompileResult generateBytecodeForSerialization(
         sourceHash,
         segment,
         sourceMapGenOrNull,
         std::move(baseBCProvider));
 
+    if (auto N = M.getContext().getSourceErrorManager().getErrorCount()) {
+      llvh::errs() << "Emitted " << N << " errors in the backend. exiting.\n";
+      return BackendError;
+    }
+
     if (cl::DumpTarget == DumpBytecode) {
       disassembleBytecode(hbc::BCProviderFromSrc::createBCProviderFromSrc(
           std::move(bytecodeModule)));
     }
   } else {
diff --git a/lib/VM/Callable.cpp b/lib/VM/Callable.cpp
index 25e1de85ea..500d423eb4 100644
--- a/lib/VM/Callable.cpp
+++ b/lib/VM/Callable.cpp
@@ -1393,11 +1393,15 @@ CallResult<PseudoHandle<>> GeneratorInnerFunction::callInnerFunction(
   // Force lazy compilation immediately in order to size the context properly.
   // We're about to call the function anyway, so this doesn't reduce laziness.
   // Note that this will do nothing after the very first time a lazy function
   // is called, so we only resize before we save any registers at all.
   if (LLVM_UNLIKELY(selfHandle->getCodeBlock(runtime)->isLazy())) {
-    selfHandle->getCodeBlock(runtime)->lazyCompile(runtime);
+    if (LLVM_UNLIKELY(
+            selfHandle->getCodeBlock(runtime)->lazyCompile(runtime) ==
+            ExecutionStatus::EXCEPTION)) {
+      return ExecutionStatus::EXCEPTION;
+    }
     if (LLVM_UNLIKELY(
             ArrayStorage::resize(
                 ctx,
                 runtime,
                 getContextSize(
diff --git a/lib/VM/CodeBlock.cpp b/lib/VM/CodeBlock.cpp
index d175dc81b4..b1204bf4fa 100644
--- a/lib/VM/CodeBlock.cpp
+++ b/lib/VM/CodeBlock.cpp
@@ -12,10 +12,11 @@
 #include "hermes/BCGen/HBC/Bytecode.h"
 #include "hermes/BCGen/HBC/HBC.h"
 #include "hermes/IRGen/IRGen.h"
 #include "hermes/Support/Conversions.h"
 #include "hermes/Support/PerfSection.h"
+#include "hermes/Support/SimpleDiagHandler.h"
 #include "hermes/VM/GCPointer-inline.h"
 #include "hermes/VM/Runtime.h"
 #include "hermes/VM/RuntimeModule.h"
 #include "hermes/VM/SerializedLiteralParser.h"
 
@@ -328,26 +329,38 @@ std::unique_ptr<hbc::BytecodeModule> compileLazyFunction(
 
   return bytecodeModule;
 }
 } // namespace
 
-void CodeBlock::lazyCompileImpl(Runtime &runtime) {
+ExecutionStatus CodeBlock::lazyCompileImpl(Runtime &runtime) {
   assert(isLazy() && "Laziness has not been checked");
   PerfSection perf("Lazy function compilation");
   auto *provider = (hbc::BCProviderLazy *)runtimeModule_->getBytecode();
   auto *func = provider->getBytecodeFunction();
   auto *lazyData = func->getLazyCompilationData();
+  SourceErrorManager &manager = lazyData->context->getSourceErrorManager();
+  SimpleDiagHandlerRAII outputManager{manager};
   auto bcModule = compileLazyFunction(lazyData);
 
+  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");
+
   runtimeModule_->initializeLazyMayAllocate(
       hbc::BCProviderFromSrc::createBCProviderFromSrc(std::move(bcModule)));
   // Reset all meta lazyData of the CodeBlock to point to the newly
   // generated bytecode module.
   functionID_ = runtimeModule_->getBytecode()->getGlobalFunctionIndex();
   functionHeader_ =
       runtimeModule_->getBytecode()->getFunctionHeader(functionID_);
   bytecode_ = runtimeModule_->getBytecode()->getBytecode(functionID_);
+
+  return ExecutionStatus::RETURNED;
 }
 #endif // HERMESVM_LEAN
 
 void CodeBlock::markCachedHiddenClasses(
     Runtime &runtime,
diff --git a/lib/VM/Debugger/Debugger.cpp b/lib/VM/Debugger/Debugger.cpp
index 869dbb76ac..3a37c336a0 100644
--- a/lib/VM/Debugger/Debugger.cpp
+++ b/lib/VM/Debugger/Debugger.cpp
@@ -1145,11 +1145,16 @@ bool Debugger::resolveBreakpointLocation(Breakpoint &breakpoint) const {
       if ((start.line < request.line && request.line < end.line) ||
           ((start.line == request.line || request.line == end.line) &&
            (start.col <= request.column && request.column <= end.col))) {
         // The code block probably contains the breakpoint we want to set.
         // First, we compile it.
-        codeBlock->lazyCompile(runtime_);
+        if (LLVM_UNLIKELY(
+                codeBlock->lazyCompile(runtime_) ==
+                ExecutionStatus::EXCEPTION)) {
+          // TODO: how to better handle this?
+          runtime_.clearThrownValue();
+        }
 
         // We've found the codeBlock at this level and expanded it,
         // so there's no point continuing the search.
         // Abandon the current toVisit queue and repopulate it.
         toVisit.clear();
diff --git a/lib/VM/Interpreter.cpp b/lib/VM/Interpreter.cpp
index 7f97b9e71c..dc415482ae 100644
--- a/lib/VM/Interpreter.cpp
+++ b/lib/VM/Interpreter.cpp
@@ -783,11 +783,14 @@ static inline const Inst *nextInstCall(const Inst *ip) {
   return IPADD(((callSizes >> (offset * W)) & mask) + minSize);
 }
 
 CallResult<HermesValue> Runtime::interpretFunctionImpl(
     CodeBlock *newCodeBlock) {
-  newCodeBlock->lazyCompile(*this);
+  if (LLVM_UNLIKELY(
+          newCodeBlock->lazyCompile(*this) == ExecutionStatus::EXCEPTION)) {
+    return ExecutionStatus::EXCEPTION;
+  }
 
 #if defined(HERMES_MEMORY_INSTRUMENTATION) || !defined(NDEBUG)
   // We always call getCurrentIP() in a debug build as this has the effect
   // of asserting the IP is correctly set (not invalidated) at this point.
   // This allows us to leverage our whole test-suite to find missing cases
@@ -1582,11 +1585,14 @@ tailCall:
 #ifdef HERMES_MEMORY_INSTRUMENTATION
         runtime.pushCallStack(curCodeBlock, ip);
 #endif
 
         CodeBlock *calleeBlock = func->getCodeBlock(runtime);
-        CAPTURE_IP(calleeBlock->lazyCompile(runtime));
+        CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime));
+        if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
+          goto exception;
+        }
         curCodeBlock = calleeBlock;
         CAPTURE_IP_SET();
         goto tailCall;
       }
       CAPTURE_IP(
@@ -1635,11 +1641,14 @@ tailCall:
 
         LLVM_DEBUG(dumpCallArguments(dbgs(), runtime, newFrame));
 
         assert(!SingleStep && "can't single-step a call");
 
-        CAPTURE_IP(calleeBlock->lazyCompile(runtime));
+        CAPTURE_IP_ASSIGN(auto res, calleeBlock->lazyCompile(runtime));
+        if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
+          goto exception;
+        }
         curCodeBlock = calleeBlock;
         CAPTURE_IP_SET();
         goto tailCall;
       }
 
diff --git a/lib/VM/Operations.cpp b/lib/VM/Operations.cpp
index 1d69bdca6f..f677bfe1de 100644
--- a/lib/VM/Operations.cpp
+++ b/lib/VM/Operations.cpp
@@ -1677,11 +1677,13 @@ CallResult<bool> isConstructor(Runtime &runtime, Callable *callable) {
   // If it is a bytecode function, check the flags.
   if (auto *func = dyn_vmcast<JSFunction>(callable)) {
     auto *cb = func->getCodeBlock(runtime);
     // Even though it doesn't make sense logically, we need to compile the
     // function in order to access it flags.
-    cb->lazyCompile(runtime);
+    if (LLVM_UNLIKELY(cb->lazyCompile(runtime) == ExecutionStatus::EXCEPTION)) {
+      return ExecutionStatus::EXCEPTION;
+    }
     return !func->getCodeBlock(runtime)->getHeaderFlags().isCallProhibited(
         true);
   }
 
   // We check for NativeFunction since those are defined to not be
diff --git a/test/hermes/far-environment-access-eval.js b/test/hermes/far-environment-access-eval.js
new file mode 100644
index 0000000000..1d72e84f3b
--- /dev/null
+++ b/test/hermes/far-environment-access-eval.js
@@ -0,0 +1,33 @@
+/**
+ * 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
diff --git a/test/hermes/far-environment-access.js b/test/hermes/far-environment-access.js
new file mode 100644
index 0000000000..da94f2eb3b
--- /dev/null
+++ b/test/hermes/far-environment-access.js
@@ -0,0 +1,36 @@
+/**
+ * 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

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