[hlk_fm22x] Add bounds checks and fix format specifiers

- Flush UART RX buffer when response exceeds max size
- Guard handle_note_ against zero-length data
- Guard handle_reply_ against length < 2
- Validate VERIFY response has full name payload before access
- Guard GET_VERSION against length underflow
- Cast %.*s precision to int, use %zu for size_t
- Improve MAX_RESPONSE_SIZE comment with payload layout
This commit is contained in:
J. Nick Koston
2026-02-08 02:38:40 -06:00
parent 2e50651400
commit 5374252470
2 changed files with 18 additions and 4 deletions

View File

@@ -135,6 +135,8 @@ void HlkFm22xComponent::recv_command_() {
if (length > HLK_FM22X_MAX_RESPONSE_SIZE) {
ESP_LOGE(TAG, "Response too large: %u bytes", length);
while (this->available())
this->read();
return;
}
@@ -169,10 +171,14 @@ void HlkFm22xComponent::recv_command_() {
}
void HlkFm22xComponent::handle_note_(const uint8_t *data, size_t length) {
if (length < 1) {
ESP_LOGE(TAG, "Empty note data");
return;
}
switch (data[0]) {
case HlkFm22xNoteType::FACE_STATE:
if (length < 17) {
ESP_LOGE(TAG, "Invalid face note data size: %u", length);
ESP_LOGE(TAG, "Invalid face note data size: %zu", length);
break;
}
{
@@ -212,6 +218,10 @@ void HlkFm22xComponent::handle_note_(const uint8_t *data, size_t length) {
void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
auto expected = this->active_command_;
this->active_command_ = HlkFm22xCommand::NONE;
if (length < 2) {
ESP_LOGE(TAG, "Reply too short: %zu bytes", length);
return;
}
if (data[0] != (uint8_t) expected) {
ESP_LOGE(TAG, "Unexpected response command. Expected: 0x%.2X, Received: 0x%.2X", expected, data[0]);
return;
@@ -238,9 +248,13 @@ void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
}
switch (expected) {
case HlkFm22xCommand::VERIFY: {
if (length < 4 + HLK_FM22X_NAME_SIZE) {
ESP_LOGE(TAG, "VERIFY response too short: %zu bytes", length);
break;
}
int16_t face_id = ((int16_t) data[2] << 8) | data[3];
const char *name_ptr = reinterpret_cast<const char *>(data + 4);
ESP_LOGD(TAG, "Face verified. ID: %d, name: %.*s", face_id, HLK_FM22X_NAME_SIZE, name_ptr);
ESP_LOGD(TAG, "Face verified. ID: %d, name: %.*s", face_id, (int) HLK_FM22X_NAME_SIZE, name_ptr);
if (this->last_face_id_sensor_ != nullptr) {
this->last_face_id_sensor_->publish_state(face_id);
}
@@ -266,7 +280,7 @@ void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) {
this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_VERSION); });
break;
case HlkFm22xCommand::GET_VERSION:
if (this->version_text_sensor_ != nullptr) {
if (this->version_text_sensor_ != nullptr && length > 2) {
this->version_text_sensor_->publish_state(reinterpret_cast<const char *>(data + 2), length - 2);
}
this->defer([this]() { this->get_face_count_(); });

View File

@@ -14,7 +14,7 @@ namespace esphome::hlk_fm22x {
static const uint16_t START_CODE = 0xEFAA;
static constexpr size_t HLK_FM22X_NAME_SIZE = 32;
// Maximum response size is 36 bytes (VERIFY reply: face_id + 32-byte name)
// Maximum response payload: 1-byte command + 1-byte result + 2-byte face_id + 32-byte name = 36
static constexpr size_t HLK_FM22X_MAX_RESPONSE_SIZE = 36;
enum HlkFm22xCommand {
NONE = 0x00,