Uploaded image for project: 'OASIS Message Queuing Telemetry Transport (MQTT) TC'
  1. OASIS Message Queuing Telemetry Transport (MQTT) TC
  2. MQTT-275

Erratum: Error in Remaining Length Decoding Algorithm

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Fixed
    • Affects Version/s: 3.1.1
    • Fix Version/s: 3.1.1
    • Component/s: core
    • Labels:
      None
    • Proposal:
      Hide

      Suggested Correction to the MQTT 3.1.1 Standard

      To correct this error, it is only necessary to move the range check so that it precedes the multiplier update. The corrected algorithm appears below:

      1 multiplier = 1
      2 value = 0
      3
      4 do
      5 encodedByte = 'next byte from stream'
      6 value += (encodedByte AND 127) * multiplier
      7 if (multiplier > 128*128*128)
      8 throw Error(Malformed Remaining Length)
      9 multiplier *= 128 <<- Now follows range check
      10 while ((encodedByte AND 128) != 0)

      With this change, the algorithm correctly decodes all values from 0 to 268,435,455, and rejects encoded values greater than 268,435,455 as required by the Standard. [NB: Both the original and corrected version contain an unnecessary multiplication (line 8 and 9) respectively on the last iteration of the loop.]

      Show
      Suggested Correction to the MQTT 3.1.1 Standard To correct this error, it is only necessary to move the range check so that it precedes the multiplier update. The corrected algorithm appears below: 1 multiplier = 1 2 value = 0 3 4 do 5 encodedByte = 'next byte from stream' 6 value += (encodedByte AND 127) * multiplier 7 if (multiplier > 128*128*128) 8 throw Error(Malformed Remaining Length) 9 multiplier *= 128 <<- Now follows range check 10 while ((encodedByte AND 128) != 0) With this change, the algorithm correctly decodes all values from 0 to 268,435,455, and rejects encoded values greater than 268,435,455 as required by the Standard. [NB: Both the original and corrected version contain an unnecessary multiplication (line 8 and 9) respectively on the last iteration of the loop.]

      Description

      While testing MQTT, I discovered an error in the Standard that will cause implementations to fail upon reception of PUBLISH messages in which the combination of topic length, QoS level, and payload size result in a fixed header remaining length > 2,097,151.

      The culprit is an incorrect loop termination check in the algorithm for decoding the remaining length field provided in § 2.2.3. It causes decoding to fail for all encoded values greater than 2,097,151. Consequently, reception fails with valid PUBLISH messages for which 2,097,152 ≤ remaining length ≤ 268,435,455 holds. This failure is not recoverable by retransmission.

      Although the algorithm is contained in a section labeled "non-normative comment", a correction to the Standard seems desirable because developers are likely to copy the algorithm verbatim.

      Applicability and Impact

      This error affects implementations using the algorithm in the MQTT 3.1.1 Standard. Implementations using other algorithms, including the older algorithm contained in MQTT 3.1, will not encounter this problem. However, as noted in MQTT-29 item 3, the MQTT 3.1 algorithm is susceptible to buffer overflow attacks and similar problems because it cannot detect malformed remaining length encoding.

      Session partners of affected implementations can be indirectly affected because they will not receive acknowledgements from affected systems when using durable sessions and QoS > 0 because the failure is unrecoverable by retransmission.

      Origin

      This error does not appear in the MQTT 3.1 Standard, and seems to have been introduced in MQTT 3.1.1 WD08 or WD09, possibly in response to safety concerns raised in MQTT-29 (item 3).

      Description of Error

      Non-normative comments in § 2.2.3 presents the following algorithm for decoding the remaining length field. (line numbers and emphasis added) :

      1 multiplier = 1
      2 value = 0
      3
      4 do
      5 encodedByte = 'next byte from stream'
      6 value += (encodedByte AND 127) * multiplier
      7 multiplier *= 128
      8 if (multiplier > 128*128*128) << – Fails
      9 throw Error(Malformed Remaining Length)
      10 while ((encodedByte AND 128) != 0)

      The range check (line 8) fails because 4 bytes are required to encode values greater than 2,097,151. In the fourth iteration of the do-while loop, the multiplier exceeds the limit of 128*128*128 causing the range check to fail.

        Attachments

          Activity

            People

            • Assignee:
              ragupta2 Rahul Gupta
              Reporter:
              edbriggs Ed Briggs [X] (Inactive)
            • Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: