Commit 059c8012e67 for nodejs
commit 059c8012e6793561d5e3440802a4542020e86aed
Author: Matteo Collina <hello@matteocollina.com>
Date: Sun Nov 1 16:20:38 2026 +0100
http2: avoid overflowing ALTSVC buffers
Http2Session::AltSvc allocated origin and field-value buffers at their
exact lengths but requested V8 to append a null terminator. This wrote
one byte past heap-backed buffers.
nghttp2_submit_altsvc() consumes explicit lengths and makes its own
terminated copies. Write only the string contents.
Assisted-by: OpenAI Codex
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs-private/node-private/pull/1035
Reviewed-By: Tim Perry <pimterry@gmail.com>
CVE-ID: CVE-2026-123828
diff --git a/src/node_http2.cc b/src/node_http2.cc
index a6779610ddb..9a7d700e046 100644
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -3371,13 +3371,8 @@ void Http2Session::AltSvc(const FunctionCallbackInfo<Value>& args) {
MaybeStackBuffer<uint8_t> origin(origin_len);
MaybeStackBuffer<uint8_t> value(value_len);
- origin_str->WriteOneByteV2(env->isolate(),
- 0,
- origin_len,
- *origin,
- String::WriteFlags::kNullTerminate);
- value_str->WriteOneByteV2(
- env->isolate(), 0, value_len, *value, String::WriteFlags::kNullTerminate);
+ origin_str->WriteOneByteV2(env->isolate(), 0, origin_len, *origin);
+ value_str->WriteOneByteV2(env->isolate(), 0, value_len, *value);
session->AltSvc(id, *origin, origin_len, *value, value_len);
}
diff --git a/test/parallel/test-http2-altsvc.js b/test/parallel/test-http2-altsvc.js
index c5abfc33265..de2a3557040 100644
--- a/test/parallel/test-http2-altsvc.js
+++ b/test/parallel/test-http2-altsvc.js
@@ -8,6 +8,9 @@ const assert = require('assert');
const http2 = require('http2');
const Countdown = require('../common/countdown');
+const longOrigin = `https://${'a'.repeat(1040)}`;
+const longAlt = `h2=":${'8'.repeat(1042)}"`;
+
const server = http2.createServer();
server.on('stream', common.mustCall((stream) => {
stream.session.altsvc('h2=":8000"', stream.id);
@@ -22,6 +25,8 @@ server.on('session', common.mustCall((session) => {
session.altsvc('h2=":8000"', 'https://example.org:8111/this');
session.altsvc('h2=":8000"', new URL('https://example.org:8111/this'));
session.altsvc('h2=":8000"', { origin: 'https://example.org:8111' });
+ session.altsvc('h2=":8001"', longOrigin);
+ session.altsvc(longAlt, 'https://example.org:8112');
// Won't error, but won't send anything because the stream does not exist
session.altsvc('h2=":8000"', 3);
@@ -104,25 +109,33 @@ server.on('session', common.mustCall((session) => {
server.listen(0, common.mustCall(() => {
const client = http2.connect(`http://localhost:${server.address().port}`);
- const countdown = new Countdown(4, () => {
+ const countdown = new Countdown(6, () => {
client.close();
server.close();
});
client.on('altsvc', common.mustCall((alt, origin, stream) => {
- assert.strictEqual(alt, 'h2=":8000"');
- switch (stream) {
- case 0:
- assert.strictEqual(origin, 'https://example.org:8111');
- break;
- case 1:
- assert.strictEqual(origin, '');
- break;
- default:
- assert.fail('should not happen');
+ if (alt === 'h2=":8001"') {
+ assert.strictEqual(origin, longOrigin);
+ assert.strictEqual(stream, 0);
+ } else if (alt === longAlt) {
+ assert.strictEqual(origin, 'https://example.org:8112');
+ assert.strictEqual(stream, 0);
+ } else {
+ assert.strictEqual(alt, 'h2=":8000"');
+ switch (stream) {
+ case 0:
+ assert.strictEqual(origin, 'https://example.org:8111');
+ break;
+ case 1:
+ assert.strictEqual(origin, '');
+ break;
+ default:
+ assert.fail('should not happen');
+ }
}
countdown.dec();
- }, 4));
+ }, 6));
const req = client.request();
req.resume();