Skip to content

Commit 4863f6a

Browse files
committed
net: make holding the buffer in memory more robust
Set the `req.buffer` property, which serves as a way of keeping a `Buffer` alive that is being written to a stream, on the C++ side instead of the JS side. This closes a hole where buffers that were temporarily created in order to write strings with uncommon encodings (e.g. `hex`) were passed to the native side without being set as `req.buffer`. Fixes: #8251 PR-URL: #8252 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 5bef38b commit 4863f6a

5 files changed

+68
-1
lines changed

lib/net.js

-1
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,6 @@ Socket.prototype._writeGeneric = function(writev, data, encoding, cb) {
694694
} else {
695695
var enc;
696696
if (data instanceof Buffer) {
697-
req.buffer = data; // Keep reference alive.
698697
enc = 'buffer';
699698
} else {
700699
enc = encoding;

src/env.h

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ namespace node {
6969
V(args_string, "args") \
7070
V(async, "async") \
7171
V(async_queue_string, "_asyncQueue") \
72+
V(buffer_string, "buffer") \
7273
V(bytes_string, "bytes") \
7374
V(bytes_parsed_string, "bytesParsed") \
7475
V(bytes_read_string, "bytesRead") \

src/stream_base.cc

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ int StreamBase::WriteBuffer(const FunctionCallbackInfo<Value>& args) {
227227

228228
err = DoWrite(req_wrap, bufs, count, nullptr);
229229
req_wrap_obj->Set(env->async(), True(env->isolate()));
230+
req_wrap_obj->Set(env->buffer_string(), args[1]);
230231

231232
if (err)
232233
req_wrap->Dispose();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
// Flags: --expose-gc
3+
4+
// Note: This is a variant of test-net-write-fully-async-hex-string.js.
5+
// This always worked, but it seemed appropriate to add a test that checks the
6+
// behaviour for Buffers, too.
7+
const common = require('../common');
8+
const net = require('net');
9+
10+
const data = Buffer.alloc(1000000);
11+
12+
const server = net.createServer(common.mustCall(function(conn) {
13+
conn.resume();
14+
})).listen(0, common.mustCall(function() {
15+
const conn = net.createConnection(this.address().port, common.mustCall(() => {
16+
let count = 0;
17+
18+
function writeLoop() {
19+
if (count++ === 200) {
20+
conn.destroy();
21+
server.close();
22+
return;
23+
}
24+
25+
while (conn.write(Buffer.from(data)));
26+
global.gc(true);
27+
// The buffer allocated above should still be alive.
28+
}
29+
30+
conn.on('drain', writeLoop);
31+
32+
writeLoop();
33+
}));
34+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
// Flags: --expose-gc
3+
4+
// Regression test for https://github.com/nodejs/node/issues/8251.
5+
const common = require('../common');
6+
const net = require('net');
7+
8+
const data = Buffer.alloc(1000000).toString('hex');
9+
10+
const server = net.createServer(common.mustCall(function(conn) {
11+
conn.resume();
12+
})).listen(0, common.mustCall(function() {
13+
const conn = net.createConnection(this.address().port, common.mustCall(() => {
14+
let count = 0;
15+
16+
function writeLoop() {
17+
if (count++ === 20) {
18+
conn.destroy();
19+
server.close();
20+
return;
21+
}
22+
23+
while (conn.write(data, 'hex'));
24+
global.gc(true);
25+
// The buffer allocated inside the .write() call should still be alive.
26+
}
27+
28+
conn.on('drain', writeLoop);
29+
30+
writeLoop();
31+
}));
32+
}));

0 commit comments

Comments
 (0)