Skip to content

Commit 56e1586

Browse files
addaleaxMylesBorins
authored andcommitted
tls: unconsume stream on destroy
When the TLS stream is destroyed for whatever reason, we should unset all callbacks on the underlying transport stream. PR-URL: #17478 Fixes: #17475 Reviewed-By: Fedor Indutny <[email protected]> Reviewed-By: Jan Krems <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent af3e074 commit 56e1586

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

src/tls_wrap.cc

+19-2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ TLSWrap::~TLSWrap() {
8282
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
8383
sni_context_.Reset();
8484
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
85+
86+
// See test/parallel/test-tls-transport-destroy-after-own-gc.js:
87+
// If this TLSWrap is garbage collected, we cannot allow callbacks to be
88+
// called on this stream.
89+
90+
if (stream_ == nullptr)
91+
return;
92+
stream_->set_destruct_cb({ nullptr, nullptr });
93+
stream_->set_after_write_cb({ nullptr, nullptr });
94+
stream_->set_alloc_cb({ nullptr, nullptr });
95+
stream_->set_read_cb({ nullptr, nullptr });
96+
stream_->set_destruct_cb({ nullptr, nullptr });
97+
stream_->Unconsume();
8598
}
8699

87100

@@ -545,12 +558,16 @@ uint32_t TLSWrap::UpdateWriteQueueSize(uint32_t write_queue_size) {
545558

546559

547560
int TLSWrap::ReadStart() {
548-
return stream_->ReadStart();
561+
if (stream_ != nullptr)
562+
return stream_->ReadStart();
563+
return 0;
549564
}
550565

551566

552567
int TLSWrap::ReadStop() {
553-
return stream_->ReadStop();
568+
if (stream_ != nullptr)
569+
return stream_->ReadStop();
570+
return 0;
554571
}
555572

556573

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Flags: --expose-gc
2+
'use strict';
3+
4+
// Regression test for https://github.com/nodejs/node/issues/17475
5+
// Unfortunately, this tests only "works" reliably when checked with valgrind or
6+
// a similar tool.
7+
8+
const common = require('../common');
9+
if (!common.hasCrypto)
10+
common.skip('missing crypto');
11+
12+
const { TLSSocket } = require('tls');
13+
const makeDuplexPair = require('../common/duplexpair');
14+
15+
let { clientSide } = makeDuplexPair();
16+
17+
let clientTLS = new TLSSocket(clientSide, { isServer: false });
18+
// eslint-disable-next-line no-unused-vars
19+
let clientTLSHandle = clientTLS._handle;
20+
21+
setImmediate(() => {
22+
clientTLS = null;
23+
global.gc();
24+
clientTLSHandle = null;
25+
global.gc();
26+
setImmediate(() => {
27+
clientSide = null;
28+
global.gc();
29+
});
30+
});

0 commit comments

Comments
 (0)