Skip to content

Commit a30e7ea

Browse files
committed
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
1 parent 7375212 commit a30e7ea

File tree

2 files changed

+14
-0
lines changed

2 files changed

+14
-0
lines changed

Diff for: llvm/include/llvm/ADT/SmallVector.h

+7
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class SmallVectorBase {
4646

4747
/// This is an implementation of the grow() method which only works
4848
/// on POD-like data types and is out of line to reduce code duplication.
49+
/// This function will report a fatal error if it cannot increase capacity.
4950
void grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize);
5051

5152
public:
@@ -234,6 +235,12 @@ void SmallVectorTemplateBase<T, TriviallyCopyable>::grow(size_t MinSize) {
234235
if (MinSize > UINT32_MAX)
235236
report_bad_alloc_error("SmallVector capacity overflow during allocation");
236237

238+
// Ensure we can meet the guarantee of space for at least one more element.
239+
// The above check alone will not catch the case where grow is called with a
240+
// default MinCapacity of 0, but the current capacity cannot be increased.
241+
if (this->capacity() == size_t(UINT32_MAX))
242+
report_bad_alloc_error("SmallVector capacity unable to grow");
243+
237244
// Always grow, even from zero.
238245
size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2));
239246
NewCapacity = std::min(std::max(NewCapacity, MinSize), size_t(UINT32_MAX));

Diff for: llvm/lib/Support/SmallVector.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,19 @@ static_assert(sizeof(SmallVector<void *, 1>) ==
3939

4040
/// grow_pod - This is an implementation of the grow() method which only works
4141
/// on POD-like datatypes and is out of line to reduce code duplication.
42+
/// This function will report a fatal error if it cannot increase capacity.
4243
void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity,
4344
size_t TSize) {
4445
// Ensure we can fit the new capacity in 32 bits.
4546
if (MinCapacity > UINT32_MAX)
4647
report_bad_alloc_error("SmallVector capacity overflow during allocation");
4748

49+
// Ensure we can meet the guarantee of space for at least one more element.
50+
// The above check alone will not catch the case where grow is called with a
51+
// default MinCapacity of 0, but the current capacity cannot be increased.
52+
if (capacity() == size_t(UINT32_MAX))
53+
report_bad_alloc_error("SmallVector capacity unable to grow");
54+
4855
size_t NewCapacity = 2 * capacity() + 1; // Always grow.
4956
NewCapacity =
5057
std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));

0 commit comments

Comments
 (0)