CVE-2022-32234: Out-of-bounds Write (CWE-787)
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.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2022-10-11T01:15Z
Repository: https://github.com/facebook/hermes
Language: C++
Files changed: external/llvh/include/llvh/ADT/SmallVector.h, external/llvh/lib/Support/SmallVector.cpp, external/llvh/patches/SmallVector.patch
Commit date: 2022-07-06 16:51:56 -0700
Commit message: Re-sync with internal repository (#772)

Co-authored-by: Facebook Community Bot <6422482+facebook-github-bot@users.noreply.github.com>
Diff: diff --git a/external/llvh/include/llvh/ADT/SmallVector.h b/external/llvh/include/llvh/ADT/SmallVector.h
index a035d6e43..596ddf2d6 100644
--- a/external/llvh/include/llvh/ADT/SmallVector.h
+++ b/external/llvh/include/llvh/ADT/SmallVector.h
@@ -236,16 +236,20 @@ public:
 };
 
 // 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.
   this->uninitialized_move(this->begin(), this->end(), NewElts);
 
diff --git a/external/llvh/lib/Support/SmallVector.cpp b/external/llvh/lib/Support/SmallVector.cpp
index 3e0b31d0b..6b1f993c0 100644
--- a/external/llvh/lib/Support/SmallVector.cpp
+++ b/external/llvh/lib/Support/SmallVector.cpp
@@ -40,17 +40,20 @@ static_assert(sizeof(SmallVector<void *, 1>) ==
 
 /// grow_pod - This is an implementation of the grow() method which only works
 /// 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) {
     NewElts = safe_malloc(NewCapacity * TSize);
 
diff --git a/external/llvh/patches/SmallVector.patch b/external/llvh/patches/SmallVector.patch
new file mode 100644
index 000000000..a3bfa9794
--- /dev/null
+++ b/external/llvh/patches/SmallVector.patch
@@ -0,0 +1,51 @@
+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
++++ b/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
++++ b/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) {

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) {