Skip to content

Commit 238fa57

Browse files
committed
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 <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Backport-PR-URL: #26302 Reviewed-By: Michaël Zasso <[email protected]>
1 parent 20dc172 commit 238fa57

File tree

4 files changed

+103
-1
lines changed

4 files changed

+103
-1
lines changed

src/api/environment.cc

+75-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,82 @@ void* ArrayBufferAllocator::Allocate(size_t size) {
7575
return UncheckedMalloc(size);
7676
}
7777

78+
DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
79+
CHECK(allocations_.empty());
80+
}
81+
82+
void* DebuggingArrayBufferAllocator::Allocate(size_t size) {
83+
Mutex::ScopedLock lock(mutex_);
84+
void* data = ArrayBufferAllocator::Allocate(size);
85+
RegisterPointerInternal(data, size);
86+
return data;
87+
}
88+
89+
void* DebuggingArrayBufferAllocator::AllocateUninitialized(size_t size) {
90+
Mutex::ScopedLock lock(mutex_);
91+
void* data = ArrayBufferAllocator::AllocateUninitialized(size);
92+
RegisterPointerInternal(data, size);
93+
return data;
94+
}
95+
96+
void DebuggingArrayBufferAllocator::Free(void* data, size_t size) {
97+
Mutex::ScopedLock lock(mutex_);
98+
UnregisterPointerInternal(data, size);
99+
ArrayBufferAllocator::Free(data, size);
100+
}
101+
102+
void* DebuggingArrayBufferAllocator::Reallocate(void* data,
103+
size_t old_size,
104+
size_t size) {
105+
Mutex::ScopedLock lock(mutex_);
106+
void* ret = ArrayBufferAllocator::Reallocate(data, old_size, size);
107+
if (ret == nullptr) {
108+
if (size == 0) // i.e. equivalent to free().
109+
UnregisterPointerInternal(data, old_size);
110+
return nullptr;
111+
}
112+
113+
if (data != nullptr) {
114+
auto it = allocations_.find(data);
115+
CHECK_NE(it, allocations_.end());
116+
allocations_.erase(it);
117+
}
118+
119+
RegisterPointerInternal(ret, size);
120+
return ret;
121+
}
122+
123+
void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
124+
Mutex::ScopedLock lock(mutex_);
125+
RegisterPointerInternal(data, size);
126+
}
127+
128+
void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
129+
Mutex::ScopedLock lock(mutex_);
130+
UnregisterPointerInternal(data, size);
131+
}
132+
133+
void DebuggingArrayBufferAllocator::UnregisterPointerInternal(void* data,
134+
size_t size) {
135+
if (data == nullptr) return;
136+
auto it = allocations_.find(data);
137+
CHECK_NE(it, allocations_.end());
138+
CHECK_EQ(it->second, size);
139+
allocations_.erase(it);
140+
}
141+
142+
void DebuggingArrayBufferAllocator::RegisterPointerInternal(void* data,
143+
size_t size) {
144+
if (data == nullptr) return;
145+
CHECK_EQ(allocations_.count(data), 0);
146+
allocations_[data] = size;
147+
}
148+
78149
ArrayBufferAllocator* CreateArrayBufferAllocator() {
79-
return new ArrayBufferAllocator();
150+
if (per_process::cli_options->debug_arraybuffer_allocations)
151+
return new DebuggingArrayBufferAllocator();
152+
else
153+
return new ArrayBufferAllocator();
80154
}
81155

82156
void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) {

src/node_internals.h

+23
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,34 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
109109
void* AllocateUninitialized(size_t size) override
110110
{ return node::UncheckedMalloc(size); }
111111
void Free(void* data, size_t) override { free(data); }
112+
virtual void* Reallocate(void* data, size_t old_size, size_t size) {
113+
return static_cast<void*>(
114+
UncheckedRealloc<char>(static_cast<char*>(data), size));
115+
}
116+
virtual void RegisterPointer(void* data, size_t size) {}
117+
virtual void UnregisterPointer(void* data, size_t size) {}
112118

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

123+
class DebuggingArrayBufferAllocator final : public ArrayBufferAllocator {
124+
public:
125+
~DebuggingArrayBufferAllocator() override;
126+
void* Allocate(size_t size) override;
127+
void* AllocateUninitialized(size_t size) override;
128+
void Free(void* data, size_t size) override;
129+
void* Reallocate(void* data, size_t old_size, size_t size) override;
130+
void RegisterPointer(void* data, size_t size) override;
131+
void UnregisterPointer(void* data, size_t size) override;
132+
133+
private:
134+
void RegisterPointerInternal(void* data, size_t size);
135+
void UnregisterPointerInternal(void* data, size_t size);
136+
Mutex mutex_;
137+
std::unordered_map<void*, size_t> allocations_;
138+
};
139+
117140
namespace Buffer {
118141
v8::MaybeLocal<v8::Object> Copy(Environment* env, const char* data, size_t len);
119142
v8::MaybeLocal<v8::Object> New(Environment* env, size_t size);

src/node_options.cc

+4
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@ PerProcessOptionsParser::PerProcessOptionsParser() {
369369
"SlowBuffer instances",
370370
&PerProcessOptions::zero_fill_all_buffers,
371371
kAllowedInEnvironment);
372+
AddOption("--debug-arraybuffer-allocations",
373+
"", /* undocumented, only for debugging */
374+
&PerProcessOptions::debug_arraybuffer_allocations,
375+
kAllowedInEnvironment);
372376

373377
AddOption("--security-reverts", "", &PerProcessOptions::security_reverts);
374378
AddOption("--completion-bash",

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class PerProcessOptions : public Options {
168168
uint64_t max_http_header_size = 8 * 1024;
169169
int64_t v8_thread_pool_size = 4;
170170
bool zero_fill_all_buffers = false;
171+
bool debug_arraybuffer_allocations = false;
171172

172173
std::vector<std::string> security_reverts;
173174
bool print_bash_completion = false;

0 commit comments

Comments
 (0)