[llvm] a30e7ea - Make SmallVector assert if it cannot grow.

Andrew Browne via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 21 18:05:57 PDT 2020


Author: Andrew Browne
Date: 2020-04-21T17:53:39-07:00
New Revision: a30e7ea88e75568feed020aedae73c52de888835

URL: https://github.com/llvm/llvm-project/commit/a30e7ea88e75568feed020aedae73c52de888835
DIFF: https://github.com/llvm/llvm-project/commit/a30e7ea88e75568feed020aedae73c52de888835.diff

LOG: Make SmallVector assert if it cannot grow.

Context:

  /// Double the size of the allocated memory, guaranteeing space for at
  /// least one more element or MinSize if specified.
  void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); }

  void push_back(const T &Elt) {
    if (LLVM_UNLIKELY(this->size() >= this->capacity()))
      this->grow();
    memcpy(reinterpret_cast<void *>(this->end()), &Elt, sizeof(T));
    this->set_size(this->size() + 1);
  }

When grow is called in push_back() without a MinSize specified, this is
relying on the guarantee of space for at least one more element.

There is an edge case bug where the SmallVector is already at its maximum size
and push_back() calls grow() with default MinSize of zero. Grow is unable to
provide space for one more element, but push_back() assumes the additional
element it will be available. This can result in silent memory corruption, as
this->end() will be an invalid pointer and the program may continue executing.

Another alternative to fix would be to remove the default argument from
grow(), which would mean several changing grow() to grow(this->size()+1)
in several places.

No test case added because it would require allocating ~4GB.

Reviewers: echristo

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D77601

Added: 
    

Modified: 
    llvm/include/llvm/ADT/SmallVector.h
    llvm/lib/Support/SmallVector.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h
index 28b514d530dc..12a58761955b 100644
--- a/llvm/include/llvm/ADT/SmallVector.h
+++ b/llvm/include/llvm/ADT/SmallVector.h
@@ -46,6 +46,7 @@ class SmallVectorBase {
 
   /// This is an implementation of the grow() method which only works
   /// on POD-like data types and is out of line to reduce code duplication.
+  /// This function will report a fatal error if it cannot increase capacity.
   void grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize);
 
 public:
@@ -234,6 +235,12 @@ void SmallVectorTemplateBase<T, TriviallyCopyable>::grow(size_t MinSize) {
   if (MinSize > UINT32_MAX)
     report_bad_alloc_error("SmallVector capacity overflow during allocation");
 
+  // Ensure we can meet the guarantee of space for at least one more element.
+  // The above check alone will not catch the case where grow is called with a
+  // default MinCapacity of 0, but the current capacity cannot be increased.
+  if (this->capacity() == size_t(UINT32_MAX))
+    report_bad_alloc_error("SmallVector capacity unable to grow");
+
   // 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));

diff  --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp
index 36f0a81f6b00..9ece0c5a3b62 100644
--- a/llvm/lib/Support/SmallVector.cpp
+++ b/llvm/lib/Support/SmallVector.cpp
@@ -39,12 +39,19 @@ 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.
+/// This function will report a fatal error if it cannot increase capacity.
 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");
 
+  // Ensure we can meet the guarantee of space for at least one more element.
+  // The above check alone will not catch the case where grow is called with a
+  // default MinCapacity of 0, but the current capacity cannot be increased.
+  if (capacity() == size_t(UINT32_MAX))
+    report_bad_alloc_error("SmallVector capacity unable to grow");
+
   size_t NewCapacity = 2 * capacity() + 1; // Always grow.
   NewCapacity =
       std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));


        


More information about the llvm-commits mailing list