tls: prevent use-after-free
authorFedor Indutny <fedor@indutny.com>
Mon, 18 May 2015 18:24:19 +0000 (20:24 +0200)
committerFedor Indutny <fedor@indutny.com>
Thu, 4 Jun 2015 21:38:26 +0000 (23:38 +0200)
* Destroy `SSL*` and friends on a next tick to make sure that we are not
  doing it in one of the OpenSSL callbacks
* Add more checks to the C++ methods that might be invoked during
  destructor's pending queue cleanup

Fix: https://github.com/joyent/node/issues/8780
Fix: https://github.com/iojs/io.js/issues/1696
PR-URL: https://github.com/nodejs/io.js/pull/1702
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
lib/_tls_wrap.js
src/tls_wrap.cc
test/parallel/test-tls-js-stream.js

index 63f1f6c..ede98ee 100644 (file)
@@ -305,13 +305,18 @@ TLSSocket.prototype._wrapHandle = function(handle) {
   });
 
   this.on('close', function() {
-    this._destroySSL();
+    // Make sure we are not doing it on OpenSSL's stack
+    setImmediate(destroySSL, this);
     res = null;
   });
 
   return res;
 };
 
+function destroySSL(self) {
+  self._destroySSL();
+}
+
 TLSSocket.prototype._destroySSL = function _destroySSL() {
   if (!this.ssl) return;
   this.ssl.destroySSL();
index 3076864..ca0690d 100644 (file)
@@ -337,6 +337,9 @@ void TLSWrap::EncOutCb(WriteWrap* req_wrap, int status) {
     return;
   }
 
+  if (wrap->ssl_ == nullptr)
+    return;
+
   // Commit
   NodeBIO::FromBIO(wrap->enc_out_)->Read(nullptr, wrap->write_size_);
 
@@ -554,6 +557,7 @@ int TLSWrap::DoWrite(WriteWrap* w,
                      size_t count,
                      uv_stream_t* send_handle) {
   CHECK_EQ(send_handle, nullptr);
+  CHECK_NE(ssl_, nullptr);
 
   bool empty = true;
 
@@ -627,6 +631,11 @@ void TLSWrap::OnAfterWriteImpl(WriteWrap* w, void* ctx) {
 void TLSWrap::OnAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) {
   TLSWrap* wrap = static_cast<TLSWrap*>(ctx);
 
+  if (wrap->ssl_ == nullptr) {
+    *buf = uv_buf_init(nullptr, 0);
+    return;
+  }
+
   size_t size = 0;
   buf->base = NodeBIO::FromBIO(wrap->enc_in_)->PeekWritable(&size);
   buf->len = size;
@@ -747,6 +756,10 @@ void TLSWrap::SetVerifyMode(const FunctionCallbackInfo<Value>& args) {
 void TLSWrap::EnableSessionCallbacks(
     const FunctionCallbackInfo<Value>& args) {
   TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
+  if (wrap->ssl_ == nullptr) {
+    return wrap->env()->ThrowTypeError(
+        "EnableSessionCallbacks after destroySSL");
+  }
   wrap->enable_session_callbacks();
   NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength);
   wrap->hello_parser_.Start(SSLWrap<TLSWrap>::OnClientHello,
index e156f44..292bd4f 100644 (file)
@@ -61,6 +61,7 @@ var server = tls.createServer({
 
     socket.end('hello');
     socket.resume();
+    socket.destroy();
   });
 
   socket.once('close', function() {