99
1010#include " kafka/protocol/flex_versions.h"
1111
12+ #include " base/units.h"
1213#include " kafka/protocol/messages.h"
1314#include " kafka/protocol/types.h"
1415#include " kafka/protocol/wire.h"
1718
1819#include < seastar/core/iostream.hh>
1920
20- #include < stdexcept>
21-
2221namespace kafka {
2322
2423namespace {
@@ -49,6 +48,11 @@ get_flexible_request_min_versions_list(type_list<RequestTypes...> r) {
4948constexpr auto g_flex_mapping = get_flexible_request_min_versions_list(
5049 request_types ());
5150
51+ struct protocol_parse_exception : public net ::parsing_exception {
52+ explicit protocol_parse_exception (const std::string& m)
53+ : net::parsing_exception(m) {}
54+ };
55+
5256} // namespace
5357
5458bool flex_versions::is_flexible_request (api_key key, api_version version) {
@@ -67,9 +71,20 @@ bool flex_versions::is_api_in_schema(api_key key) noexcept {
6771 return first_flex_version != invalid_api;
6872}
6973
74+ namespace {
75+ // TODO(C++26): replace with std::sub_sat
76+ size_t sub_sat (size_t a, size_t b) {
77+ size_t c = 0 ;
78+ if (!__builtin_sub_overflow (a, b, &c)) {
79+ return c;
80+ }
81+ return 0 ;
82+ }
83+ } // namespace
84+
7085ss::future<std::pair<std::optional<tagged_fields>, size_t >>
7186// NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters)
72- parse_tags (ss::input_stream<char >& src) {
87+ parse_tags (ss::input_stream<char >& src, size_t max_bytes ) {
7388 size_t total_bytes_read = 0 ;
7489 auto read_unsigned_vint =
7590 // NOLINTNEXTLINE(cppcoreguidelines-avoid-reference-coroutine-parameters)
@@ -94,26 +109,44 @@ parse_tags(ss::input_stream<char>& src) {
94109 while (num_tags-- > 0 ) {
95110 auto id = co_await read_unsigned_vint (total_bytes_read, src);
96111 auto next_len = co_await read_unsigned_vint (total_bytes_read, src);
112+ if (next_len > sub_sat (max_bytes, total_bytes_read)) {
113+ throw protocol_parse_exception (
114+ fmt::format (
115+ " tagged field {} length {} exceeds remaining message budget {}" ,
116+ id,
117+ next_len,
118+ max_bytes - total_bytes_read));
119+ }
97120 if (next_len > 128_KiB) {
98- throw std::invalid_argument (
99- fmt::format (" Too large of a tagged field: {}" , next_len));
121+ throw protocol_parse_exception (
122+ fmt::format (
123+ " tagged field {} length {} exceeds 128 KiB limit" ,
124+ id,
125+ next_len));
100126 }
101127 auto buf = co_await src.read_exactly (next_len);
128+ if (buf.size () != next_len) {
129+ throw protocol_parse_exception (
130+ fmt::format (
131+ " short read for tagged field {} length {} but got {}" ,
132+ id,
133+ next_len,
134+ buf.size ()));
135+ }
102136 bytes data (bytes::initialized_later{}, buf.size ());
103137 std::copy_n (buf.begin (), buf.size (), data.begin ());
104138 total_bytes_read += next_len;
105139 auto [_, succeded] = tags.emplace (tag_id (id), std::move (data));
106140 if (!succeded) {
107- throw std::logic_error (
108- fmt::format (" Protocol error, duplicate tag id detected, {}" , id));
141+ throw protocol_parse_exception (
142+ fmt::format (" duplicate tag id detected: {}" , id));
109143 }
110144 }
111145 co_return std::make_pair (std::move (tags), total_bytes_read);
112146}
113147
114148namespace {
115149struct invalid_buffer_size_exception : public net ::parsing_exception {
116- public:
117150 explicit invalid_buffer_size_exception (const std::string& m)
118151 : net::parsing_exception(m) {}
119152};
0 commit comments