Set bufferCallback_ back to nullptr upon HTTPSession shutdown
authorYang Chi <yangchi@fb.com>
Fri, 27 May 2016 22:33:40 +0000 (15:33 -0700)
committerFacebook Github Bot 4 <facebook-github-bot-4-bot@fb.com>
Fri, 27 May 2016 22:38:35 +0000 (15:38 -0700)
Summary: AsyncSocket::handleWrite may trigger HTTPSession::onWriteSuccess which may totally delete the HTTPSession. AsyncSocket::handleWrite also calls bufferCallback_->onEgressBufferCleared(). That bufferCallback_ is the HTTPSession. So in this diff resets bufferCallback_ to nullptr upon HTTPSession shutdown. afrind had a suggestion of adding a DestructorGuard in AsyncSocket::handleWrite. But i don't know DestructorGuard that well. I think it will keep AsyncSocket from being deleted but not the HTTPSession? Or maybe I can DestructorGuard dg(bufferCallback_) ?

Reviewed By: afrind

Differential Revision: D3311058

fbshipit-source-id: cdb5fcbd53837a3effbc096eab87fca4e5d17291

folly/io/async/AsyncSocket.cpp
folly/io/async/test/AsyncSocketTest2.cpp

index 848cf5c47a94f0567eb55754109790fde4b48120..3d9f91c7889862a4b562a678127ef6941e3ba93b 100644 (file)
@@ -1430,6 +1430,8 @@ void AsyncSocket::handleRead() noexcept {
 void AsyncSocket::handleWrite() noexcept {
   VLOG(5) << "AsyncSocket::handleWrite() this=" << this << ", fd=" << fd_
           << ", state=" << state_;
+  DestructorGuard dg(this);
+
   if (state_ == StateEnum::CONNECTING) {
     handleConnect();
     return;
index 4eecf3a68b6440f7b2afe84951f5e96acca45163..4f9bd3a2c1447930972fee045b09f3fb18eb5fbc 100644 (file)
@@ -2280,3 +2280,34 @@ TEST(AsyncSocketTest, BufferTest) {
   ASSERT_TRUE(socket->isClosedBySelf());
   ASSERT_FALSE(socket->isClosedByPeer());
 }
+
+TEST(AsyncSocketTest, BufferCallbackKill) {
+  TestServer server;
+  EventBase evb;
+  AsyncSocket::OptionMap option{{{SOL_SOCKET, SO_SNDBUF}, 128}};
+  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);
+  ConnCallback ccb;
+  socket->connect(&ccb, server.getAddress(), 30, option);
+  evb.loopOnce();
+
+  char buf[100 * 1024];
+  memset(buf, 'c', sizeof(buf));
+  BufferCallback* bcb = new BufferCallback;
+  socket->setBufferCallback(bcb);
+  WriteCallback wcb;
+  wcb.successCallback = [&] {
+    ASSERT_TRUE(socket.unique());
+    socket.reset();
+  };
+
+  // This will trigger AsyncSocket::handleWrite,
+  // which calls WriteCallback::writeSuccess,
+  // which calls wcb.successCallback above,
+  // which tries to delete socket
+  // Then, the socket will also try to use this BufferCallback
+  // And that should crash us, if there is no DestructorGuard on the stack
+  socket->write(&wcb, buf, sizeof(buf), WriteFlags::NONE);
+
+  evb.loop();
+  CHECK_EQ(ccb.state, STATE_SUCCEEDED);
+}