pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/nodejs/node/commit/3cdb1cd437f63dd256ae2ab3b7e9016257326cb4

/> src,lib: refactor unsafe buffer creation to remove zero-fill toggle · nodejs/node@3cdb1cd · GitHub
Skip to content

Commit 3cdb1cd

Browse files
ChALkeRRafaelGSS
authored andcommitted
src,lib: refactor unsafe buffer creation to remove zero-fill toggle
This removes the zero-fill toggle mechanism that allowed JavaScript to control ArrayBuffer initialization via shared memory. Instead, unsafe buffer creation now uses a dedicated C++ API. Refs: https://hackerone.com/reports/3405778 Co-Authored-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com> PR-URL: nodejs-private/node-private#759 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> CVE-ID: CVE-2025-55131
1 parent 97595f7 commit 3cdb1cd

4 files changed

Lines changed: 62 additions & 80 deletions

File tree

lib/internal/buffer.js

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const {
3030
hexWrite,
3131
ucs2Write,
3232
utf8WriteStatic,
33-
getZeroFillToggle,
33+
createUnsafeArrayBuffer,
3434
} = internalBinding('buffer');
3535

3636
const {
@@ -39,13 +39,6 @@ const {
3939
},
4040
} = internalBinding('util');
4141

42-
const {
43-
namespace: {
44-
isBuildingSnapshot,
45-
},
46-
addAfterUserSerializeCallback,
47-
} = require('internal/v8/startup_snapshot');
48-
4942
// Temporary buffers to convert numbers.
5043
const float32Array = new Float32Array(1);
5144
const uInt8Float32Array = new Uint8Array(float32Array.buffer);
@@ -1086,28 +1079,14 @@ function isMarkedAsUntransferable(obj) {
10861079
return obj[untransferable_object_private_symbol] !== undefined;
10871080
}
10881081

1089-
// A toggle used to access the zero fill setting of the array buffer allocator
1090-
// in C++.
1091-
// |zeroFill| can be undefined when running inside an isolate where we
1092-
// do not own the ArrayBuffer allocator. Zero fill is always on in that case.
1093-
let zeroFill;
10941082
function createUnsafeBuffer(size) {
1095-
if (!zeroFill) {
1096-
zeroFill = getZeroFillToggle();
1097-
if (isBuildingSnapshot()) {
1098-
// Reset the toggle so that after serialization, we'll re-create a real
1099-
// toggle connected to the C++ one via getZeroFillToggle().
1100-
addAfterUserSerializeCallback(() => {
1101-
zeroFill = undefined;
1102-
});
1103-
}
1104-
}
1105-
zeroFill[0] = 0;
1106-
try {
1083+
if (size <= 64) {
1084+
// Allocated in heap, doesn't call backing store anyway
1085+
// This is the same that the old impl did implicitly, but explicit now
11071086
return new FastBuffer(size);
1108-
} finally {
1109-
zeroFill[0] = 1;
11101087
}
1088+
1089+
return new FastBuffer(createUnsafeArrayBuffer(size));
11111090
}
11121091

11131092
module.exports = {

src/api/environment.cc

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,8 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
113113

114114
void* NodeArrayBufferAllocator::Allocate(size_t size) {
115115
void* ret;
116-
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) {
117-
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled");
118-
ret = allocator_->Allocate(size);
119-
} else {
120-
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.Uninitialized");
121-
ret = allocator_->AllocateUninitialized(size);
122-
}
116+
COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled");
117+
ret = allocator_->Allocate(size);
123118
if (ret != nullptr) [[likely]] {
124119
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
125120
}

src/node_buffer.cc

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ using v8::Object;
8080
using v8::SharedArrayBuffer;
8181
using v8::String;
8282
using v8::Uint32;
83-
using v8::Uint32Array;
8483
using v8::Uint8Array;
8584
using v8::Value;
8685

@@ -1244,45 +1243,6 @@ void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) {
12441243
realm->set_buffer_prototype_object(proto);
12451244
}
12461245

1247-
void GetZeroFillToggle(const FunctionCallbackInfo<Value>& args) {
1248-
Environment* env = Environment::GetCurrent(args);
1249-
NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator();
1250-
Local<ArrayBuffer> ab;
1251-
// It can be a nullptr when running inside an isolate where we
1252-
// do not own the ArrayBuffer allocator.
1253-
if (allocator == nullptr || env->isolate_data()->is_building_snapshot()) {
1254-
// Create a dummy Uint32Array - the JS land can only toggle the C++ land
1255-
// setting when the allocator uses our toggle. With this the toggle in JS
1256-
// land results in no-ops.
1257-
// When building a snapshot, just use a dummy toggle as well to avoid
1258-
// introducing the dynamic external reference. We'll re-initialize the
1259-
// toggle with a real one connected to the C++ allocator after snapshot
1260-
// deserialization.
1261-
1262-
ab = ArrayBuffer::New(env->isolate(), sizeof(uint32_t));
1263-
} else {
1264-
// TODO(joyeecheung): save ab->GetBackingStore()->Data() in the Node.js
1265-
// array buffer allocator and include it into the C++ toggle while the
1266-
// Environment is still alive.
1267-
uint32_t* zero_fill_field = allocator->zero_fill_field();
1268-
std::unique_ptr<BackingStore> backing =
1269-
ArrayBuffer::NewBackingStore(zero_fill_field,
1270-
sizeof(*zero_fill_field),
1271-
[](void*, size_t, void*) {},
1272-
nullptr);
1273-
ab = ArrayBuffer::New(env->isolate(), std::move(backing));
1274-
}
1275-
1276-
if (ab->SetPrivate(env->context(),
1277-
env->untransferable_object_private_symbol(),
1278-
True(env->isolate()))
1279-
.IsNothing()) {
1280-
return;
1281-
}
1282-
1283-
args.GetReturnValue().Set(Uint32Array::New(ab, 0, 1));
1284-
}
1285-
12861246
static void Btoa(const FunctionCallbackInfo<Value>& args) {
12871247
CHECK_EQ(args.Length(), 1);
12881248
Environment* env = Environment::GetCurrent(args);
@@ -1449,6 +1409,57 @@ void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) {
14491409
memcpy(dest, src, bytes_to_copy);
14501410
}
14511411

1412+
// Converts a number parameter to size_t suitable for ArrayBuffer sizes
1413+
// Could be larger than uint32_t
1414+
// See v8::internal::TryNumberToSize and v8::internal::NumberToSize
1415+
inline size_t CheckNumberToSize(Local<Value> number) {
1416+
CHECK(number->IsNumber());
1417+
double value = number.As<Number>()->Value();
1418+
// See v8::internal::TryNumberToSize on this (and on < comparison)
1419+
double maxSize = static_cast<double>(std::numeric_limits<size_t>::max());
1420+
CHECK(value >= 0 && value < maxSize);
1421+
size_t size = static_cast<size_t>(value);
1422+
#ifdef V8_ENABLE_SANDBOX
1423+
CHECK_LE(size, kMaxSafeBufferSizeForSandbox);
1424+
#endif
1425+
return size;
1426+
}
1427+
1428+
void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
1429+
Environment* env = Environment::GetCurrent(args);
1430+
if (args.Length() != 1) {
1431+
env->ThrowRangeError("Invalid array buffer length");
1432+
return;
1433+
}
1434+
1435+
size_t size = CheckNumberToSize(args[0]);
1436+
1437+
Isolate* isolate = env->isolate();
1438+
1439+
Local<ArrayBuffer> buf;
1440+
1441+
// 0-length, or zero-fill flag is set, or building snapshot
1442+
if (size == 0 || per_process::cli_options->zero_fill_all_buffers ||
1443+
env->isolate_data()->is_building_snapshot()) {
1444+
buf = ArrayBuffer::New(isolate, size);
1445+
} else {
1446+
std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
1447+
isolate,
1448+
size,
1449+
BackingStoreInitializationMode::kUninitialized,
1450+
v8::BackingStoreOnFailureMode::kReturnNull);
1451+
1452+
if (!store) {
1453+
env->ThrowRangeError("Array buffer allocation failed");
1454+
return;
1455+
}
1456+
1457+
buf = ArrayBuffer::New(isolate, std::move(store));
1458+
}
1459+
1460+
args.GetReturnValue().Set(buf);
1461+
}
1462+
14521463
template <encoding encoding>
14531464
uint32_t WriteOneByteString(const char* src,
14541465
uint32_t src_len,
@@ -1576,6 +1587,8 @@ void Initialize(Local<Object> target,
15761587
SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString);
15771588

15781589
SetMethod(context, target, "copyArrayBuffer", CopyArrayBuffer);
1590+
SetMethodNoSideEffect(
1591+
context, target, "createUnsafeArrayBuffer", CreateUnsafeArrayBuffer);
15791592

15801593
SetMethod(context, target, "swap16", Swap16);
15811594
SetMethod(context, target, "swap32", Swap32);
@@ -1625,8 +1638,6 @@ void Initialize(Local<Object> target,
16251638
"utf8WriteStatic",
16261639
SlowWriteString<UTF8>,
16271640
&fast_write_string_utf8);
1628-
1629-
SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
16301641
}
16311642

16321643
} // anonymous namespace
@@ -1675,9 +1686,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16751686
registry->Register(StringWrite<HEX>);
16761687
registry->Register(StringWrite<UCS2>);
16771688
registry->Register(StringWrite<UTF8>);
1678-
registry->Register(GetZeroFillToggle);
16791689

16801690
registry->Register(CopyArrayBuffer);
1691+
registry->Register(CreateUnsafeArrayBuffer);
16811692

16821693
registry->Register(Atob);
16831694
registry->Register(Btoa);

src/node_internals.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ v8::MaybeLocal<v8::Object> InitializePrivateSymbols(
124124

125125
class NodeArrayBufferAllocator : public ArrayBufferAllocator {
126126
public:
127-
inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
128-
129127
void* Allocate(size_t size) override; // Defined in src/node.cc
130128
void* AllocateUninitialized(size_t size) override;
131129
void Free(void* data, size_t size) override;
@@ -142,7 +140,6 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
142140
}
143141

144142
private:
145-
uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
146143
std::atomic<size_t> total_mem_usage_ {0};
147144

148145
// Delegate to V8's allocator for compatibility with the V8 memory cage.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy