Skip to content

Commit cd3d626

Browse files
committed
buffer: disallow ArrayBuffer transfer on pooled buffer
1 parent e1fc3dc commit cd3d626

File tree

5 files changed

+39
-0
lines changed

5 files changed

+39
-0
lines changed

deps/v8/src/api/api.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8829,6 +8829,7 @@ void v8::ArrayBuffer::SetDetachKey(v8::Local<v8::Value> key) {
88298829
auto obj = Utils::OpenDirectHandle(this);
88308830
auto i_key = Utils::OpenDirectHandle(*key);
88318831
obj->set_detach_key(*i_key);
8832+
obj->set_is_detachable(false);
88328833
}
88338834

88348835
size_t v8::ArrayBuffer::ByteLength() const {

lib/buffer.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ const {
129129
createUnsafeBuffer,
130130
} = require('internal/buffer');
131131

132+
const {
133+
namespace: {
134+
addDeserializeCallback,
135+
isBuildingSnapshot,
136+
},
137+
} = require('internal/v8/startup_snapshot');
138+
132139
FastBuffer.prototype.constructor = Buffer;
133140
Buffer.prototype = FastBuffer.prototype;
134141
addBufferPrototypeMethods(Buffer.prototype);
@@ -159,6 +166,13 @@ function createPool() {
159166
poolOffset = 0;
160167
}
161168
createPool();
169+
if (isBuildingSnapshot()) {
170+
addDeserializeCallback(() => {
171+
// TODO(legendecas): ArrayBuffer.[[ArrayBufferDetachKey]] is not been serialized.
172+
// Remove this callback when snapshot serialization supports it.
173+
createPool();
174+
});
175+
}
162176

163177
function alignPool() {
164178
// Ensure aligned slices

lib/internal/buffer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const {
3131
ucs2Write,
3232
utf8WriteStatic,
3333
getZeroFillToggle,
34+
setDetachKey,
3435
} = internalBinding('buffer');
3536

3637
const {
@@ -45,6 +46,7 @@ const {
4546
},
4647
addAfterUserSerializeCallback,
4748
} = require('internal/v8/startup_snapshot');
49+
const { isArrayBuffer } = require('util/types');
4850

4951
// Temporary buffers to convert numbers.
5052
const float32Array = new Float32Array(1);
@@ -1075,6 +1077,10 @@ function markAsUntransferable(obj) {
10751077
if ((typeof obj !== 'object' && typeof obj !== 'function') || obj === null)
10761078
return; // This object is a primitive and therefore already untransferable.
10771079
obj[untransferable_object_private_symbol] = true;
1080+
1081+
if (isArrayBuffer(obj)) {
1082+
setDetachKey(obj, Symbol('unique_detach_key_for_untransferable_arraybuffer'));
1083+
}
10781084
}
10791085

10801086
// This simply checks if the object is marked as untransferable and doesn't

src/node_buffer.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,15 @@ static void Atob(const FunctionCallbackInfo<Value>& args) {
13941394
args.GetReturnValue().Set(error_code);
13951395
}
13961396

1397+
static void SetDetachKey(const FunctionCallbackInfo<Value>& args) {
1398+
CHECK_EQ(args.Length(), 2);
1399+
CHECK(args[0]->IsArrayBuffer());
1400+
1401+
Local<ArrayBuffer> ab = args[0].As<ArrayBuffer>();
1402+
Local<Value> key = args[1];
1403+
ab->SetDetachKey(key);
1404+
}
1405+
13971406
namespace {
13981407

13991408
std::pair<void*, size_t> DecomposeBufferToParts(Local<Value> buffer) {
@@ -1627,6 +1636,7 @@ void Initialize(Local<Object> target,
16271636
&fast_write_string_utf8);
16281637

16291638
SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
1639+
SetMethod(context, target, "setDetachKey", SetDetachKey);
16301640
}
16311641

16321642
} // anonymous namespace
@@ -1681,6 +1691,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16811691

16821692
registry->Register(Atob);
16831693
registry->Register(Btoa);
1694+
1695+
registry->Register(SetDetachKey);
16841696
}
16851697

16861698
} // namespace Buffer

test/parallel/test-buffer-pool-untransferable.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,9 @@ assert.throws(() => port1.postMessage(a, [ a.buffer ]), {
2121
// Verify that the pool ArrayBuffer has not actually been transferred:
2222
assert.strictEqual(a.buffer, b.buffer);
2323
assert.strictEqual(a.length, length);
24+
25+
// Verify that ArrayBuffer.prototype.transfer() also throws.
26+
assert.throws(() => a.buffer.transfer(), {
27+
name: 'TypeError',
28+
});
29+
assert.strictEqual(a.buffer, b.buffer);

0 commit comments

Comments
 (0)