Memory safety (heap allocation/use-after-free risk in Latin1 path of Buffer.indexOf)
Description
The commit fixes a memory safety issue in the Latin1 path of Buffer.indexOf. Previously, IndexOfString used a heap allocation (UncheckedMalloc) for the needle in the LATIN1 branch, with an explicit free. If allocation failed or due to edge-case error handling, this path could be prone to memory-management bugs such as use-after-free or double-free scenarios. The patch replaces the heap allocation with MaybeStackBuffer, removing the explicit free and relying on stack allocation when possible, with a heap fallback managed by MaybeStackBuffer. This reduces memory-management risks in a hot path and improves overall memory safety for the Latin1 indexOf operation.
Commit Details
Author: Mert Can Altin
Date: 2026-03-17 20:27 UTC
Message:
src: use stack allocation in indexOf latin1 path
PR-URL: https://github.com/nodejs/node/pull/62268
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Daniel Lemire <daniel@lemire.me>
Triage Assessment
Vulnerability Type: Memory safety
Confidence: HIGH
Reasoning:
The commit replaces a heap allocation (UncheckedMalloc) and manual free with a stack-allocated buffer (MaybeStackBuffer) for the LATIN1 path in IndexOfString. This reduces memory-management risks (e.g., allocation failure handling, double free, use-after-free) and mitigates potential memory-safety vulnerabilities in that path. Although not a conventional vulnerability like XSS/SQLi, it improves memory safety which is a common security concern.
Verification Assessment
Vulnerability Type: Memory safety (heap allocation/use-after-free risk in Latin1 path of Buffer.indexOf)
Confidence: HIGH
Affected Versions: < 25.9.0
Code Diff
diff --git a/benchmark/buffers/buffer-indexof.js b/benchmark/buffers/buffer-indexof.js
index 52cc95ccb9cf7e..be6cc243a975da 100644
--- a/benchmark/buffers/buffer-indexof.js
+++ b/benchmark/buffers/buffer-indexof.js
@@ -19,7 +19,7 @@ const searchStrings = [
const bench = common.createBenchmark(main, {
search: searchStrings,
- encoding: ['undefined', 'utf8', 'ucs2'],
+ encoding: ['undefined', 'utf8', 'ucs2', 'latin1'],
type: ['buffer', 'string'],
n: [5e4],
}, {
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index e40a21288ee79d..acf4aff37c5a9c 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -1051,23 +1051,19 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) {
offset,
is_forward);
} else if (enc == LATIN1) {
- uint8_t* needle_data = node::UncheckedMalloc<uint8_t>(needle_length);
- if (needle_data == nullptr) {
- return args.GetReturnValue().Set(-1);
- }
+ MaybeStackBuffer<uint8_t> needle_data(needle_length);
StringBytes::Write(isolate,
- reinterpret_cast<char*>(needle_data),
+ reinterpret_cast<char*>(needle_data.out()),
needle_length,
needle,
enc);
result = nbytes::SearchString(reinterpret_cast<const uint8_t*>(haystack),
haystack_length,
- needle_data,
+ needle_data.out(),
needle_length,
offset,
is_forward);
- free(needle_data);
}
args.GetReturnValue().Set(