Fix deallocating never-allocated storage in vector.merge()

If merge() is called on an empty vector, then priv_merge_in_new_buffer() will
call deallocate() with size 0 on the old (not-yet-allocated) vector storage.
This violates the Allocator requirement that the pointer passed to deallocate()
must have come from a call to allocate() with the same size.

Fix this by checking old_cap against 0, and also add a unit test for this bug.
This commit is contained in:
Roland Dreier
2019-02-23 14:12:10 -08:00
parent 81d78dbefa
commit 1eb591e85c
2 changed files with 50 additions and 1 deletions

View File

@@ -2373,7 +2373,9 @@ private:
pointer const old_p = this->m_holder.start();
size_type const old_cap = this->m_holder.capacity();
boost::container::destroy_alloc_n(a, boost::movelib::to_raw_pointer(old_p), old_size);
this->m_holder.deallocate(old_p, old_cap);
if (old_cap > 0) {
this->m_holder.deallocate(old_p, old_cap);
}
this->m_holder.m_size = old_size + added;
this->m_holder.start(new_storage);
this->m_holder.capacity(new_cap);

View File

@@ -152,6 +152,47 @@ struct alloc_propagate_base<boost_container_vector>
}}} //namespace boost::container::test
template<typename T>
class check_dealloc_allocator : public std::allocator<T>
{
public:
bool allocate_zero_called_;
bool deallocate_called_without_allocate_;
check_dealloc_allocator()
: std::allocator<T>()
, allocate_zero_called_(false)
, deallocate_called_without_allocate_(false)
{}
T* allocate(std::size_t n)
{
if (n == 0) {
allocate_zero_called_ = true;
}
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, std::size_t n)
{
if (n == 0 && !allocate_zero_called_) {
deallocate_called_without_allocate_ = true;
}
return std::allocator<T>::deallocate(p, n);
}
};
bool test_merge_empty_free()
{
vector<int> source;
source.emplace_back(1);
vector< int, check_dealloc_allocator<int> > empty;
empty.merge(source.begin(), source.end());
return empty.get_stored_allocator().deallocate_called_without_allocate_;
}
int main()
{
{
@@ -285,5 +326,11 @@ int main()
}
}
#endif
if (test_merge_empty_free()) {
std::cerr << "Merge into empty vector test failed" << std::endl;
return 1;
}
return 0;
}