From 54b4e372f39329ff0f3688e135c2e09cbdf434f4 Mon Sep 17 00:00:00 2001 From: Rafael Gonzaga Date: Tue, 25 Aug 2026 07:32:37 -0300 Subject: [PATCH] buffer: prevent abort on indexOf with lone surrogate needle Signed-off-by: RafaelGSS PR-URL: https://github.com/nodejs/node/pull/65430 Reviewed-By: James M Snell Reviewed-By: Antoine du Hamel --- src/string_bytes.cc | 6 ++++-- test/parallel/test-buffer-indexof.js | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 0dbfbfac6d5..6e628b907e6 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -533,8 +533,10 @@ Maybe StringBytes::Size(Isolate* isolate, return Just(simdutf::utf8_length_from_latin1( reinterpret_cast(view.data8()), view.length())); } - return Just(simdutf::utf8_length_from_utf16( - reinterpret_cast(view.data16()), view.length())); + return Just( + simdutf::utf8_length_from_utf16_with_replacement( + reinterpret_cast(view.data16()), view.length()) + .count); case UCS2: return Just(view.length() * sizeof(uint16_t)); diff --git a/test/parallel/test-buffer-indexof.js b/test/parallel/test-buffer-indexof.js index 2dd62c91e25..4bed7f935b8 100644 --- a/test/parallel/test-buffer-indexof.js +++ b/test/parallel/test-buffer-indexof.js @@ -619,6 +619,32 @@ assert.strictEqual(reallyLong.lastIndexOf(pattern), 0); assert.strictEqual(haystack.lastIndexOf(needle), haystack.length - 3); } +// UTF-8 string search should use the same replacement-character semantics +// as Buffer.from() for unpaired UTF-16 surrogates. +{ + const prefix = 'a'.repeat(16); + const suffix = 'b'.repeat(16); + const needles = [ + `${prefix}${String.fromCharCode(0xd800)}${suffix}`, + `${prefix}${String.fromCharCode(0xdc00)}${suffix}`, + JSON.parse(`{"needle":"${prefix}\\ud800${suffix}"}`).needle, + ]; + + for (const needle of needles) { + const needleBuffer = Buffer.from(needle, 'utf8'); + const haystack = Buffer.concat([ + Buffer.from('xx'), + needleBuffer, + Buffer.from('yy'), + ]); + + assert.strictEqual(Buffer.byteLength(needle, 'utf8'), needleBuffer.length); + assert.strictEqual(haystack.indexOf(needle, 0, 'utf8'), 2); + assert.strictEqual(haystack.lastIndexOf(needle, haystack.length - 1, 'utf8'), 2); + assert.strictEqual(haystack.includes(needle, 0, 'utf8'), true); + } +} + // Avoid abort because of invalid usage // see https://github.com/nodejs/node/issues/32753 {