Skip to content

Commit

Permalink
[quic] Handle receiving a malformed QUIC frame type
Browse files Browse the repository at this point in the history
If we fail to parse a frame type, for example due to a truncated
variable-length integer, close the connection with a
`FRAME_ENCODING_ERROR`.

If we receive an unknown frame type, close the connection with
`FRAME_ENCODING_ERROR` too, not `PROTOCOL_VIOLATION`.

https://datatracker.ietf.org/doc/html/rfc9000#name-frames-and-frame-types

Fixes: aiortc#406
  • Loading branch information
jlaine committed Nov 16, 2023
1 parent c7473f6 commit e9b2579
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 7 deletions.
14 changes: 11 additions & 3 deletions src/aioquic/quic/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2292,19 +2292,27 @@ def _payload_received(
is_ack_eliciting = False
is_probing = None
while not buf.eof():
frame_type = buf.pull_uint_var()
# get frame type
try:
frame_type = buf.pull_uint_var()
except BufferReadError:
raise QuicConnectionError(
error_code=QuicErrorCode.FRAME_ENCODING_ERROR,
frame_type=None,
reason_phrase="Malformed frame type",
)

# check frame type is known
try:
frame_handler, frame_epochs = self.__frame_handlers[frame_type]
except KeyError:
raise QuicConnectionError(
error_code=QuicErrorCode.PROTOCOL_VIOLATION,
error_code=QuicErrorCode.FRAME_ENCODING_ERROR,
frame_type=frame_type,
reason_phrase="Unknown frame type",
)

# check frame is allowed for the epoch
# check frame type is allowed for the epoch
if context.epoch not in frame_epochs:
raise QuicConnectionError(
error_code=QuicErrorCode.PROTOCOL_VIOLATION,
Expand Down
21 changes: 17 additions & 4 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2325,16 +2325,29 @@ def test_payload_received_padding_only(self):
self.assertFalse(is_ack_eliciting)
self.assertTrue(is_probing)

def test_payload_received_unknown_frame(self):
def test_payload_received_malformed_frame_type(self):
with client_and_server() as (client, server):
# client receives unknown frame
# client receives a malformed frame type
with self.assertRaises(QuicConnectionError) as cm:
client._payload_received(client_receive_context(client), b"\xff")
self.assertEqual(
cm.exception.error_code, QuicErrorCode.FRAME_ENCODING_ERROR
)
self.assertEqual(cm.exception.frame_type, None)
self.assertEqual(cm.exception.reason_phrase, "Malformed frame type")

def test_payload_received_unknown_frame_type(self):
with client_and_server() as (client, server):
# client receives unknown frame type
with self.assertRaises(QuicConnectionError) as cm:
client._payload_received(client_receive_context(client), b"\x1f")
self.assertEqual(cm.exception.error_code, QuicErrorCode.PROTOCOL_VIOLATION)
self.assertEqual(
cm.exception.error_code, QuicErrorCode.FRAME_ENCODING_ERROR
)
self.assertEqual(cm.exception.frame_type, 0x1F)
self.assertEqual(cm.exception.reason_phrase, "Unknown frame type")

def test_payload_received_unexpected_frame(self):
def test_payload_received_unexpected_frame_type(self):
with client_and_server() as (client, server):
# client receives CRYPTO frame in 0-RTT
with self.assertRaises(QuicConnectionError) as cm:
Expand Down

0 comments on commit e9b2579

Please sign in to comment.