From e209dbe103886d75ab6a88af026fc0b1b3805c76 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 22 May 2026 19:35:11 +0200 Subject: [PATCH 1/2] src: remove TOCTOU race condition when encoding SAB-backed `Buffer`s Signed-off-by: Antoine du Hamel --- src/encoding_binding.cc | 15 ++++++++++++++- src/node_buffer.cc | 23 ++++++++++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc index c569375383e8d9..ba8566bc036338 100644 --- a/src/encoding_binding.cc +++ b/src/encoding_binding.cc @@ -418,8 +418,9 @@ void BindingData::DecodeUTF8(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); // list, flags CHECK_GE(args.Length(), 1); + auto isShared = args[0]->IsSharedArrayBuffer(); - if (!(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() || + if (!(args[0]->IsArrayBuffer() || isShared || args[0]->IsArrayBufferView())) { return node::THROW_ERR_INVALID_ARG_TYPE( env->isolate(), @@ -427,6 +428,11 @@ void BindingData::DecodeUTF8(const FunctionCallbackInfo& args) { "ArrayBuffer or ArrayBufferView."); } + if (args[0]->IsArrayBufferView()) { + Local view = args[0].As(); + isShared = view->Buffer()->IsSharedArrayBuffer(); + } + ArrayBufferViewContents buffer(args[0]); bool ignore_bom = args[1]->IsTrue(); @@ -435,6 +441,13 @@ void BindingData::DecodeUTF8(const FunctionCallbackInfo& args) { const char* data = buffer.data(); size_t length = buffer.length(); + std::unique_ptr data_copy; + if (isShared && length != 0) { + data_copy.reset(new char[length]); + memcpy(data_copy.get(), data, length); + data = data_copy.get(); + } + if (!ignore_bom && length >= 3) { if (memcmp(data, "\xEF\xBB\xBF", 3) == 0) { data += 3; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 2778422ea4e7b7..ae522981f90899 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -567,19 +567,32 @@ void StringSlice(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); ArrayBufferViewContents buffer(args[0]); - if (buffer.length() == 0) + auto buffer_length = buffer.length(); + const char* data_ptr = buffer.data(); + + Local view = args[0].As(); + + if (buffer_length == 0) return args.GetReturnValue().SetEmptyString(); size_t start = 0; size_t end = 0; THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], 0, &start)); - THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], buffer.length(), &end)); - if (end < start) end = start; - THROW_AND_RETURN_IF_OOB(Just(end <= buffer.length())); + THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], buffer_length, &end)); + if (end <= start) return args.GetReturnValue().SetEmptyString(); + THROW_AND_RETURN_IF_OOB(Just(end <= buffer_length)); size_t length = end - start; + std::unique_ptr data_copy; + if (view->Buffer()->IsSharedArrayBuffer()) { + data_copy.reset(new char[length]); + memcpy(data_copy.get(), data_ptr + start, length); + data_ptr = data_copy.get(); + start = 0; + } + Local ret; - if (StringBytes::Encode(isolate, buffer.data() + start, length, encoding) + if (StringBytes::Encode(isolate, data_ptr + start, length, encoding) .ToLocal(&ret)) { args.GetReturnValue().Set(ret); } From dc2be3191fea814b963e3c1e4848ba40f461ba54 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sat, 23 May 2026 17:52:19 +0200 Subject: [PATCH 2/2] fixup! src: remove TOCTOU race condition when encoding SAB-backed `Buffer`s --- src/encoding_binding.cc | 3 +-- src/node_buffer.cc | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc index ba8566bc036338..5c07d8d5ecc8d4 100644 --- a/src/encoding_binding.cc +++ b/src/encoding_binding.cc @@ -420,8 +420,7 @@ void BindingData::DecodeUTF8(const FunctionCallbackInfo& args) { CHECK_GE(args.Length(), 1); auto isShared = args[0]->IsSharedArrayBuffer(); - if (!(args[0]->IsArrayBuffer() || isShared || - args[0]->IsArrayBufferView())) { + if (!(args[0]->IsArrayBuffer() || isShared || args[0]->IsArrayBufferView())) { return node::THROW_ERR_INVALID_ARG_TYPE( env->isolate(), "The \"list\" argument must be an instance of SharedArrayBuffer, " diff --git a/src/node_buffer.cc b/src/node_buffer.cc index ae522981f90899..3e79ced3f1ec76 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -572,8 +572,7 @@ void StringSlice(const FunctionCallbackInfo& args) { Local view = args[0].As(); - if (buffer_length == 0) - return args.GetReturnValue().SetEmptyString(); + if (buffer_length == 0) return args.GetReturnValue().SetEmptyString(); size_t start = 0; size_t end = 0;