Skip to content

Commit 5b00de7

Browse files
jasnellrichardlau
authored andcommittedDec 24, 2020
src: retain pointers to WriteWrap/ShutdownWrap
Avoids potential use-after-free when wrap req's are synchronously destroyed. CVE-ID: CVE-2020-8265 Fixes: nodejs-private/node-private#227 PR-URL: nodejs-private/node-private#230 Refs: https://hackerone.com/bugs?subject=nodejs&report_id=988103 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 420244e commit 5b00de7

File tree

4 files changed

+68
-4
lines changed

4 files changed

+68
-4
lines changed
 

Diff for: ‎src/stream_base-inl.h

+8-3
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,11 @@ inline int StreamBase::Shutdown(v8::Local<v8::Object> req_wrap_obj) {
163163
StreamReq::ResetObject(req_wrap_obj);
164164
}
165165

166+
BaseObjectPtr<AsyncWrap> req_wrap_ptr;
166167
AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(GetAsyncWrap());
167168
ShutdownWrap* req_wrap = CreateShutdownWrap(req_wrap_obj);
169+
if (req_wrap != nullptr)
170+
req_wrap_ptr.reset(req_wrap->GetAsyncWrap());
168171
int err = DoShutdown(req_wrap);
169172

170173
if (err != 0 && req_wrap != nullptr) {
@@ -198,7 +201,7 @@ inline StreamWriteResult StreamBase::Write(
198201
if (send_handle == nullptr) {
199202
err = DoTryWrite(&bufs, &count);
200203
if (err != 0 || count == 0) {
201-
return StreamWriteResult { false, err, nullptr, total_bytes };
204+
return StreamWriteResult { false, err, nullptr, total_bytes, {} };
202205
}
203206
}
204207

@@ -208,13 +211,14 @@ inline StreamWriteResult StreamBase::Write(
208211
if (!env->write_wrap_template()
209212
->NewInstance(env->context())
210213
.ToLocal(&req_wrap_obj)) {
211-
return StreamWriteResult { false, UV_EBUSY, nullptr, 0 };
214+
return StreamWriteResult { false, UV_EBUSY, nullptr, 0, {} };
212215
}
213216
StreamReq::ResetObject(req_wrap_obj);
214217
}
215218

216219
AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(GetAsyncWrap());
217220
WriteWrap* req_wrap = CreateWriteWrap(req_wrap_obj);
221+
BaseObjectPtr<AsyncWrap> req_wrap_ptr(req_wrap->GetAsyncWrap());
218222

219223
err = DoWrite(req_wrap, bufs, count, send_handle);
220224
bool async = err == 0;
@@ -232,7 +236,8 @@ inline StreamWriteResult StreamBase::Write(
232236
ClearError();
233237
}
234238

235-
return StreamWriteResult { async, err, req_wrap, total_bytes };
239+
return StreamWriteResult {
240+
async, err, req_wrap, total_bytes, std::move(req_wrap_ptr) };
236241
}
237242

238243
template <typename OtherBase>

Diff for: ‎src/stream_base.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ int StreamBase::WriteString(const FunctionCallbackInfo<Value>& args) {
259259

260260
// Immediate failure or success
261261
if (err != 0 || count == 0) {
262-
SetWriteResult(StreamWriteResult { false, err, nullptr, data_size });
262+
SetWriteResult(StreamWriteResult { false, err, nullptr, data_size, {} });
263263
return err;
264264
}
265265

Diff for: ‎src/stream_base.h

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct StreamWriteResult {
2424
int err;
2525
WriteWrap* wrap;
2626
size_t bytes;
27+
BaseObjectPtr<AsyncWrap> wrap_obj;
2728
};
2829

2930
using JSMethodFunction = void(const v8::FunctionCallbackInfo<v8::Value>& args);

Diff for: ‎test/parallel/test-tls-use-after-free-regression.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const https = require('https');
9+
const tls = require('tls');
10+
11+
const kMessage =
12+
'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: Keep-alive\r\n\r\n';
13+
14+
const key = `-----BEGIN EC PARAMETERS-----
15+
BggqhkjOPQMBBw==
16+
-----END EC PARAMETERS-----
17+
-----BEGIN EC PRIVATE KEY-----
18+
MHcCAQEEIDKfHHbiJMdu2STyHL11fWC7psMY19/gUNpsUpkwgGACoAoGCCqGSM49
19+
AwEHoUQDQgAEItqm+pYj3Ca8bi5mBs+H8xSMxuW2JNn4I+kw3aREsetLk8pn3o81
20+
PWBiTdSZrGBGQSy+UAlQvYeE6Z/QXQk8aw==
21+
-----END EC PRIVATE KEY-----`;
22+
23+
const cert = `-----BEGIN CERTIFICATE-----
24+
MIIBhjCCASsCFDJU1tCo88NYU//pE+DQKO9hUDsFMAoGCCqGSM49BAMCMEUxCzAJ
25+
BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l
26+
dCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAwOTIyMDg1NDU5WhcNNDgwMjA3MDg1NDU5
27+
WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwY
28+
SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
29+
QgAEItqm+pYj3Ca8bi5mBs+H8xSMxuW2JNn4I+kw3aREsetLk8pn3o81PWBiTdSZ
30+
rGBGQSy+UAlQvYeE6Z/QXQk8azAKBggqhkjOPQQDAgNJADBGAiEA7Bdn4F87KqIe
31+
Y/ABy/XIXXpFUb2nyv3zV7POQi2lPcECIQC3UWLmfiedpiIKsf9YRIyO0uEood7+
32+
glj2R1NNr1X68w==
33+
-----END CERTIFICATE-----`;
34+
35+
const server = https.createServer(
36+
{ key, cert },
37+
common.mustCall((req, res) => {
38+
res.writeHead(200);
39+
res.end('boom goes the dynamite\n');
40+
}, 3));
41+
42+
server.listen(0, common.mustCall(() => {
43+
const socket =
44+
tls.connect(
45+
server.address().port,
46+
'localhost',
47+
{ rejectUnauthorized: false },
48+
common.mustCall(() => {
49+
socket.write(kMessage);
50+
socket.write(kMessage);
51+
socket.write(kMessage);
52+
}));
53+
54+
socket.on('data', common.mustCall(() => socket.destroy()));
55+
socket.on('close', () => {
56+
setImmediate(() => server.close());
57+
});
58+
}));

0 commit comments

Comments
 (0)
Please sign in to comment.