Skip to content

Commit 97842b0

Browse files
authored
Validate SUBACK/UNSUBACK reason code count (#432)
1 parent e35b9ca commit 97842b0

3 files changed

Lines changed: 67 additions & 11 deletions

File tree

source/v5/mqtt5_client.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3512,15 +3512,39 @@ int aws_mqtt5_client_service_operational_state(struct aws_mqtt5_client_operation
35123512
return AWS_OP_SUCCESS;
35133513
}
35143514

3515+
static bool s_aws_mqtt5_client_validate_ack_reason_code_count(
3516+
const struct aws_mqtt5_operation *operation,
3517+
enum aws_mqtt5_packet_type packet_type,
3518+
const void *packet_view) {
3519+
3520+
if (packet_type == AWS_MQTT5_PT_SUBACK) {
3521+
const struct aws_mqtt5_packet_subscribe_view *subscribe_view = operation->packet_view;
3522+
const struct aws_mqtt5_packet_suback_view *suback_view = packet_view;
3523+
3524+
return subscribe_view->subscription_count == suback_view->reason_code_count;
3525+
}
3526+
3527+
if (packet_type == AWS_MQTT5_PT_UNSUBACK) {
3528+
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view = operation->packet_view;
3529+
const struct aws_mqtt5_packet_unsuback_view *unsuback_view = packet_view;
3530+
3531+
return unsubscribe_view->topic_filter_count == unsuback_view->reason_code_count;
3532+
}
3533+
3534+
return true;
3535+
}
3536+
35153537
void aws_mqtt5_client_operational_state_handle_ack(
35163538
struct aws_mqtt5_client_operational_state *client_operational_state,
35173539
aws_mqtt5_packet_id_t packet_id,
35183540
enum aws_mqtt5_packet_type packet_type,
35193541
const void *packet_view,
35203542
int error_code) {
35213543

3544+
struct aws_mqtt5_client *client = client_operational_state->client;
3545+
35223546
if (packet_type == AWS_MQTT5_PT_PUBACK) {
3523-
aws_mqtt5_client_flow_control_state_on_puback(client_operational_state->client);
3547+
aws_mqtt5_client_flow_control_state_on_puback(client);
35243548
}
35253549

35263550
struct aws_hash_element *elem = NULL;
@@ -3530,23 +3554,45 @@ void aws_mqtt5_client_operational_state_handle_ack(
35303554
AWS_LOGF_ERROR(
35313555
AWS_LS_MQTT5_CLIENT,
35323556
"id=%p: received an ACK for an unknown operation with id %d",
3533-
(void *)client_operational_state->client,
3557+
(void *)client,
35343558
(int)packet_id);
35353559
return;
35363560
} else {
3537-
AWS_LOGF_TRACE(
3538-
AWS_LS_MQTT5_CLIENT,
3539-
"id=%p: Processing ACK with id %d",
3540-
(void *)client_operational_state->client,
3541-
(int)packet_id);
3561+
AWS_LOGF_TRACE(AWS_LS_MQTT5_CLIENT, "id=%p: Processing ACK with id %d", (void *)client, (int)packet_id);
35423562
}
35433563

35443564
struct aws_mqtt5_operation *operation = elem->value;
35453565

35463566
aws_linked_list_remove(&operation->node);
35473567
aws_hash_table_remove(&client_operational_state->unacked_operations_table, &packet_id, NULL, NULL);
35483568

3549-
s_complete_operation(client_operational_state->client, operation, error_code, packet_type, packet_view);
3569+
if (!s_aws_mqtt5_client_validate_ack_reason_code_count(operation, packet_type, packet_view)) {
3570+
AWS_LOGF_ERROR(
3571+
AWS_LS_MQTT5_CLIENT,
3572+
"id=%p: received a %s with a reason code count that does not match the acknowledged request; treating as a "
3573+
"protocol error and disconnecting",
3574+
(void *)client,
3575+
aws_mqtt5_packet_type_to_c_string(packet_type));
3576+
3577+
/*
3578+
* We must complete the operation explicitly rather than relying on the shutdown's operational-state reset:
3579+
* a subscribe/unsubscribe is retainable under the default offline queue policy, so the reset would requeue it
3580+
* for retry, potentially producing an endless disconnect/reconnect/resubscribe loop against a misbehaving
3581+
* broker.
3582+
*/
3583+
s_complete_operation(client, operation, AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR, AWS_MQTT5_PT_NONE, NULL);
3584+
3585+
if (s_should_client_disconnect_cleanly(client)) {
3586+
s_aws_mqtt5_client_shutdown_channel_clean(
3587+
client, AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR, AWS_MQTT5_DRC_PROTOCOL_ERROR);
3588+
} else {
3589+
s_aws_mqtt5_client_shutdown_channel(client, AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR);
3590+
}
3591+
3592+
return;
3593+
}
3594+
3595+
s_complete_operation(client, operation, error_code, packet_type, packet_view);
35503596
}
35513597

35523598
bool aws_mqtt5_client_are_negotiated_settings_valid(const struct aws_mqtt5_client *client) {

tests/v5/mqtt5_client_tests.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5529,7 +5529,7 @@ static int s_aws_mqtt5_server_send_aliased_publish_sequence(
55295529

55305530
struct aws_mqtt5_packet_suback_view suback_view = {
55315531
.packet_id = subscribe_view->packet_id,
5532-
.reason_code_count = 1,
5532+
.reason_code_count = subscribe_view->subscription_count,
55335533
.reason_codes = s_alias_reason_codes,
55345534
};
55355535

@@ -5727,7 +5727,7 @@ static int s_aws_mqtt5_server_send_aliased_publish_failure(
57275727

57285728
struct aws_mqtt5_packet_suback_view suback_view = {
57295729
.packet_id = subscribe_view->packet_id,
5730-
.reason_code_count = 1,
5730+
.reason_code_count = subscribe_view->subscription_count,
57315731
.reason_codes = s_alias_reason_codes,
57325732
};
57335733

tests/v5/mqtt5_to_mqtt3_adapter_tests.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3169,6 +3169,16 @@ static int s_mqtt5to3_adapter_subscribe_multi_oversized_suback_fn(struct aws_all
31693169

31703170
s_wait_for_n_adapter_operation_events(&fixture, AWS_MQTT3_OET_SUBSCRIBE_COMPLETE, 1);
31713171

3172+
struct aws_mqtt3_operation_event expected_events[] = {
3173+
{
3174+
.type = AWS_MQTT3_OET_SUBSCRIBE_COMPLETE,
3175+
.error_code = AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR,
3176+
},
3177+
};
3178+
3179+
ASSERT_SUCCESS(s_aws_mqtt5_to_mqtt3_adapter_test_fixture_verify_operation_sequence_contains(
3180+
&fixture, AWS_ARRAY_SIZE(expected_events), expected_events));
3181+
31723182
aws_mqtt5_to_mqtt3_adapter_test_fixture_clean_up(&fixture);
31733183
aws_mqtt_library_clean_up();
31743184

@@ -3885,7 +3895,7 @@ static int s_mqtt5_mock_server_handle_unsubscribe_unsuback_failure(
38853895

38863896
struct aws_mqtt5_packet_unsuback_view unsuback_view = {
38873897
.packet_id = unsubscribe_view->packet_id,
3888-
.reason_code_count = AWS_ARRAY_SIZE(mqtt5_unsuback_codes),
3898+
.reason_code_count = unsubscribe_view->topic_filter_count,
38893899
.reason_codes = mqtt5_unsuback_codes,
38903900
};
38913901

0 commit comments

Comments
 (0)