transport: reject initial frames with wild body sizes (#4620)

If someone opens a connection to port 9042 and sends some random bytes,
there is a 1 in 64 probability we'll recognize it as a valid frame
(since we only check the version byte, allowing versions 1-4) and we'll
try to read frame.length bytes for the body. If this value is very large,
we'll run out of memory very quickly.

Fix this by checking for reasonable body size (100kB). The initial message
must be a STARTUP, whose body is a [string map] of options, of which just
three are recognized. 100kB is plenty for future expansion.

Note that this does not replace true security on listening ports and
only serves to protect against mistakes, not attacks. An attacker can
easily exhaust server memory by opening many connections and trickle-feeding
them small amounts of data so they appear alive.

We can't use the config item native_transport_max_frame_size_in_mb,
because that can be legitimately large (and the default is atrocious,
256MB).

Fixes #4366.
This commit is contained in:
Avi Kivity
2019-07-01 20:02:34 +03:00
committed by Tomasz Grabiec
parent eb496b5eae
commit c987397e52

View File

@@ -329,7 +329,15 @@ cql_server::connection::read_frame() {
temporary_buffer<char> full(frame_size());
full.get_write()[0] = _version;
std::copy(tail.get(), tail.get() + tail.size(), full.get_write() + 1);
return make_ready_future<ret_type>(parse_frame(std::move(full)));
auto frame = parse_frame(std::move(full));
// This is the very first frame, so reject obviously incorrect frames, to
// avoid allocating large amounts of memory for the message body
if (frame.length > 100'000) {
// The STARTUP message body is a [string map] containing just a few options,
// so it should be smaller that 100kB. See #4366.
throw exceptions::protocol_exception(format("Initial message size too large ({:d}), rejecting as invalid", frame.length));
}
return make_ready_future<ret_type>(frame);
});
});
} else {