folly/io/async/tests: always detach event base in tests, fixes UBSAN tests
authorNathan Bronson <ngbronson@fb.com>
Fri, 9 Dec 2016 20:42:28 +0000 (12:42 -0800)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Fri, 9 Dec 2016 20:47:59 +0000 (12:47 -0800)
Summary:
In AsyncSSLSocket tests the shared_ptr structure means that the
AsyncSSLSocket-s outlive the stack-allocated EventBase on which they were
created.  Previously there were scattered calls to detachEventBase on the
last interesting callback, but several calls were missing.  This diff
switches to a model where the SSLServerAcceptCallbackBase is responsible
for detaching the sockets.

This diff also fixes a low-firing race between shutdown
of the TestSSLAsyncCacheServer Main thread and a call to
EmptyReadCallback::readEOF.  Most uses of EmptyReadCallback don't attach
the TCP socket, so they can't actually tolerate a call to readError
or readEOF.

These use-after-destruction problems were discovered by UBSAN.

Reviewed By: djwatson

Differential Revision: D4301416

fbshipit-source-id: 127fb5e506d0694c5ca81d7a38c704d69f4ab3eb

folly/io/async/test/AsyncSSLSocketTest.cpp
folly/io/async/test/AsyncSSLSocketTest.h

index bc7c4f4896db1e174a5967854a49ae320def8036..f2bd909ff81b12155347b40ceb127c2a618d3148 100644 (file)
@@ -915,7 +915,6 @@ TEST(AsyncSSLSocketTest, SSLServerTimeoutTest) {
   // Start listening on a local port
   WriteCallbackBase writeCallback;
   ReadCallback readCallback(&writeCallback);
-  EmptyReadCallback clientReadCallback;
   HandshakeCallback handshakeCallback(&readCallback);
   SSLServerAcceptCallback acceptCallback(&handshakeCallback, 50);
   TestSSLAsyncCacheServer server(&acceptCallback);
@@ -925,6 +924,8 @@ TEST(AsyncSSLSocketTest, SSLServerTimeoutTest) {
   // only do a TCP connect
   std::shared_ptr<AsyncSocket> sock = AsyncSocket::newSocket(&eventBase);
   sock->connect(nullptr, server.getAddress());
+
+  EmptyReadCallback clientReadCallback;
   clientReadCallback.tcpSocket_ = sock;
   sock->setReadCB(&clientReadCallback);
 
index cc09b1064366dce9cdfb704add72638371e92c6a..d66f50886334b62365f0ae82644f45cf52bb58a5 100644 (file)
@@ -83,7 +83,6 @@ public:
     this->bytesWritten = nBytesWritten;
     exception = ex;
     socket_->close();
-    socket_->detachEventBase();
   }
 
   std::shared_ptr<AsyncSSLSocket> socket_;
@@ -119,14 +118,12 @@ public AsyncTransportWrapper::ReadCallback {
     std::cerr << "readError " << ex.what() << std::endl;
     state = STATE_FAILED;
     socket_->close();
-    socket_->detachEventBase();
   }
 
   void readEOF() noexcept override {
     std::cerr << "readEOF" << std::endl;
 
     socket_->close();
-    socket_->detachEventBase();
   }
 
   std::shared_ptr<AsyncSSLSocket> socket_;
@@ -288,15 +285,16 @@ public:
   void readErr(const AsyncSocketException& ex) noexcept override {
     std::cerr << "readError " << ex.what() << std::endl;
     state = STATE_FAILED;
-    tcpSocket_->close();
-    tcpSocket_->detachEventBase();
+    if (tcpSocket_) {
+      tcpSocket_->close();
+    }
   }
 
   void readEOF() noexcept override {
     std::cerr << "readEOF" << std::endl;
-
-    tcpSocket_->close();
-    tcpSocket_->detachEventBase();
+    if (tcpSocket_) {
+      tcpSocket_->close();
+    }
     state = STATE_SUCCEEDED;
   }
 
@@ -395,12 +393,14 @@ public:
 
   void connectionAccepted(
       int fd, const folly::SocketAddress& /* clientAddr */) noexcept override {
+    if (socket_) {
+      socket_->detachEventBase();
+    }
     printf("Connection accepted\n");
-    std::shared_ptr<AsyncSSLSocket> sslSock;
     try {
       // Create a AsyncSSLSocket object with the fd. The socket should be
       // added to the event base and in the state of accepting SSL connection.
-      sslSock = AsyncSSLSocket::newSocket(ctx_, base_, fd);
+      socket_ = AsyncSSLSocket::newSocket(ctx_, base_, fd);
     } catch (const std::exception &e) {
       LOG(ERROR) << "Exception %s caught while creating a AsyncSSLSocket "
         "object with socket " << e.what() << fd;
@@ -409,15 +409,20 @@ public:
       return;
     }
 
-    connAccepted(sslSock);
+    connAccepted(socket_);
   }
 
   virtual void connAccepted(
     const std::shared_ptr<folly::AsyncSSLSocket> &s) = 0;
 
+  void detach() {
+    socket_->detachEventBase();
+  }
+
   StateEnum state;
   HandshakeCallback *hcb_;
   std::shared_ptr<folly::SSLContext> ctx_;
+  std::shared_ptr<AsyncSSLSocket> socket_;
   folly::EventBase* base_;
 };
 
@@ -551,8 +556,6 @@ public:
     EXPECT_EQ(hcb_->state, STATE_FAILED);
     EXPECT_EQ(callback2.state, STATE_FAILED);
 
-    sock->detachEventBase();
-
     state = STATE_SUCCEEDED;
     hcb_->setState(STATE_SUCCEEDED);
     callback2.setState(STATE_SUCCEEDED);
@@ -623,6 +626,7 @@ class TestSSLServer {
   static void *Main(void *ctx) {
     TestSSLServer *self = static_cast<TestSSLServer*>(ctx);
     self->evb_.loop();
+    self->acb_->detach();
     std::cerr << "Server thread exited event loop" << std::endl;
     return nullptr;
   }