src: correctly report memory changes to V8

Call `V8::ExternalMemoryAccounter::Update` instead of
`V8::ExternalMemoryAccounter::Increase` to report memory difference to
V8

Calling `V8::ExternalMemoryAccounter::Increase` with a signed integer on
32-bit platforms causes instances where GC inside GC takes place leading
to a crash in certain cases.

During GC, native objects are destructed. In destructor for
`CompressionStream` class used by zlib, memory release information is
passed onto `V8::ExternalMemoryAccounter::Increase()` instead of
`V8::ExternalMemoryAccounter::Decrease()` which triggers V8's memory
limits, thus triggering GC inside GC which leads to crash.

Bug initially introduced in commit
1d5d7b6eedb2274c9ad48b5f378598a10479e4a7

For full report see https://hackerone.com/reports/3302484

PR-URL: https://github.com/nodejs/node/pull/59623
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Yaksh Bariya
2025-09-09 17:47:47 +05:30
committed by GitHub
parent 96a749b7b3
commit d58343ec48
2 changed files with 2 additions and 2 deletions

View File

@@ -59,7 +59,7 @@ void* NgLibMemoryManager<Class, T>::ReallocImpl(void* ptr,
// Environment*/Isolate* parameter and call the V8 method transparently.
const int64_t new_size = size - previous_size;
manager->IncreaseAllocatedSize(new_size);
manager->env()->external_memory_accounter()->Increase(
manager->env()->external_memory_accounter()->Update(
manager->env()->isolate(), new_size);
*reinterpret_cast<size_t*>(mem) = size;
mem += sizeof(size_t);

View File

@@ -644,7 +644,7 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork {
if (report == 0) return;
CHECK_IMPLIES(report < 0, zlib_memory_ >= static_cast<size_t>(-report));
zlib_memory_ += report;
AsyncWrap::env()->external_memory_accounter()->Increase(
AsyncWrap::env()->external_memory_accounter()->Update(
AsyncWrap::env()->isolate(), report);
}