mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-02 20:42:04 +00:00
3cea474ccf
This fixes https://github.com/ClickHouse/ClickHouse/issues/33549 The infinite loop caused because sometimes the pos of ReadBuffer overflows the end. It happens f.e. when `MySQLPacketPayloadReadBuffer::nextImpl` might return empty buffer, because `in.nextIfAtEnd();` could not read more bytes and thus no bytes available (pos == end). It might happen when a network error or the connection to MySQL was closed or killed. This leads to empty `working_buffer` but successful returning from the func. And check `if (in.eof())` from `MySQLBinlogEventReadBuffer::nextImpl()` fails and also causes empty its `working_buffer` and successul return. At the end `payload.ignore(1)` and `payload.read(c)` produces the infinite loop since it is not eof() and ++pos overflows the end of the buffer. Should be tested by `test_mysql_kill*` from test.py
62 lines
1.9 KiB
C++
62 lines
1.9 KiB
C++
#include <IO/MySQLPacketPayloadReadBuffer.h>
|
|
|
|
namespace DB
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
{
|
|
extern const int UNKNOWN_PACKET_FROM_CLIENT;
|
|
}
|
|
|
|
const size_t MAX_PACKET_LENGTH = (1 << 24) - 1; // 16 mb
|
|
|
|
MySQLPacketPayloadReadBuffer::MySQLPacketPayloadReadBuffer(ReadBuffer & in_, uint8_t & sequence_id_)
|
|
: ReadBuffer(in_.position(), 0), in(in_), sequence_id(sequence_id_) // not in.buffer().begin(), because working buffer may include previous packet
|
|
{
|
|
}
|
|
|
|
bool MySQLPacketPayloadReadBuffer::nextImpl()
|
|
{
|
|
if (!has_read_header || (payload_length == MAX_PACKET_LENGTH && offset == payload_length))
|
|
{
|
|
has_read_header = true;
|
|
working_buffer.resize(0);
|
|
offset = 0;
|
|
payload_length = 0;
|
|
in.readStrict(reinterpret_cast<char *>(&payload_length), 3);
|
|
|
|
if (payload_length > MAX_PACKET_LENGTH)
|
|
throw Exception(ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT,
|
|
"Received packet with payload larger than max_packet_size: {}", payload_length);
|
|
|
|
size_t packet_sequence_id = 0;
|
|
in.readStrict(reinterpret_cast<char &>(packet_sequence_id));
|
|
if (packet_sequence_id != sequence_id)
|
|
throw Exception(ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT,
|
|
"Received packet with wrong sequence-id: {}. Expected: {}.", packet_sequence_id, static_cast<unsigned int>(sequence_id));
|
|
sequence_id++;
|
|
|
|
if (payload_length == 0)
|
|
return false;
|
|
}
|
|
else if (offset == payload_length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
in.nextIfAtEnd();
|
|
/// Don't return a buffer when no bytes available
|
|
if (!in.hasPendingData())
|
|
return false;
|
|
working_buffer = ReadBuffer::Buffer(in.position(), in.buffer().end());
|
|
size_t count = std::min(in.available(), payload_length - offset);
|
|
working_buffer.resize(count);
|
|
in.ignore(count);
|
|
|
|
offset += count;
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|