Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.2k
stream_base,tls_wrap: notify on destruct#11947
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
LGTM, thanks!
Can you also undo the lib/ and src/ changes from#11776 here? They should just be unnecessary overhead now.
addaleax commentedMar 20, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Curious, I tried it too but it worked fine (leaving out the one in edit: diff:diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.jsindex e1767c5e6723..4c9294fb4e69 100644--- a/lib/_tls_wrap.js+++ b/lib/_tls_wrap.js@@ -399,12 +399,6 @@ TLSSocket.prototype._wrapHandle = function(wrap) { res = null; });- if (wrap) {- wrap.on('close', function() {- res.onStreamClose();- });- }- return res; };diff --git a/src/tls_wrap.cc b/src/tls_wrap.ccindex 3dad65011ff8..b4b1e9c9b10d 100644--- a/src/tls_wrap.cc+++ b/src/tls_wrap.cc@@ -815,13 +815,6 @@ void TLSWrap::EnableSessionCallbacks( }-void TLSWrap::OnStreamClose(const FunctionCallbackInfo<Value>& args) {- TLSWrap* wrap;- ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());-- wrap->stream_ = nullptr;-}- void TLSWrap::DestroySSL(const FunctionCallbackInfo<Value>& args) { TLSWrap* wrap;@@ -953,7 +946,6 @@ void TLSWrap::Initialize(Local<Object> target, env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks); env->SetProtoMethod(t, "destroySSL", DestroySSL); env->SetProtoMethod(t, "enableCertCb", EnableCertCb);- env->SetProtoMethod(t, "onStreamClose", OnStreamClose); StreamBase::AddMethods<TLSWrap>(env, t, StreamBase::kFlagHasWritev); SSLWrap<TLSWrap>::AddMethods(env, t);diff --git a/src/tls_wrap.h b/src/tls_wrap.hindex f1d53f3e2f5d..19633ea8667f 100644--- a/src/tls_wrap.h+++ b/src/tls_wrap.h@@ -161,7 +161,6 @@ class TLSWrap : public AsyncWrap, static void EnableCertCb( const v8::FunctionCallbackInfo<v8::Value>& args); static void DestroySSL(const v8::FunctionCallbackInfo<v8::Value>& args);- static void OnStreamClose(const v8::FunctionCallbackInfo<v8::Value>& args); #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB static void GetServername(const v8::FunctionCallbackInfo<v8::Value>& args); |
@addaleax ah yup. it should be |
addaleax left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks! CI:https://ci.nodejs.org/job/node-test-commit/8576/ (edit: green up to a single Jenkins failure)
virtual ~StreamResource() = default; | ||
virtual ~StreamResource() { | ||
if (!destruct_cb_.is_empty()) | ||
destruct_cb_.fn(destruct_cb_.ctx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
If TLSWrap is the only user of this, wouldn't it make more sense to setstream_ = nullptr
in ~TLSWrap()?
(Also, that inheritance chain... TLSWrap -> StreamWrap -> StreamBase -> StreamResource, with multiple inheritance, CRTP and ad hoc function pointers thrown in for good measure. Barf!)
trevnorrisMar 22, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@bnoordhuissettingstream_ = nullptr
in~TLSWrap()
actually isn't enough. And actually, just managed to create a new test that still segfaults. So I'm revisiting the patch.
EDIT: The failure I mentioned just happened to occur under similar circumstances, but unrelated to this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@bnoordhuis okay. so this took me far longer than I'd have liked but I now remember why I made the change this way to begin with.
TLSWrap::stream_
points to theStreamBase*
instance that's passed toTLSWrap::TLSWrap()
. Each of these pointers are assigned to their ownFunctionTemplate
instance as an internal aligned pointer, and the lifetime of each is independent from the other. Meaningstream_
may be deleted even though theTLSWrap
instance remains alive. In JS they're just attached as properties to each other.
So if theStreamBase
instance is closed and released thenstream_
will point to invalid memory, even though theTLSWrap
instance is still alive. Which means the necessary step is to zero out thestream_
field in theTLSWrap
instance, and also addstream != nullptr
checks to the native methods likeTLSWrap::IsAlive()
, so it is no longer pointing to an invalid memory address.
To clarify (if that's possible)TLSWrap
inherits fromStreamBase
and it holds a pointer to anotherStreamBase
instance.
I'll update the commit message with all this information.
FYI all that class template multiple inheritance stuff going on is what lead me to this solution inc0e6c66:
#defineASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \do { \ *ptr = \ Unwrap<typename node::remove_reference<decltype(**ptr)>::type>(obj); \if (*ptr ==nullptr) \return __VA_ARGS__; \ }while (0)
In order to handle unwrapping in methods liketemplate<class Base, ...> StreamBase::JSMethod()
that need to convert avoid*
of a class instance in a template method to its parent class, that also has multiple inheritance.
The TLSWrap constructor is passed a StreamBase* which it stores asTLSWrap::stream_, and is used to receive/send data along the pipeline(e.g. tls -> tcp). Problem is the lifetime of the instance that stream_points to is independent of the lifetime of the TLSWrap instance. Soit's possible for stream_ to be delete'd while the TLSWrap instance isstill alive, allowing potential access to a then invalid pointer.Fix by having the StreamBase destructor null out TLSWrap::stream_;allowing all TLSWrap methods that rely on stream_ to do a check to seeif it's available.While the test provided is fixed by this commit, it was also previouslyfixed by478fabf. Regardless, leave the test in for better testing.PR-URL:nodejs#11947
This partually reverts commit4cdb0e8.A nullptr check in TSLWrap::IsAlive() and the added test were left.PR-URL:nodejs#11947
fd61420
toab67615
Compare@bnoordhuis Updated the commit message with some of the specifics I outlined above. Rebased and new CI:https://ci.nodejs.org/job/node-test-commit/8650/ |
All tests are passing. Will land this tomorrow if there are no objections. |
Landed in0db49fe...595efd8 |
The TLSWrap constructor is passed a StreamBase* which it stores asTLSWrap::stream_, and is used to receive/send data along the pipeline(e.g. tls -> tcp). Problem is the lifetime of the instance that stream_points to is independent of the lifetime of the TLSWrap instance. Soit's possible for stream_ to be delete'd while the TLSWrap instance isstill alive, allowing potential access to a then invalid pointer.Fix by having the StreamBase destructor null out TLSWrap::stream_;allowing all TLSWrap methods that rely on stream_ to do a check to seeif it's available.While the test provided is fixed by this commit, it was also previouslyfixed by478fabf. Regardless, leave the test in for better testing.PR-URL:#11947Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Anna Henningsen <anna@addaleax.net>
@addaleax thanks much! |
The TLSWrap constructor is passed a StreamBase* which it stores asTLSWrap::stream_, and is used to receive/send data along the pipeline(e.g. tls -> tcp). Problem is the lifetime of the instance that stream_points to is independent of the lifetime of the TLSWrap instance. Soit's possible for stream_ to be delete'd while the TLSWrap instance isstill alive, allowing potential access to a then invalid pointer.Fix by having the StreamBase destructor null out TLSWrap::stream_;allowing all TLSWrap methods that rely on stream_ to do a check to seeif it's available.While the test provided is fixed by this commit, it was also previouslyfixed by478fabf. Regardless, leave the test in for better testing.PR-URL:#11947Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Anna Henningsen <anna@addaleax.net>
The TLSWrap constructor is passed a StreamBase* which it stores asTLSWrap::stream_, and is used to receive/send data along the pipeline(e.g. tls -> tcp). Problem is the lifetime of the instance that stream_points to is independent of the lifetime of the TLSWrap instance. Soit's possible for stream_ to be delete'd while the TLSWrap instance isstill alive, allowing potential access to a then invalid pointer.Fix by having the StreamBase destructor null out TLSWrap::stream_;allowing all TLSWrap methods that rely on stream_ to do a check to seeif it's available.While the test provided is fixed by this commit, it was also previouslyfixed by478fabf. Regardless, leave the test in for better testing.PR-URL:#11947Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Anna Henningsen <anna@addaleax.net>
The TLSWrap constructor is passed a StreamBase* which it stores asTLSWrap::stream_, and is used to receive/send data along the pipeline(e.g. tls -> tcp). Problem is the lifetime of the instance that stream_points to is independent of the lifetime of the TLSWrap instance. Soit's possible for stream_ to be delete'd while the TLSWrap instance isstill alive, allowing potential access to a then invalid pointer.Fix by having the StreamBase destructor null out TLSWrap::stream_;allowing all TLSWrap methods that rely on stream_ to do a check to seeif it's available.While the test provided is fixed by this commit, it was also previouslyfixed by478fabf. Regardless, leave the test in for better testing.PR-URL:#11947Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Anna Henningsen <anna@addaleax.net>
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)#11947 * (Ben Noordhuis)#11898 * (jBarz)#11776PR-URL:#12499
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)#11947 * (Ben Noordhuis)#11898 * (jBarz)#11776PR-URL:#12497
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)#11947 * (Ben Noordhuis)#11898 * (jBarz)#11776PR-URL:#12499
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)#11947 * (Ben Noordhuis)#11898 * (jBarz)#11776PR-URL:#12497
Notable Changes: * module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs/node#9283 * src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs/node#11995 * tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs/node#11947 * (Ben Noordhuis)nodejs/node#11898 * (jBarz)nodejs/node#11776 PR-URL:nodejs/node#12499Signed-off-by: Ilkka Myller <ilkka.myller@nodefield.com>
Notable Changes: * module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs/node#9283 * src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs/node#11995 * tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs/node#11947 * (Ben Noordhuis)nodejs/node#11898 * (jBarz)nodejs/node#11776 PR-URL:nodejs/node#12497Signed-off-by: Ilkka Myller <ilkka.myller@nodefield.com>
Notable Changes: * module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs/node#9283 * src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs/node#11995 * tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs/node#11947 * (Ben Noordhuis)nodejs/node#11898 * (jBarz)nodejs/node#11776 PR-URL:nodejs/node#12499Signed-off-by: Ilkka Myller <ilkka.myller@nodefield.com>
Notable Changes: * module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs/node#9283 * src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs/node#11995 * tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs/node#11947 * (Ben Noordhuis)nodejs/node#11898 * (jBarz)nodejs/node#11776 PR-URL:nodejs/node#12497Signed-off-by: Ilkka Myller <ilkka.myller@nodefield.com>
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs#11947 * (Ben Noordhuis)nodejs#11898 * (jBarz)nodejs#11776PR-URL:nodejs#12499
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs#11947 * (Ben Noordhuis)nodejs#11898 * (jBarz)nodejs#11776PR-URL:nodejs#12497
The TLSWrap constructor is passed a StreamBase* which it stores asTLSWrap::stream_, and is used to receive/send data along the pipeline(e.g. tls -> tcp). Problem is the lifetime of the instance that stream_points to is independent of the lifetime of the TLSWrap instance. Soit's possible for stream_ to be delete'd while the TLSWrap instance isstill alive, allowing potential access to a then invalid pointer.Fix by having the StreamBase destructor null out TLSWrap::stream_;allowing all TLSWrap methods that rely on stream_ to do a check to seeif it's available.While the test provided is fixed by this commit, it was also previouslyfixed by478fabf. Regardless, leave the test in for better testing.PR-URL:nodejs/node#11947Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Anna Henningsen <anna@addaleax.net>
This partually reverts commit4cdb0e8.A nullptr check in TSLWrap::IsAlive() and the added test were left.PR-URL:nodejs/node#11947Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Anna Henningsen <anna@addaleax.net>
Notable Changes:* module: - The module loading global fallback to the Node executable's directory now works correctly on Windows. (Richard Lau)nodejs/node#9283* src: - fix base64 decoding in rare edgecase (Nikolai Vavilov)nodejs/node#11995* tls: - fix rare segmentation faults when using TLS * (Trevor Norris)nodejs/node#11947 * (Ben Noordhuis)nodejs/node#11898 * (jBarz)nodejs/node#11776PR-URL:nodejs/node#12497
Uh oh!
There was an error while loading.Please reload this page.
Classes like
TLSWrap
are given a pointer to aStreamBase
instance;stored on the private member
TLSWrap::stream_
. Unfortunately thelifetime of
stream_
is not reported to theTLSWrap
instance and iffree'd will leave an invalid pointer; which can still be accessed by the
TLSWrap
instance. Causing the application to segfault if accessed.Give
StreamBase
a destruct callback to notify when the class is beingcleaned up. Use this in
TLSWrap
to setstream_ = nullptr
.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
stream_base, tls_wrap
CI:https://ci.nodejs.org/job/node-test-commit/8575/