From d9b44b91050ec93523eccb4870e4c969460d9548 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Tue, 14 Apr 2015 21:08:02 +0200 Subject: [PATCH] Avoid calling memcpy with NULL pointers According to the C/C++ standards, calling `memcpy(NULL, NULL, 0)` is undefined behaviour. Recent GCC versions may rely on this by optimizing NULL pointer checks more aggressively, see [1]. This patch tries to avoid calling std::memcpy with zero elements. As a side effect, explicitly return NULL when requesting an empty block from MemoryPoolAllocator::Malloc. This may be related to #301. [1] https://gcc.gnu.org/gcc-4.9/porting_to.html --- include/rapidjson/allocators.h | 7 ++++++- include/rapidjson/document.h | 22 ++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h index 7b348b6b0..d3dcb1221 100644 --- a/include/rapidjson/allocators.h +++ b/include/rapidjson/allocators.h @@ -160,6 +160,9 @@ class MemoryPoolAllocator { //! Allocates a memory block. (concept Allocator) void* Malloc(size_t size) { + if (!size) + return NULL; + size = RAPIDJSON_ALIGN(size); if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); @@ -191,7 +194,9 @@ class MemoryPoolAllocator { // Realloc process: allocate and copy memory, do not free original buffer. void* newBuffer = Malloc(newSize); RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. - return std::memcpy(newBuffer, originalPtr, originalSize); + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; } //! Frees a memory block (concept Allocator) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 204e3bde1..3bee5d7b2 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1582,17 +1582,27 @@ class GenericValue { // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { flags_ = kArrayFlag; - data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); - std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); - data_.a.size = data_.a.capacity = count; + if (!values) { + data_.a.elements = NULL; + data_.a.size = data_.a.capacity = 0u; + } else { + data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + data_.a.size = data_.a.capacity = count; + } } //! Initialize this value as object with initial data, without calling destructor. void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { flags_ = kObjectFlag; - data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); - std::memcpy(data_.o.members, members, count * sizeof(Member)); - data_.o.size = data_.o.capacity = count; + if (!members) { + data_.o.members = NULL; + data_.o.size = data_.o.capacity = 0u; + } else { + data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); + std::memcpy(data_.o.members, members, count * sizeof(Member)); + data_.o.size = data_.o.capacity = count; + } } //! Initialize this value as constant string, without calling destructor.