Fix race in writes during reads:

fix #261

This fixes a rare condition when responding to a ping or
close frame where the wr_block_ stream variable is not
correctly set for a short window of time.
This commit is contained in:
Vinnie Falco
2017-02-10 11:15:14 -05:00
parent 4c0f7d9286
commit d735aa949e
2 changed files with 31 additions and 8 deletions

View File

@ -1,3 +1,9 @@
1.0.0-b29
* Fix race in writes during WebSocket reads
--------------------------------------------------------------------------------
1.0.0-b28 1.0.0-b28
* Split out and rename test stream classes * Split out and rename test stream classes

View File

@ -499,6 +499,8 @@ operator()(error_code ec,
//------------------------------------------------------------------ //------------------------------------------------------------------
case do_pong_resume: case do_pong_resume:
BOOST_ASSERT(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
d.state = do_pong_resume + 1; d.state = do_pong_resume + 1;
d.ws.get_io_service().post(bind_handler( d.ws.get_io_service().post(bind_handler(
std::move(*this), ec, bytes_transferred)); std::move(*this), ec, bytes_transferred));
@ -511,8 +513,7 @@ operator()(error_code ec,
ec = boost::asio::error::operation_aborted; ec = boost::asio::error::operation_aborted;
goto upcall; goto upcall;
} }
d.state = do_pong; // [[fallthrough]]
break; // VFALCO fall through?
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -520,14 +521,21 @@ operator()(error_code ec,
if(d.ws.wr_close_) if(d.ws.wr_close_)
{ {
// ignore ping when closing // ignore ping when closing
if(d.ws.wr_block_)
{
BOOST_ASSERT(d.ws.wr_block_ == &d);
d.ws.wr_block_ = nullptr;
}
d.fb.reset(); d.fb.reset();
d.state = do_read_fh; d.state = do_read_fh;
break; break;
} }
// send pong // send pong
if(! d.ws.wr_block_)
d.ws.wr_block_ = &d;
else
BOOST_ASSERT(d.ws.wr_block_ == &d);
d.state = do_pong + 1; d.state = do_pong + 1;
BOOST_ASSERT(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_, boost::asio::async_write(d.ws.stream_,
d.fb.data(), std::move(*this)); d.fb.data(), std::move(*this));
return; return;
@ -541,18 +549,25 @@ operator()(error_code ec,
//------------------------------------------------------------------ //------------------------------------------------------------------
case do_close_resume: case do_close_resume:
BOOST_ASSERT(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
d.state = do_close_resume + 1; d.state = do_close_resume + 1;
// The current context is safe but might not be
// the same as the one for this operation (since
// we are being called from a write operation).
// Call post to make sure we are invoked the same
// way as the final handler for this operation.
d.ws.get_io_service().post(bind_handler( d.ws.get_io_service().post(bind_handler(
std::move(*this), ec, bytes_transferred)); std::move(*this), ec, bytes_transferred));
return; return;
case do_close_resume + 1: case do_close_resume + 1:
BOOST_ASSERT(d.ws.wr_block_ == &d);
if(d.ws.failed_) if(d.ws.failed_)
{ {
// call handler // call handler
d.state = do_call_handler;
ec = boost::asio::error::operation_aborted; ec = boost::asio::error::operation_aborted;
break; goto upcall;
} }
if(d.ws.wr_close_) if(d.ws.wr_close_)
{ {
@ -566,10 +581,12 @@ operator()(error_code ec,
//------------------------------------------------------------------ //------------------------------------------------------------------
case do_close: case do_close:
if(! d.ws.wr_block_)
d.ws.wr_block_ = &d;
else
BOOST_ASSERT(d.ws.wr_block_ == &d);
d.state = do_teardown; d.state = do_teardown;
d.ws.wr_close_ = true; d.ws.wr_close_ = true;
BOOST_ASSERT(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_, boost::asio::async_write(d.ws.stream_,
d.fb.data(), std::move(*this)); d.fb.data(), std::move(*this));
return; return;