Reserved Will QoS Value 3 Is Accepted
Summary
wolfMQTT's CONNECT decoder accepts a malformed MQTT 3.1.1 packet where Will Flag is 1, but the two-bit Will QoS field is set to the reserved value 3.
In MQTT 3.1.1, Will QoS is valid only when Will Flag=1, and even then the only legal values are 0, 1, and 2. The value 3 is explicitly forbidden. wolfMQTT currently extracts the two-bit value directly from the CONNECT flags and stores it into the decoded LWT state without rejecting the reserved value. The stock broker then accepts the CONNECT packet and returns a successful CONNACK.
Standard Reference
MQTT Version 3.1.1, Section 3.1.2.6 Will Flag and Section 3.1.2.7 Will QoS:
Source: OASIS MQTT Version 3.1.1, Sections 3.1.2.6 and 3.1.2.7.
Original English requirement:
If the Will Flag is set to 1, the value of Will QoS can be 0 (0x00), 1
(0x01), or 2 (0x02). It MUST NOT be 3 (0x03) [MQTT-3.1.2-14].
Related English description from Section 3.1.2.6:
If the Will Flag is set to 1, the Will QoS and Will Retain fields in the
Connect Flags will be used by the Server, and the Will Topic and Will Message
fields MUST be present in the payload.
These rules mean that when Will Flag=1, the receiver must not merely extract the two-bit value. It must also enforce the legal set:
Will QoS in {0, 1, 2}
Will QoS != 3
Expected Behavior
For MQTT 3.1.1 CONNECT decoding:
| Will Flag |
Will QoS bits |
Meaning |
Expected result |
1 |
00 |
QoS 0 |
Accept |
1 |
01 |
QoS 1 |
Accept |
1 |
10 |
QoS 2 |
Accept |
1 |
11 |
Reserved value 3 |
Reject as malformed |
The important rule is not just that Will QoS is present when Will Flag=1, but that its value is restricted to the legal set {0,1,2}.
Code Description
Encoder constructs normal values from caller-provided LWT state
File: wolfMQTT-master/src/mqtt_packet.c
Function: MqttEncode_Connect()
Relevant code:
if (mc_connect->enable_lwt) {
packet.flags |= MQTT_CONNECT_FLAG_WILL_FLAG;
if (mc_connect->lwt_msg->qos) {
packet.flags |= MQTT_CONNECT_FLAG_SET_QOS(mc_connect->lwt_msg->qos);
}
if (mc_connect->lwt_msg->retain) {
packet.flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
}
}
The normal outbound path derives the CONNECT flags from the configured LWT state. This path does not itself prove that qos=3 is impossible from all callers, but its main role here is that the decoder cannot rely on construction-time discipline when parsing network input.
Decoder extracts Will QoS directly and does not reject value 3
File: wolfMQTT-master/src/mqtt_packet.c
Function: MqttDecode_Connect()
Relevant code:
mc_connect->enable_lwt =
(packet.flags & MQTT_CONNECT_FLAG_WILL_FLAG) ? 1 : 0;
When Will Flag=1, the decoder enters the LWT branch and directly assigns the two-bit Will QoS value:
mc_connect->lwt_msg->qos =
(MqttQoS)MQTT_CONNECT_FLAG_GET_QOS(packet.flags);
mc_connect->lwt_msg->retain =
(packet.flags & MQTT_CONNECT_FLAG_WILL_RETAIN) ? 1 : 0;
There is no validation equivalent to:
if (MQTT_CONNECT_FLAG_GET_QOS(packet.flags) == 3) {
return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA);
}
So a CONNECT packet with Will Flag=1 and Will QoS=3 is decoded as if qos=3 were a legitimate application value.
Broker stores the decoded reserved Will QoS value
File: wolfMQTT-master/src/mqtt_broker.c
Function: BrokerHandle_Connect()
Relevant code:
bc->will_qos = mc.lwt_msg->qos;
bc->will_retain = mc.lwt_msg->retain;
The broker does not reject a reserved Will QoS before storing it into the connection state. As a result, the malformed CONNECT can continue through the normal accepted-connection path.
Runtime Reproduction
A broker-level reproducer is available at:
wolfMQTT/301-350/repro_connect_will_qos_flag_validation.c
The malformed CONNECT packet uses:
| Field |
Value |
| Protocol Name |
MQTT |
| Protocol Level |
4 |
| Connect Flags |
0x1E |
Meaning of 0x1E |
Clean Session=1, Will Flag=1, Will QoS=3, Will Retain=0 |
| Payload |
ClientId cid, Will Topic t, Will Message m |
Build and run:
gcc -IwolfMQTT-master -D_WOLFMQTT_VS_SETTINGS_ -DWOLFMQTT_BROKER -DWOLFMQTT_BROKER_CUSTOM_NET -DNO_MAIN_DRIVER '-DWOLFMQTT_BROKER_GET_TIME_S()=0' '-DBROKER_SLEEP_MS(ms)=((void)0)' wolfMQTT\301-350\repro_connect_will_qos_flag_validation.c wolfMQTT-master\src\mqtt_broker.c wolfMQTT-master\src\mqtt_packet.c wolfMQTT-master\src\mqtt_client.c wolfMQTT-master\src\mqtt_socket.c -o wolfMQTT\301-350\repro_connect_will_qos_flag_validation.exe
wolfMQTT\301-350\repro_connect_will_qos_flag_validation.exe
Relevant observed output:
will flag 1 but qos=3 decode rc=23 accepted=yes enable_lwt=1 will_qos=3 will_retain=0
will flag 1 but qos=3 broker connack=20 02 00 00 return_code=0 accepted=yes
Interpretation:
MqttDecode_Connect() accepts the malformed CONNECT and decodes will_qos=3.
- The broker returns
20 02 00 00, which is a successful MQTT 3.1.1 CONNACK.
- Therefore the reserved
Will QoS value is not rejected on the receive path.
Inconsistency
| Standard requirement |
wolfMQTT behavior |
If Will Flag=1, Will QoS may be only 0, 1, or 2 |
Will QoS=3 is decoded and accepted |
Will QoS=3 MUST NOT be used |
Reserved value 3 is stored into LWT state |
| Malformed CONNECT flags should be rejected |
Broker returns successful CONNACK |
| Protocol-level value validation must constrain the decoded field |
Decoder only extracts bits; it does not validate the legal set |
Inconsistency Reason
The implementation performs this step:
extract the two-bit Will QoS value from the CONNECT flags
But it misses the required second step:
validate that the extracted value is one of {0,1,2}
That conflates:
with:
MQTT 3.1.1 requires both. wolfMQTT currently does only the first one for this case.
Impact
This is a receive-side protocol validation issue.
Potential impact:
| Effect |
Description |
| Protocol non-compliance |
A malformed CONNECT packet is accepted instead of rejected. |
| Reserved-value acceptance |
The implementation stores and propagates a forbidden Will QoS value. |
| Broker behavior ambiguity |
Later Will publication paths may operate on a non-standard QoS state. |
| Interoperability risk |
Strict MQTT peers can reject packets that wolfMQTT accepts. |
| Fuzzing blind spot |
Reserved flag-value combinations can enter the accepted CONNECT state machine. |
Suggested Fix Direction
MqttDecode_Connect() should reject reserved Will QoS=3 immediately after extracting the CONNECT flags and before decoding the Will payload fields:
if ((packet.flags & MQTT_CONNECT_FLAG_WILL_FLAG) &&
MQTT_CONNECT_FLAG_GET_QOS(packet.flags) == 3) {
return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA);
}
This should be applied in the same validation area that handles other CONNECT flag cross-constraints, so the receive path rejects invalid flag combinations before they can enter normal broker processing.
Reserved Will QoS Value 3 Is Accepted
Summary
wolfMQTT's CONNECT decoder accepts a malformed MQTT 3.1.1 packet where
Will Flagis1, but the two-bitWill QoSfield is set to the reserved value3.In MQTT 3.1.1,
Will QoSis valid only whenWill Flag=1, and even then the only legal values are0,1, and2. The value3is explicitly forbidden. wolfMQTT currently extracts the two-bit value directly from the CONNECT flags and stores it into the decoded LWT state without rejecting the reserved value. The stock broker then accepts the CONNECT packet and returns a successfulCONNACK.Standard Reference
MQTT Version 3.1.1, Section
3.1.2.6 Will Flagand Section3.1.2.7 Will QoS:Source: OASIS MQTT Version 3.1.1, Sections
3.1.2.6and3.1.2.7.Original English requirement:
Related English description from Section
3.1.2.6:These rules mean that when
Will Flag=1, the receiver must not merely extract the two-bit value. It must also enforce the legal set:Expected Behavior
For MQTT 3.1.1 CONNECT decoding:
1001011101113The important rule is not just that
Will QoSis present whenWill Flag=1, but that its value is restricted to the legal set{0,1,2}.Code Description
Encoder constructs normal values from caller-provided LWT state
File:
wolfMQTT-master/src/mqtt_packet.cFunction:
MqttEncode_Connect()Relevant code:
The normal outbound path derives the CONNECT flags from the configured LWT state. This path does not itself prove that
qos=3is impossible from all callers, but its main role here is that the decoder cannot rely on construction-time discipline when parsing network input.Decoder extracts Will QoS directly and does not reject value 3
File:
wolfMQTT-master/src/mqtt_packet.cFunction:
MqttDecode_Connect()Relevant code:
When
Will Flag=1, the decoder enters the LWT branch and directly assigns the two-bit Will QoS value:There is no validation equivalent to:
So a CONNECT packet with
Will Flag=1andWill QoS=3is decoded as ifqos=3were a legitimate application value.Broker stores the decoded reserved Will QoS value
File:
wolfMQTT-master/src/mqtt_broker.cFunction:
BrokerHandle_Connect()Relevant code:
The broker does not reject a reserved
Will QoSbefore storing it into the connection state. As a result, the malformed CONNECT can continue through the normal accepted-connection path.Runtime Reproduction
A broker-level reproducer is available at:
The malformed CONNECT packet uses:
MQTT40x1E0x1EClean Session=1,Will Flag=1,Will QoS=3,Will Retain=0cid, Will Topict, Will MessagemBuild and run:
Relevant observed output:
Interpretation:
MqttDecode_Connect()accepts the malformed CONNECT and decodeswill_qos=3.20 02 00 00, which is a successful MQTT 3.1.1CONNACK.Will QoSvalue is not rejected on the receive path.Inconsistency
Will Flag=1,Will QoSmay be only0,1, or2Will QoS=3is decoded and acceptedWill QoS=3MUST NOT be used3is stored into LWT stateCONNACKInconsistency Reason
The implementation performs this step:
But it misses the required second step:
That conflates:
with:
MQTT 3.1.1 requires both. wolfMQTT currently does only the first one for this case.
Impact
This is a receive-side protocol validation issue.
Potential impact:
Suggested Fix Direction
MqttDecode_Connect()should reject reservedWill QoS=3immediately after extracting the CONNECT flags and before decoding the Will payload fields:This should be applied in the same validation area that handles other CONNECT flag cross-constraints, so the receive path rejects invalid flag combinations before they can enter normal broker processing.