mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +02:00
Fix assert when basic_stream used as underlying of ssl::stream with 0-length write
Fixes #2065 Closes #2078
This commit is contained in:
@ -1,3 +1,9 @@
|
||||
Version XXX:
|
||||
|
||||
* Fix assert when basic_stream used as underlying of ssl::stream with zero-length write.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Version 301:
|
||||
|
||||
* Fix Travis CI bug.
|
||||
|
@ -56,19 +56,25 @@ struct stream_base
|
||||
|
||||
class pending_guard
|
||||
{
|
||||
bool& b_;
|
||||
bool* b_ = nullptr;
|
||||
bool clear_ = true;
|
||||
|
||||
public:
|
||||
~pending_guard()
|
||||
{
|
||||
if(clear_)
|
||||
b_ = false;
|
||||
if(clear_ && b_)
|
||||
*b_ = false;
|
||||
}
|
||||
|
||||
pending_guard()
|
||||
: b_(nullptr)
|
||||
, clear_(true)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
pending_guard(bool& b)
|
||||
: b_(b)
|
||||
: b_(&b)
|
||||
{
|
||||
// If this assert goes off, it means you are attempting
|
||||
// to issue two of the same asynchronous I/O operation
|
||||
@ -77,8 +83,8 @@ struct stream_base
|
||||
// calls to async_read_some. Only one pending call of
|
||||
// each I/O type (read and write) is permitted.
|
||||
//
|
||||
BOOST_ASSERT(! b_);
|
||||
b_ = true;
|
||||
BOOST_ASSERT(! *b_);
|
||||
*b_ = true;
|
||||
}
|
||||
|
||||
pending_guard(
|
||||
@ -89,11 +95,29 @@ struct stream_base
|
||||
{
|
||||
}
|
||||
|
||||
void assign(bool& b)
|
||||
{
|
||||
BOOST_ASSERT(!b_);
|
||||
BOOST_ASSERT(clear_);
|
||||
b_ = &b;
|
||||
|
||||
// If this assert goes off, it means you are attempting
|
||||
// to issue two of the same asynchronous I/O operation
|
||||
// at the same time, without waiting for the first one
|
||||
// to complete. For example, attempting two simultaneous
|
||||
// calls to async_read_some. Only one pending call of
|
||||
// each I/O type (read and write) is permitted.
|
||||
//
|
||||
BOOST_ASSERT(! *b_);
|
||||
*b_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
reset()
|
||||
{
|
||||
BOOST_ASSERT(clear_);
|
||||
b_ = false;
|
||||
if (b_)
|
||||
*b_ = false;
|
||||
clear_ = false;
|
||||
}
|
||||
};
|
||||
|
@ -269,6 +269,8 @@ class transfer_op
|
||||
std::move(*this));
|
||||
}
|
||||
|
||||
static bool never_pending_;
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
transfer_op(
|
||||
@ -278,10 +280,25 @@ public:
|
||||
: async_base<Handler, Executor>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, impl_(s.impl_)
|
||||
, pg_(state().pending)
|
||||
, pg_()
|
||||
, b_(b)
|
||||
{
|
||||
(*this)({});
|
||||
if (buffer_bytes(b_) == 0 && state().pending)
|
||||
{
|
||||
// Workaround:
|
||||
// Corner case discovered in https://github.com/boostorg/beast/issues/2065
|
||||
// Enclosing SSL stream wishes to complete a 0-length write early by
|
||||
// executing a 0-length read against the underlying stream.
|
||||
// This can occur even if an existing async_read is in progress.
|
||||
// In this specific case, we will complete the async op with no error
|
||||
// in order to prevent assertions and/or internal corruption of the basic_stream
|
||||
this->complete(false, error_code(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
pg_.assign(state().pending);
|
||||
(*this)({});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1366,6 +1366,34 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
testIssue2065()
|
||||
{
|
||||
using stream_type = basic_stream<tcp,
|
||||
net::io_context::executor_type>;
|
||||
|
||||
char buf[4];
|
||||
net::io_context ioc;
|
||||
std::memset(buf, 0, sizeof(buf));
|
||||
net::mutable_buffer mb(buf, sizeof(buf));
|
||||
auto const ep = net::ip::tcp::endpoint(
|
||||
net::ip::make_address("127.0.0.1"), 0);
|
||||
|
||||
// async_read_some
|
||||
|
||||
{
|
||||
// success
|
||||
test_server srv("*", ep, log);
|
||||
stream_type s(ioc);
|
||||
s.socket().connect(srv.local_endpoint());
|
||||
s.expires_never();
|
||||
s.async_read_some(mb, handler({}, 1));
|
||||
s.async_read_some(net::buffer(buf, 0), handler({}, 0));
|
||||
ioc.run();
|
||||
ioc.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
@ -1382,6 +1410,7 @@ public:
|
||||
boost::ignore_unused(&basic_stream_test::testAwaitableCompilation);
|
||||
#endif
|
||||
boost::ignore_unused(&basic_stream_test::testConnectionConditionArgs);
|
||||
testIssue2065();
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user