Skip to content

Commit 8725e56

Browse files
RajeshKumar11aduh95
authored andcommitted
src: fix crash when writing odd-length hex string via Writev
StringBytes::StorageSize had a CHECK that fatal-asserted when a hex-encoded string with an odd number of characters was written through Writev (e.g. via HTTP requests which are automatically corked). Writing the same string via a single Write did not crash because StringBytes::Write delegates to HexDecode, which silently drops the trailing incomplete nibble. Remove the CHECK and let integer division handle odd lengths, which is consistent with StringBytes::Size and HexDecode. Fixes: #45150 Signed-off-by: RajeshKumar11 <kakumanurajeshkumar@gmail.com> PR-URL: #63658 Reviewed-By: Edy Silva <edigleyssonsilva@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
1 parent 3def577 commit 8725e56

2 files changed

Lines changed: 91 additions & 1 deletion

File tree

src/string_bytes.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ Maybe<size_t> StringBytes::StorageSize(Isolate* isolate,
472472
break;
473473

474474
case HEX:
475-
CHECK(view.length() % 2 == 0 && "invalid hex string length");
476475
data_size = view.length() / 2;
477476
break;
478477

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('node:assert');
5+
const http = require('node:http');
6+
const net = require('node:net');
7+
8+
// Regression test for https://github.com/nodejs/node/issues/45150
9+
// Writing an odd-length hex string to a stream that batches writes via
10+
// Writev (e.g. HTTP requests that are automatically corked) used to
11+
// fatal-assert in StringBytes::StorageSize. The trailing incomplete nibble
12+
// should be silently dropped, consistent with non-Writev paths.
13+
14+
// Test 1: HTTP POST with a single odd-length hex write.
15+
// "1" has no complete bytes in hex encoding, so the request body is empty.
16+
{
17+
const server = http.createServer(common.mustCall((req, res) => {
18+
const chunks = [];
19+
req.on('data', (chunk) => chunks.push(chunk));
20+
req.on('end', common.mustCall(() => {
21+
assert.strictEqual(Buffer.concat(chunks).length, 0);
22+
res.end();
23+
server.close();
24+
}));
25+
}));
26+
27+
server.listen(0, common.mustCall(() => {
28+
const req = http.request({
29+
port: server.address().port,
30+
method: 'POST',
31+
}, common.mustCall((res) => {
32+
res.resume();
33+
}));
34+
req.write('1', 'hex');
35+
req.end();
36+
}));
37+
}
38+
39+
// Test 2: HTTP POST with cork/uncork and mixed odd-length hex writes.
40+
// "ff1" (3 hex chars) decodes to 1 byte (0xff); the trailing "1" nibble is
41+
// dropped. "1" (1 hex char) decodes to 0 bytes.
42+
{
43+
const server = http.createServer(common.mustCall((req, res) => {
44+
const chunks = [];
45+
req.on('data', (chunk) => chunks.push(chunk));
46+
req.on('end', common.mustCall(() => {
47+
assert.deepStrictEqual(Buffer.concat(chunks), Buffer.from([0xff]));
48+
res.end();
49+
server.close();
50+
}));
51+
}));
52+
53+
server.listen(0, common.mustCall(() => {
54+
const req = http.request({
55+
port: server.address().port,
56+
method: 'POST',
57+
}, common.mustCall((res) => {
58+
res.resume();
59+
}));
60+
req.cork();
61+
req.write('ff1', 'hex');
62+
req.write('1', 'hex');
63+
req.uncork();
64+
req.end();
65+
}));
66+
}
67+
68+
// Test 3: net socket with cork/uncork and an odd-length hex write.
69+
// Exercises the Writev path directly at the net layer.
70+
{
71+
const server = net.createServer(common.mustCall((socket) => {
72+
const chunks = [];
73+
socket.on('data', (chunk) => chunks.push(chunk));
74+
socket.on('end', common.mustCall(() => {
75+
assert.deepStrictEqual(Buffer.concat(chunks), Buffer.from([0xff]));
76+
server.close();
77+
}));
78+
socket.resume();
79+
}));
80+
81+
server.listen(0, common.mustCall(() => {
82+
const conn = net.createConnection(server.address().port);
83+
conn.on('connect', common.mustCall(() => {
84+
conn.cork();
85+
conn.write('ff', 'hex');
86+
conn.write('1', 'hex');
87+
conn.uncork();
88+
conn.end();
89+
}));
90+
}));
91+
}

0 commit comments

Comments
 (0)