From ed8257f220192d041d86e90e3158d897cab09231 Mon Sep 17 00:00:00 2001 From: Archkon <180910180+Archkon@users.noreply.github.com> Date: Sun, 26 Jul 2026 10:42:59 +0800 Subject: [PATCH] src: handle empty external string resources V8 requires external string resources to have a non-null data pointer, even when their length is zero. Empty vectors may return a null data pointer, causing NewExternalTwoByte() to abort the process. Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com> --- src/node_union_bytes.h | 4 ++++ src/util.cc | 6 ++++++ test/cctest/test_util.cc | 14 ++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/node_union_bytes.h b/src/node_union_bytes.h index 4a3f67980fdc92..28d6f1a9846b79 100644 --- a/src/node_union_bytes.h +++ b/src/node_union_bytes.h @@ -66,6 +66,10 @@ class UnionBytes { UnionBytes& operator=(UnionBytes&&) = default; bool is_one_byte() const { return one_byte_resource_ != nullptr; } + size_t length() const { + return is_one_byte() ? one_byte_resource_->length() + : two_byte_resource_->length(); + } v8::Local ToStringChecked(v8::Isolate* isolate) const; diff --git a/src/util.cc b/src/util.cc index 317b8db0daac69..72fcffde991e22 100644 --- a/src/util.cc +++ b/src/util.cc @@ -636,6 +636,12 @@ void SetConstructorFunction(Isolate* isolate, } Local UnionBytes::ToStringChecked(Isolate* isolate) const { + if (length() == 0) [[unlikely]] { + // V8 requires non-null data pointers for empty external strings, + // but we don't guarantee that. Solve this by not creating an + // external string at all in that case. + return String::Empty(isolate); + } if (is_one_byte()) { return String::NewExternalOneByte(isolate, one_byte_resource_) .ToLocalChecked(); diff --git a/test/cctest/test_util.cc b/test/cctest/test_util.cc index c3beea15f2dfc4..d2631182910d65 100644 --- a/test/cctest/test_util.cc +++ b/test/cctest/test_util.cc @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "node_options-inl.h" #include "node_test_fixture.h" +#include "node_union_bytes.h" #include "simdutf.h" #include "util-inl.h" #include "v8-function-callback.h" @@ -312,6 +313,19 @@ TEST_F(UtilTest, DumpJavaScriptStackWithNoIsolate) { node::DumpJavaScriptBacktrace(stderr); } +TEST_F(UtilTest, EmptyUnionBytesToString) { + const v8::HandleScope handle_scope(isolate_); + node::StaticExternalOneByteResource one_byte_resource(nullptr, 0, nullptr); + node::StaticExternalTwoByteResource two_byte_resource(nullptr, 0, nullptr); + + EXPECT_EQ( + 0, + node::UnionBytes(&one_byte_resource).ToStringChecked(isolate_)->Length()); + EXPECT_EQ( + 0, + node::UnionBytes(&two_byte_resource).ToStringChecked(isolate_)->Length()); +} + TEST_F(UtilTest, DetermineSpecificErrorType) { const v8::HandleScope handle_scope(isolate_); Argv argv;