Skip to content

Commit 749c2fc

Browse files
committed
buffer: disallow ArrayBuffer transfer on pooled buffer
1 parent 2209d95 commit 749c2fc

File tree

4 files changed

+40
-0
lines changed

4 files changed

+40
-0
lines changed

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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const {
1515
ERR_OUT_OF_RANGE,
1616
} = require('internal/errors').codes;
1717
const { validateNumber } = require('internal/validators');
18+
const { isArrayBuffer } = require('util/types');
19+
1820
const {
1921
asciiSlice,
2022
base64Slice,
@@ -31,6 +33,7 @@ const {
3133
ucs2Write,
3234
utf8WriteStatic,
3335
createUnsafeArrayBuffer,
36+
setDetachKey,
3437
} = internalBinding('buffer');
3538

3639
const {
@@ -1068,6 +1071,10 @@ function markAsUntransferable(obj) {
10681071
if ((typeof obj !== 'object' && typeof obj !== 'function') || obj === null)
10691072
return; // This object is a primitive and therefore already untransferable.
10701073
obj[untransferable_object_private_symbol] = true;
1074+
1075+
if (isArrayBuffer(obj)) {
1076+
setDetachKey(obj, Symbol('unique_detach_key_for_untransferable_arraybuffer'));
1077+
}
10711078
}
10721079

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

src/node_buffer.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,15 @@ static void Atob(const FunctionCallbackInfo<Value>& args) {
13541354
args.GetReturnValue().Set(error_code);
13551355
}
13561356

1357+
static void SetDetachKey(const FunctionCallbackInfo<Value>& args) {
1358+
CHECK_EQ(args.Length(), 2);
1359+
CHECK(args[0]->IsArrayBuffer());
1360+
1361+
Local<ArrayBuffer> ab = args[0].As<ArrayBuffer>();
1362+
Local<Value> key = args[1];
1363+
ab->SetDetachKey(key);
1364+
}
1365+
13571366
namespace {
13581367

13591368
std::pair<void*, size_t> DecomposeBufferToParts(Local<Value> buffer) {
@@ -1638,6 +1647,8 @@ void Initialize(Local<Object> target,
16381647
"utf8WriteStatic",
16391648
SlowWriteString<UTF8>,
16401649
&fast_write_string_utf8);
1650+
1651+
SetMethod(context, target, "setDetachKey", SetDetachKey);
16411652
}
16421653

16431654
} // anonymous namespace
@@ -1692,6 +1703,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16921703

16931704
registry->Register(Atob);
16941705
registry->Register(Btoa);
1706+
1707+
registry->Register(SetDetachKey);
16951708
}
16961709

16971710
} // 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)