Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
src: add debugging array allocator
Add a subclass of `ArrayBufferAllocator` that performs additional
debug checking, which in particular verifies that:

- All `ArrayBuffer` backing stores have been allocated with this
  allocator, or have been explicitly marked as coming from a
  compatible source.
- All memory allocated by the allocator has been freed once it is
  destroyed.

PR-URL: #26207
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
  • Loading branch information
addaleax committed Feb 28, 2019
commit 86fdd46d2139369da3c0514c8b78f01fbcd22706
76 changes: 75 additions & 1 deletion src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,82 @@ void* ArrayBufferAllocator::Allocate(size_t size) {
return UncheckedMalloc(size);
}

DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
CHECK(allocations_.empty());
}

void* DebuggingArrayBufferAllocator::Allocate(size_t size) {
Mutex::ScopedLock lock(mutex_);
void* data = ArrayBufferAllocator::Allocate(size);
RegisterPointerInternal(data, size);
return data;
}

void* DebuggingArrayBufferAllocator::AllocateUninitialized(size_t size) {
Mutex::ScopedLock lock(mutex_);
void* data = ArrayBufferAllocator::AllocateUninitialized(size);
RegisterPointerInternal(data, size);
return data;
}

void DebuggingArrayBufferAllocator::Free(void* data, size_t size) {
Mutex::ScopedLock lock(mutex_);
UnregisterPointerInternal(data, size);
ArrayBufferAllocator::Free(data, size);
}

void* DebuggingArrayBufferAllocator::Reallocate(void* data,
size_t old_size,
size_t size) {
Mutex::ScopedLock lock(mutex_);
void* ret = ArrayBufferAllocator::Reallocate(data, old_size, size);
if (ret == nullptr) {
if (size == 0) // i.e. equivalent to free().
UnregisterPointerInternal(data, old_size);
return nullptr;
}

if (data != nullptr) {
auto it = allocations_.find(data);
CHECK_NE(it, allocations_.end());
allocations_.erase(it);
}

RegisterPointerInternal(ret, size);
return ret;
}

void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
Mutex::ScopedLock lock(mutex_);
RegisterPointerInternal(data, size);
}

void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
Mutex::ScopedLock lock(mutex_);
UnregisterPointerInternal(data, size);
}

void DebuggingArrayBufferAllocator::UnregisterPointerInternal(void* data,
size_t size) {
if (data == nullptr) return;
auto it = allocations_.find(data);
CHECK_NE(it, allocations_.end());
CHECK_EQ(it->second, size);
allocations_.erase(it);
}

void DebuggingArrayBufferAllocator::RegisterPointerInternal(void* data,
size_t size) {
if (data == nullptr) return;
CHECK_EQ(allocations_.count(data), 0);
allocations_[data] = size;
}

ArrayBufferAllocator* CreateArrayBufferAllocator() {
return new ArrayBufferAllocator();
if (per_process::cli_options->debug_arraybuffer_allocations)
return new DebuggingArrayBufferAllocator();
else
return new ArrayBufferAllocator();
}

void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) {
Expand Down
23 changes: 23 additions & 0 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,34 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
void* AllocateUninitialized(size_t size) override
{ return node::UncheckedMalloc(size); }
void Free(void* data, size_t) override { free(data); }
virtual void* Reallocate(void* data, size_t old_size, size_t size) {
return static_cast<void*>(
UncheckedRealloc<char>(static_cast<char*>(data), size));
}
virtual void RegisterPointer(void* data, size_t size) {}
virtual void UnregisterPointer(void* data, size_t size) {}

private:
uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
};

class DebuggingArrayBufferAllocator final : public ArrayBufferAllocator {
public:
~DebuggingArrayBufferAllocator() override;
void* Allocate(size_t size) override;
void* AllocateUninitialized(size_t size) override;
void Free(void* data, size_t size) override;
void* Reallocate(void* data, size_t old_size, size_t size) override;
void RegisterPointer(void* data, size_t size) override;
void UnregisterPointer(void* data, size_t size) override;

private:
void RegisterPointerInternal(void* data, size_t size);
void UnregisterPointerInternal(void* data, size_t size);
Mutex mutex_;
std::unordered_map<void*, size_t> allocations_;
};

namespace Buffer {
v8::MaybeLocal<v8::Object> Copy(Environment* env, const char* data, size_t len);
v8::MaybeLocal<v8::Object> New(Environment* env, size_t size);
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ PerProcessOptionsParser::PerProcessOptionsParser() {
"SlowBuffer instances",
&PerProcessOptions::zero_fill_all_buffers,
kAllowedInEnvironment);
AddOption("--debug-arraybuffer-allocations",
"", /* undocumented, only for debugging */
&PerProcessOptions::debug_arraybuffer_allocations,
kAllowedInEnvironment);

AddOption("--security-reverts", "", &PerProcessOptions::security_reverts);
AddOption("--completion-bash",
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class PerProcessOptions : public Options {
uint64_t max_http_header_size = 8 * 1024;
int64_t v8_thread_pool_size = 4;
bool zero_fill_all_buffers = false;
bool debug_arraybuffer_allocations = false;

std::vector<std::string> security_reverts;
bool print_bash_completion = false;
Expand Down