mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +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:
|
Version 301:
|
||||||
|
|
||||||
* Fix Travis CI bug.
|
* Fix Travis CI bug.
|
||||||
|
@@ -56,19 +56,25 @@ struct stream_base
|
|||||||
|
|
||||||
class pending_guard
|
class pending_guard
|
||||||
{
|
{
|
||||||
bool& b_;
|
bool* b_ = nullptr;
|
||||||
bool clear_ = true;
|
bool clear_ = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~pending_guard()
|
~pending_guard()
|
||||||
{
|
{
|
||||||
if(clear_)
|
if(clear_ && b_)
|
||||||
b_ = false;
|
*b_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_guard()
|
||||||
|
: b_(nullptr)
|
||||||
|
, clear_(true)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
pending_guard(bool& b)
|
pending_guard(bool& b)
|
||||||
: b_(b)
|
: b_(&b)
|
||||||
{
|
{
|
||||||
// If this assert goes off, it means you are attempting
|
// If this assert goes off, it means you are attempting
|
||||||
// to issue two of the same asynchronous I/O operation
|
// 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
|
// calls to async_read_some. Only one pending call of
|
||||||
// each I/O type (read and write) is permitted.
|
// each I/O type (read and write) is permitted.
|
||||||
//
|
//
|
||||||
BOOST_ASSERT(! b_);
|
BOOST_ASSERT(! *b_);
|
||||||
b_ = true;
|
*b_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pending_guard(
|
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
|
void
|
||||||
reset()
|
reset()
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(clear_);
|
BOOST_ASSERT(clear_);
|
||||||
b_ = false;
|
if (b_)
|
||||||
|
*b_ = false;
|
||||||
clear_ = false;
|
clear_ = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -269,6 +269,8 @@ class transfer_op
|
|||||||
std::move(*this));
|
std::move(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool never_pending_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<class Handler_>
|
template<class Handler_>
|
||||||
transfer_op(
|
transfer_op(
|
||||||
@@ -278,10 +280,25 @@ public:
|
|||||||
: async_base<Handler, Executor>(
|
: async_base<Handler, Executor>(
|
||||||
std::forward<Handler_>(h), s.get_executor())
|
std::forward<Handler_>(h), s.get_executor())
|
||||||
, impl_(s.impl_)
|
, impl_(s.impl_)
|
||||||
, pg_(state().pending)
|
, pg_()
|
||||||
, b_(b)
|
, 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
|
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
|
void
|
||||||
run()
|
run()
|
||||||
{
|
{
|
||||||
@@ -1382,6 +1410,7 @@ public:
|
|||||||
boost::ignore_unused(&basic_stream_test::testAwaitableCompilation);
|
boost::ignore_unused(&basic_stream_test::testAwaitableCompilation);
|
||||||
#endif
|
#endif
|
||||||
boost::ignore_unused(&basic_stream_test::testConnectionConditionArgs);
|
boost::ignore_unused(&basic_stream_test::testConnectionConditionArgs);
|
||||||
|
testIssue2065();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user