diff --git a/pymongo/network_layer.py b/pymongo/network_layer.py index 2295af7198..102f560d65 100644 --- a/pymongo/network_layer.py +++ b/pymongo/network_layer.py @@ -775,6 +775,10 @@ def receive_message( ) data: memoryview | bytes if op_code == 2012: + if length <= 25: + raise ProtocolError( + f"Message length ({length!r}) not longer than standard OP_COMPRESSED message header size (25)" + ) op_code, _, compressor_id = _UNPACK_COMPRESSION_HEADER(receive_data(conn, 9, deadline)) data = decompress(receive_data(conn, length - 25, deadline), compressor_id) else: diff --git a/test/test_network_layer.py b/test/test_network_layer.py index 1c2fd1e01c..725d1b9597 100644 --- a/test/test_network_layer.py +++ b/test/test_network_layer.py @@ -80,6 +80,21 @@ def test_length_exceeds_max_raises(self): with self.assertRaisesRegex(ProtocolError, "larger than server max"): network_layer.receive_message(conn, request_id=None) + def test_compressed_length_too_small_raises(self): + # An OP_COMPRESSED frame with 16 < length <= 25 is malformed: it lacks + # room for the 9-byte compression sub-header plus payload. It must raise + # ProtocolError (not ValueError from a negative receive_data count). + for length in (17, 24, 25): + conn = _make_conn() + _mock_recv_into( + conn, + pack_msg_header(length=length, request_id=0, response_to=0, op_code=2012), + ) + with self.assertRaisesRegex( + ProtocolError, "not longer than standard OP_COMPRESSED message header" + ): + network_layer.receive_message(conn, request_id=None) + def test_unknown_opcode_raises(self): conn = _make_conn() _mock_recv_into(