sync receive_message raises ValueError instead of ProtocolError for OP_COMPRESSED messages with 16 < length ≤ 25

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Unknown
    • None
    • Affects Version/s: None
    • Component/s: None
    • None
    • None
    • Python Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      receive_message (sync path) is missing the OP_COMPRESSED minimum-length guard that exists in the async PyMongoProtocol.process_header.

      Affected code

      pymongo/network_layer.pyreceive_message (sync), line ~777:

      if op_code == 2012:
          op_code, _, compressor_id = _UNPACK_COMPRESSION_HEADER(receive_data(conn, 9, deadline))
          data = decompress(receive_data(conn, length - 25, deadline), compressor_id)  # ← negative count possible
      

      Root cause

      The only length guard in receive_message is if length <= 16: raise ProtocolError(...), which applies to all opcodes. For op_code == 2012 (OP_COMPRESSED), the minimum valid total message length is 26 (16-byte header + 9-byte compression sub-header + at least 1 byte of payload). The sync path does not enforce this.

      For a malformed OP_COMPRESSED frame with 16 < length ≤ 24, length - 25 is negative. receive_data immediately calls bytearray(negative_number), which raises ValueError: bytearray() argument 2 cannot be negative — not ProtocolError.

      For length == 25, receive_data(conn, 0, deadline) succeeds (returns an empty buffer), and decompress is called on 0 bytes — behavior is compressor-dependent.

      Async parity

      PyMongoProtocol.process_header has the correct guard:

      if op_code == 2012:
          if length <= 25:
              raise ProtocolError(
                  f"Message length ({length!r}) not longer than standard OP_COMPRESSED message header size (25)"
              )
      

      Fix

      Add the same guard to receive_message before entering the op_code == 2012 branch:

      if op_code == 2012:
          if length <= 25:
              raise ProtocolError(
                  f"Message length ({length!r}) not longer than standard OP_COMPRESSED message header size (25)"
              )
          ...
      

      Discovery

      Found during code review of PR #2774 (coverage increase for network_layer.py). The new tests do not cover the OP_COMPRESSED sync path, so this gap was not caught by the test suite.

            Assignee:
            Unassigned
            Reporter:
            Alex Clark
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: