fix(mqtt5): Sanitize propery len/types to harden mqtt5-msg

This commit is contained in:
David Cermak
2026-01-26 12:10:05 +01:00
committed by Euripedes Rocha
parent 7dd8f41615
commit e427ebb7ea
+213 -2
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -11,6 +11,7 @@
#include "esp_log.h"
#define MQTT5_MAX_FIXED_HEADER_SIZE 5
#define MQTT5_MAX_PROPERTY_STRING_LEN (16 * 1024)
static const char *TAG = "mqtt5_msg";
@@ -74,6 +75,11 @@ static size_t get_variable_len(uint8_t *buffer, size_t offset, size_t buffer_len
return len;
}
static bool mqtt5_property_has_bytes(size_t property_offset, size_t needed, size_t property_len)
{
return property_offset <= property_len && needed <= (property_len - property_offset);
}
static int update_property_len_value(mqtt_connection_t *connection, size_t property_len, int property_offset)
{
uint8_t encoded_lens[4] = {0}, len_bytes = 0;
@@ -244,7 +250,16 @@ static mqtt5_user_property_handle_t mqtt5_msg_get_user_property(uint8_t *buffer,
switch (property_id) {
case MQTT5_PROPERTY_REASON_STRING: //only print now
if (!mqtt5_property_has_bytes(property_offset, 2, buffer_length)) {
goto err;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, buffer_length)) {
goto err;
}
ESP_LOGD(TAG, "MQTT5_PROPERTY_REASON_STRING %.*s", len, &property[property_offset]);
property_offset += len;
continue;
@@ -252,12 +267,32 @@ static mqtt5_user_property_handle_t mqtt5_msg_get_user_property(uint8_t *buffer,
case MQTT5_PROPERTY_USER_PROPERTY: {
uint8_t *key = NULL, *value = NULL;
size_t key_len = 0, value_len = 0;
if (!mqtt5_property_has_bytes(property_offset, 2, buffer_length)) {
goto err;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, buffer_length)) {
goto err;
}
key = &property[property_offset];
key_len = len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY key: %.*s", key_len, (char *)key);
property_offset += len;
if (!mqtt5_property_has_bytes(property_offset, 2, buffer_length)) {
goto err;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, buffer_length)) {
goto err;
}
value = &property[property_offset];
value_len = len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY value: %.*s", value_len, (char *)value);
@@ -366,24 +401,46 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length,
switch (property_id) {
case MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR:
if (!mqtt5_property_has_bytes(property_offset, 1, *property_len)) {
return NULL;
}
resp_property->payload_format_indicator = property[property_offset ++];
ESP_LOGD(TAG, "MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR %d", resp_property->payload_format_indicator);
continue;
case MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL:
if (!mqtt5_property_has_bytes(property_offset, 4, *property_len)) {
return NULL;
}
MQTT5_CONVERT_ONE_BYTE_TO_FOUR(resp_property->message_expiry_interval, property[property_offset ++],
property[property_offset ++], property[property_offset ++], property[property_offset ++])
ESP_LOGD(TAG, "MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL %"PRIu32, resp_property->message_expiry_interval);
continue;
case MQTT5_PROPERTY_TOPIC_ALIAS:
if (!mqtt5_property_has_bytes(property_offset, 2, *property_len)) {
return NULL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->topic_alias, property[property_offset ++], property[property_offset ++])
ESP_LOGD(TAG, "MQTT5_PROPERTY_TOPIC_ALIAS %d", resp_property->topic_alias);
continue;
case MQTT5_PROPERTY_RESPONSE_TOPIC:
if (!mqtt5_property_has_bytes(property_offset, 2, *property_len)) {
return NULL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->response_topic_len, property[property_offset ++],
property[property_offset ++])
if (resp_property->response_topic_len > MQTT5_MAX_PROPERTY_STRING_LEN ||
!mqtt5_property_has_bytes(property_offset, resp_property->response_topic_len, *property_len)) {
return NULL;
}
resp_property->response_topic = (char *)(property + property_offset);
property_offset += resp_property->response_topic_len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_RESPONSE_TOPIC %.*s", resp_property->response_topic_len, resp_property->response_topic);
@@ -398,7 +455,12 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length,
continue;
case MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER:
resp_property->subscribe_id = get_variable_len(property, property_offset, buffer_length, &len_bytes);
resp_property->subscribe_id = get_variable_len(property, property_offset, *property_len, &len_bytes);
if (!mqtt5_property_has_bytes(property_offset, len_bytes, *property_len)) {
return NULL;
}
property_offset += len_bytes;
ESP_LOGD(TAG, "MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER %d", resp_property->subscribe_id);
continue;
@@ -414,12 +476,32 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length,
case MQTT5_PROPERTY_USER_PROPERTY: {
uint8_t *key = NULL, *value = NULL;
size_t key_len = 0, value_len = 0;
if (!mqtt5_property_has_bytes(property_offset, 2, *property_len)) {
return NULL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, *property_len)) {
return NULL;
}
key = &property[property_offset];
key_len = len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY key: %.*s", key_len, (char *)key);
property_offset += len;
if (!mqtt5_property_has_bytes(property_offset, 2, *property_len)) {
return NULL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, *property_len)) {
return NULL;
}
value = &property[property_offset];
value_len = len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY value: %.*s", value_len, (char *)value);
@@ -436,7 +518,16 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length,
}
case MQTT5_PROPERTY_REASON_STRING: //only print now
if (!mqtt5_property_has_bytes(property_offset, 2, *property_len)) {
return NULL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, *property_len)) {
return NULL;
}
ESP_LOGD(TAG, "MQTT5_PROPERTY_REASON_STRING %.*s", len, &property[property_offset]);
property_offset += len;
continue;
@@ -475,6 +566,11 @@ char *mqtt5_get_suback_data(uint8_t *buffer, size_t *length, mqtt5_user_property
if (offset < totlen) {
size_t property_len = get_variable_len(buffer, offset, totlen, &len_bytes);
offset += len_bytes;
if (property_len > (totlen - offset)) {
goto err;
}
*user_property = mqtt5_msg_get_user_property(buffer + offset, property_len);
offset += property_len;
@@ -507,6 +603,12 @@ char *mqtt5_get_puback_data(uint8_t *buffer, size_t *length, mqtt5_user_property
if (offset < totlen) {
size_t property_len = get_variable_len(buffer, offset, totlen, &len_bytes);
offset += len_bytes;
if (property_len > (totlen - offset)) {
*length = 0;
return NULL;
}
*user_property = mqtt5_msg_get_user_property(buffer + offset, property_len);
}
@@ -686,6 +788,12 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
size_t property_len = get_variable_len(buffer, offset, buffer_len, &len_bytes);
offset += len_bytes;
uint16_t property_offset = 0, len = 0;
if (property_len > (buffer_len - offset)) {
ESP_LOGE(TAG, "Property length %d exceeds buffer bounds %d", property_len, buffer_len - offset);
return ESP_FAIL;
}
uint8_t *property = (buffer + offset);
while (property_offset < property_len) {
@@ -693,6 +801,10 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
switch (property_id) {
case MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL:
if (!mqtt5_property_has_bytes(property_offset, 4, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_FOUR(connection_property->session_expiry_interval, property[property_offset ++],
property[property_offset ++], property[property_offset ++], property[property_offset ++])
ESP_LOGD(TAG, "MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL %"PRIu32, connection_property->session_expiry_interval);
@@ -705,11 +817,19 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
continue;
case MQTT5_PROPERTY_MAXIMUM_QOS:
if (!mqtt5_property_has_bytes(property_offset, 1, property_len)) {
return ESP_FAIL;
}
resp_property->max_qos = property[property_offset ++];
ESP_LOGD(TAG, "MQTT5_PROPERTY_MAXIMUM_QOS %d", resp_property->max_qos);
continue;
case MQTT5_PROPERTY_RETAIN_AVAILABLE:
if (!mqtt5_property_has_bytes(property_offset, 1, property_len)) {
return ESP_FAIL;
}
resp_property->retain_available = property[property_offset ++];
ESP_LOGD(TAG, "MQTT5_PROPERTY_RETAIN_AVAILABLE %d", resp_property->retain_available);
continue;
@@ -721,7 +841,17 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
continue;
case MQTT5_PROPERTY_ASSIGNED_CLIENT_IDENTIFIER:
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
ESP_LOGE(TAG, "Sub-length %d exceeds property bounds", len);
return ESP_FAIL;
}
if (connection_info->client_id) {
free(connection_info->client_id);
}
@@ -746,7 +876,16 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
continue;
case MQTT5_PROPERTY_REASON_STRING: //only print now
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
ESP_LOGD(TAG, "MQTT5_PROPERTY_REASON_STRING %.*s", len, &property[property_offset]);
property_offset += len;
continue;
@@ -754,12 +893,32 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
case MQTT5_PROPERTY_USER_PROPERTY: {
uint8_t *key = NULL, *value = NULL;
size_t key_len = 0, value_len = 0;
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
key = &property[property_offset];
key_len = len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY key: %.*s", key_len, (char *)key);
property_offset += len;
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
value = &property[property_offset];
value_len = len;
ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY value: %.*s", value_len, (char *)value);
@@ -776,21 +935,37 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
}
case MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE:
if (!mqtt5_property_has_bytes(property_offset, 1, property_len)) {
return ESP_FAIL;
}
resp_property->wildcard_subscribe_available = property[property_offset++];
ESP_LOGD(TAG, "MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE %d", resp_property->wildcard_subscribe_available);
continue;
case MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE:
if (!mqtt5_property_has_bytes(property_offset, 1, property_len)) {
return ESP_FAIL;
}
resp_property->subscribe_identifiers_available = property[property_offset++];
ESP_LOGD(TAG, "MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE %d", resp_property->subscribe_identifiers_available);
continue;
case MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE:
if (!mqtt5_property_has_bytes(property_offset, 1, property_len)) {
return ESP_FAIL;
}
resp_property->shared_subscribe_available = property[property_offset++];
ESP_LOGD(TAG, "MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE %d", resp_property->shared_subscribe_available);
continue;
case MQTT5_PROPERTY_SERVER_KEEP_ALIVE:
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(connection_info->keepalive, property[property_offset ++], property[property_offset ++])
ESP_LOGD(TAG, "MQTT5_PROPERTY_SERVER_KEEP_ALIVE %lld", connection_info->keepalive);
continue;
@@ -800,7 +975,16 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
free(resp_property->response_info);
}
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
resp_property->response_info = calloc(1, len + 1);
if (!resp_property->response_info) {
@@ -815,19 +999,46 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m
continue;
case MQTT5_PROPERTY_SERVER_REFERENCE: //only print now
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
ESP_LOGD(TAG, "MQTT5_PROPERTY_SERVER_REFERENCE %.*s", len, &property[property_offset]);
property_offset += len;
continue;
case MQTT5_PROPERTY_AUTHENTICATION_METHOD: //only print now
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
ESP_LOGD(TAG, "MQTT5_PROPERTY_AUTHENTICATION_METHOD %.*s", len, &property[property_offset]);
property_offset += len;
continue;
case MQTT5_PROPERTY_AUTHENTICATION_DATA: //only print now
if (!mqtt5_property_has_bytes(property_offset, 2, property_len)) {
return ESP_FAIL;
}
MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++])
if (len > MQTT5_MAX_PROPERTY_STRING_LEN || !mqtt5_property_has_bytes(property_offset, len, property_len)) {
return ESP_FAIL;
}
ESP_LOGD(TAG, "MQTT5_PROPERTY_AUTHENTICATION_DATA length %d", len);
property_offset += len;
continue;