CVE-2022-32234 (C++, published 2022-10-11T01:15Z)
Description: value: An out of bounds write in hermes, while handling large arrays, prior to commit 06eaec767e376bfdb883d912cb15e987ddf2bda1 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: nan (CVSS v3 base score: 9.8)
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:
external/llvh/include/llvh/ADT/SmallVector.h
external/llvh/lib/Support/SmallVector.cpp
external/llvh/patches/SmallVector.patch
Commit message: Re-sync with internal repository (#772)

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

Vulnerable code:
  if (MinSize > UINT32_MAX)
    report_bad_alloc_error("SmallVector capacity overflow during allocation");

  size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2));
  NewCapacity = std::min(std::max(NewCapacity, MinSize), size_t(UINT32_MAX));
  // Ensure we can fit the new capacity in 32 bits.
  if (MinCapacity > UINT32_MAX)
    report_bad_alloc_error("SmallVector capacity overflow during allocation");
  size_t NewCapacity = 2 * capacity() + 1; // Always grow.
  NewCapacity =
      std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));

Fixed code:
  constexpr size_t MinGrowth = 2;
  size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + MinGrowth));
  NewCapacity = static_cast<unsigned>(std::max(NewCapacity, MinSize));
  // Ensure that NewCapacity did not overflow an unsigned int,
  // and that the capacity in bytes will not overflow a size_t.
  if (NewCapacity <= this->capacity() ||
      NewCapacity < MinSize ||
      NewCapacity > size_t(-1) / sizeof(T))
    report_bad_alloc_error("SmallVector capacity overflow during allocation");
  constexpr size_t MinGrowth = 1;
  size_t NewCapacity = 2 * capacity() + MinGrowth; // Always grow.
  NewCapacity = static_cast<unsigned>(std::max(NewCapacity, MinCapacity));
  // Ensure that NewCapacity did not overflow an unsigned int,
  // and that the capacity in bytes will not overflow a size_t.
  if (NewCapacity <= this->capacity() ||
      NewCapacity < MinCapacity ||
      NewCapacity > size_t(-1) / TSize)
    report_bad_alloc_error("SmallVector capacity overflow during allocation");
diff --git a/xplat/hermes/external/llvh/include/llvh/ADT/SmallVector.h b/xplat/hermes/external/llvh/include/llvh/ADT/SmallVector.h
--- a/xplat/hermes/external/llvh/include/llvh/ADT/SmallVector.h
@@ -238,12 +238,16 @@
 // Define this out-of-line to dissuade the C++ compiler from inlining it.
 template <typename T, bool isPodLike>
 void SmallVectorTemplateBase<T, isPodLike>::grow(size_t MinSize) {
-  if (MinSize > UINT32_MAX)
-    report_bad_alloc_error("SmallVector capacity overflow during allocation");
-
   // Always grow, even from zero.
-  size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2));
-  NewCapacity = std::min(std::max(NewCapacity, MinSize), size_t(UINT32_MAX));
+  constexpr size_t MinGrowth = 2;
+  size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + MinGrowth));
+  NewCapacity = static_cast<unsigned>(std::max(NewCapacity, MinSize));
+  // Ensure that NewCapacity did not overflow an unsigned int,
+  // and that the capacity in bytes will not overflow a size_t.
+  if (NewCapacity <= this->capacity() ||
+      NewCapacity < MinSize ||
+      NewCapacity > size_t(-1) / sizeof(T))
+    report_bad_alloc_error("SmallVector capacity overflow during allocation");
   T *NewElts = static_cast<T*>(llvh::safe_malloc(NewCapacity*sizeof(T)));
 
   // Move the elements over.
diff --git a/xplat/hermes/external/llvh/lib/Support/SmallVector.cpp b/xplat/hermes/external/llvh/lib/Support/SmallVector.cpp
--- a/xplat/hermes/external/llvh/lib/Support/SmallVector.cpp
@@ -42,13 +42,16 @@
 /// on POD-like datatypes and is out of line to reduce code duplication.
 void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity,
                                size_t TSize) {
-  // Ensure we can fit the new capacity in 32 bits.
-  if (MinCapacity > UINT32_MAX)
-    report_bad_alloc_error("SmallVector capacity overflow during allocation");
+  constexpr size_t MinGrowth = 1;
+  size_t NewCapacity = 2 * capacity() + MinGrowth; // Always grow.
+  NewCapacity = static_cast<unsigned>(std::max(NewCapacity, MinCapacity));
 
-  size_t NewCapacity = 2 * capacity() + 1; // Always grow.
-  NewCapacity =
-      std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));
+  // Ensure that NewCapacity did not overflow an unsigned int,
+  // and that the capacity in bytes will not overflow a size_t.
+  if (NewCapacity <= this->capacity() ||
+      NewCapacity < MinCapacity ||
+      NewCapacity > size_t(-1) / TSize)
+    report_bad_alloc_error("SmallVector capacity overflow during allocation");
 
   void *NewElts;
   if (BeginX == FirstEl) {