Commit 050c1c3d authored by Tudor Bosman's avatar Tudor Bosman

Check the return value from malloc / realloc.

Summary:
https://github.com/facebook/folly/issues/7

Wrappers: checkedMalloc / checkedRealloc / checkedCalloc

Test Plan: all folly tests

FB internal diff: D486841
parent e1822c55
...@@ -193,9 +193,7 @@ struct ArenaAllocatorTraits { ...@@ -193,9 +193,7 @@ struct ArenaAllocatorTraits {
class SysAlloc { class SysAlloc {
public: public:
void* allocate(size_t size) { void* allocate(size_t size) {
void* mem = malloc(size); return checkedMalloc(size);
if (!mem) throw std::bad_alloc();
return mem;
} }
void deallocate(void* p) { void deallocate(void* p) {
......
...@@ -301,7 +301,7 @@ public: ...@@ -301,7 +301,7 @@ public:
// one extra Char for the null terminator. // one extra Char for the null terminator.
auto const allocSize = auto const allocSize =
goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char)); goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));
ml_.data_ = static_cast<Char*>(malloc(allocSize)); ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
fbstring_detail::pod_copy(rhs.ml_.data_, fbstring_detail::pod_copy(rhs.ml_.data_,
// 1 for terminator // 1 for terminator
rhs.ml_.data_ + rhs.ml_.size_ + 1, rhs.ml_.data_ + rhs.ml_.size_ + 1,
...@@ -364,7 +364,7 @@ public: ...@@ -364,7 +364,7 @@ public:
// Medium strings are allocated normally. Don't forget to // Medium strings are allocated normally. Don't forget to
// allocate one extra Char for the terminating null. // allocate one extra Char for the terminating null.
auto const allocSize = goodMallocSize((1 + size) * sizeof(Char)); auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));
ml_.data_ = static_cast<Char*>(malloc(allocSize)); ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));
fbstring_detail::pod_copy(data, data + size, ml_.data_); fbstring_detail::pod_copy(data, data + size, ml_.data_);
ml_.size_ = size; ml_.size_ = size;
ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium; ml_.capacity_ = (allocSize / sizeof(Char) - 1) | isMedium;
...@@ -586,7 +586,7 @@ public: ...@@ -586,7 +586,7 @@ public:
// Don't forget to allocate one extra Char for the terminating null // Don't forget to allocate one extra Char for the terminating null
auto const allocSizeBytes = auto const allocSizeBytes =
goodMallocSize((1 + minCapacity) * sizeof(Char)); goodMallocSize((1 + minCapacity) * sizeof(Char));
auto const data = static_cast<Char*>(malloc(allocSizeBytes)); auto const data = static_cast<Char*>(checkedMalloc(allocSizeBytes));
auto const size = smallSize(); auto const size = smallSize();
fbstring_detail::pod_copy(small_, small_ + size + 1, data); fbstring_detail::pod_copy(small_, small_ + size + 1, data);
// No need for writeTerminator(), we wrote it above with + 1. // No need for writeTerminator(), we wrote it above with + 1.
...@@ -742,7 +742,7 @@ private: ...@@ -742,7 +742,7 @@ private:
// struct. // struct.
const size_t allocSize = goodMallocSize( const size_t allocSize = goodMallocSize(
sizeof(RefCounted) + *size * sizeof(Char)); sizeof(RefCounted) + *size * sizeof(Char));
auto result = static_cast<RefCounted*>(malloc(allocSize)); auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));
result->refCount_.store(1, std::memory_order_release); result->refCount_.store(1, std::memory_order_release);
*size = (allocSize - sizeof(RefCounted)) / sizeof(Char); *size = (allocSize - sizeof(RefCounted)) / sizeof(Char);
return result; return result;
...@@ -2184,7 +2184,7 @@ getline( ...@@ -2184,7 +2184,7 @@ getline(
for (;;) { for (;;) {
// This looks quadratic but it really depends on realloc // This looks quadratic but it really depends on realloc
auto const newSize = size + 128; auto const newSize = size + 128;
buf = static_cast<char*>(realloc(buf, newSize)); buf = static_cast<char*>(checkedRealloc(buf, newSize));
is.getline(buf + size, newSize - size, delim); is.getline(buf + size, newSize - size, delim);
if (is.bad() || is.eof() || !is.fail()) { if (is.bad() || is.eof() || !is.fail()) {
// done by either failure, end of file, or normal read // done by either failure, end of file, or normal read
......
...@@ -280,7 +280,7 @@ public: ...@@ -280,7 +280,7 @@ public:
} }
auto const nBytes = goodMallocSize(n * sizeof(T)); auto const nBytes = goodMallocSize(n * sizeof(T));
b_ = static_cast<T*>(malloc(nBytes)); b_ = static_cast<T*>(checkedMalloc(nBytes));
fbvector_detail::uninitializedFillDefaultOrFree(b_, n); fbvector_detail::uninitializedFillDefaultOrFree(b_, n);
e_ = b_ + n; e_ = b_ + n;
z_ = b_ + nBytes / sizeof(T); z_ = b_ + nBytes / sizeof(T);
...@@ -293,7 +293,7 @@ public: ...@@ -293,7 +293,7 @@ public:
} }
auto const nBytes = goodMallocSize(n * sizeof(T)); auto const nBytes = goodMallocSize(n * sizeof(T));
b_ = static_cast<T*>(malloc(nBytes)); b_ = static_cast<T*>(checkedMalloc(nBytes));
fbvector_detail::uninitializedFillOrFree(b_, n, value); fbvector_detail::uninitializedFillOrFree(b_, n, value);
e_ = b_ + n; e_ = b_ + n;
z_ = b_ + nBytes / sizeof(T); z_ = b_ + nBytes / sizeof(T);
...@@ -396,7 +396,7 @@ private: ...@@ -396,7 +396,7 @@ private:
// Must reallocate - just do it on the side // Must reallocate - just do it on the side
auto const nBytes = goodMallocSize(newSize * sizeof(T)); auto const nBytes = goodMallocSize(newSize * sizeof(T));
auto const b = static_cast<T*>(malloc(nBytes)); auto const b = static_cast<T*>(checkedMalloc(nBytes));
std::uninitialized_copy(first, last, b); std::uninitialized_copy(first, last, b);
this->fbvector::~fbvector(); this->fbvector::~fbvector();
b_ = b; b_ = b;
...@@ -590,7 +590,7 @@ private: ...@@ -590,7 +590,7 @@ private:
assert(crtCapacity < n); // reserve_in_place should have taken assert(crtCapacity < n); // reserve_in_place should have taken
// care of this // care of this
auto const newCapacityBytes = goodMallocSize(n * sizeof(T)); auto const newCapacityBytes = goodMallocSize(n * sizeof(T));
auto b = static_cast<T*>(malloc(newCapacityBytes)); auto b = static_cast<T*>(checkedMalloc(newCapacityBytes));
auto const oldSize = size(); auto const oldSize = size();
memcpy(b, b_, oldSize * sizeof(T)); memcpy(b, b_, oldSize * sizeof(T));
// Done with the old chunk. Free but don't call destructors! // Done with the old chunk. Free but don't call destructors!
......
...@@ -133,6 +133,28 @@ inline size_t goodMallocSize(size_t minSize) { ...@@ -133,6 +133,28 @@ inline size_t goodMallocSize(size_t minSize) {
// expanded in place, and this constant reflects that. // expanded in place, and this constant reflects that.
static const size_t jemallocMinInPlaceExpandable = 4096; static const size_t jemallocMinInPlaceExpandable = 4096;
/**
* Trivial wrappers around malloc, calloc, realloc that check for allocation
* failure and throw std::bad_alloc in that case.
*/
inline void* checkedMalloc(size_t size) {
void* p = malloc(size);
if (!p) throw std::bad_alloc();
return p;
}
inline void* checkedCalloc(size_t n, size_t size) {
void* p = calloc(n, size);
if (!p) throw std::bad_alloc();
return p;
}
inline void* checkedRealloc(void* ptr, size_t size) {
void* p = realloc(ptr, size);
if (!p) throw std::bad_alloc();
return p;
}
/** /**
* This function tries to reallocate a buffer of which only the first * This function tries to reallocate a buffer of which only the first
* currentSize bytes are used. The problem with using realloc is that * currentSize bytes are used. The problem with using realloc is that
...@@ -162,7 +184,7 @@ inline void* smartRealloc(void* p, ...@@ -162,7 +184,7 @@ inline void* smartRealloc(void* p,
return p; return p;
} }
// Cannot expand; must move // Cannot expand; must move
auto const result = malloc(newCapacity); auto const result = checkedMalloc(newCapacity);
std::memcpy(result, p, currentSize); std::memcpy(result, p, currentSize);
free(p); free(p);
return result; return result;
...@@ -172,13 +194,13 @@ inline void* smartRealloc(void* p, ...@@ -172,13 +194,13 @@ inline void* smartRealloc(void* p,
auto const slack = currentCapacity - currentSize; auto const slack = currentCapacity - currentSize;
if (slack * 2 > currentSize) { if (slack * 2 > currentSize) {
// Too much slack, malloc-copy-free cycle: // Too much slack, malloc-copy-free cycle:
auto const result = malloc(newCapacity); auto const result = checkedMalloc(newCapacity);
std::memcpy(result, p, currentSize); std::memcpy(result, p, currentSize);
free(p); free(p);
return result; return result;
} }
// If there's not too much slack, we realloc in hope of coalescing // If there's not too much slack, we realloc in hope of coalescing
return realloc(p, newCapacity); return checkedRealloc(p, newCapacity);
} }
#ifdef _LIBSTDCXX_FBSTRING #ifdef _LIBSTDCXX_FBSTRING
......
...@@ -994,10 +994,7 @@ private: ...@@ -994,10 +994,7 @@ private:
needBytes += kHeapifyCapacitySize; needBytes += kHeapifyCapacitySize;
} }
auto const sizeBytes = goodMallocSize(needBytes); auto const sizeBytes = goodMallocSize(needBytes);
void* newh = std::malloc(sizeBytes); void* newh = checkedMalloc(sizeBytes);
if (!newh) {
throw std::bad_alloc();
}
// We expect newh to be at least 2-aligned, because we want to // We expect newh to be at least 2-aligned, because we want to
// use its least significant bit as a flag. // use its least significant bit as a flag.
assert(!detail::pointerFlagGet(newh)); assert(!detail::pointerFlagGet(newh));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment