From fecedeb01833d01cbc26ad331597a554e7904086 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:20:09 -0400 Subject: [PATCH] [multiple] Fix crashes from malformed external input (batch 2) (#14651) Co-authored-by: Claude Opus 4.6 --- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 5 ++++ .../modbus_controller/modbus_controller.cpp | 23 +++++++++++++++++++ .../nextion/nextion_upload_arduino.cpp | 6 +++++ .../nextion/nextion_upload_esp32.cpp | 6 +++++ .../seeed_mr60bha2/seeed_mr60bha2.cpp | 2 +- .../components/usb_host/usb_host_client.cpp | 5 ++++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 73a298d279..0e2a515b40 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -514,6 +514,11 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) { continue; // Possible zero padded advertisement data } + // Validate field fits in remaining payload + if (offset + field_length > len) { + break; + } + // first byte of adv record is adv record type const uint8_t record_type = payload[offset++]; const uint8_t *record = &payload[offset]; diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 7f0eb230e0..f77f51a20d 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -59,6 +59,10 @@ bool ModbusController::send_next_command_() { // Queue incoming response void ModbusController::on_modbus_data(const std::vector &data) { + if (this->command_queue_.empty()) { + ESP_LOGW(TAG, "Received modbus data but command queue is empty"); + return; + } auto ¤t_command = this->command_queue_.front(); if (current_command != nullptr) { if (this->module_offline_) { @@ -92,6 +96,9 @@ void ModbusController::process_modbus_data_(const ModbusCommandItem *response) { void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) { ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code); + if (this->command_queue_.empty()) { + return; + } // Remove pending command waiting for a response auto ¤t_command = this->command_queue_.front(); if (current_command != nullptr) { @@ -175,6 +182,11 @@ void ModbusController::on_modbus_write_registers(uint8_t function_code, const st uint16_t payload_offset; if (function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) { + if (data.size() < 5) { + ESP_LOGW(TAG, "Write multiple registers data too short (%zu bytes)", data.size()); + this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE); + return; + } number_of_registers = uint16_t(data[3]) | (uint16_t(data[2]) << 8); if (number_of_registers == 0 || number_of_registers > modbus::MAX_NUM_OF_REGISTERS_TO_WRITE) { ESP_LOGW(TAG, "Invalid number of registers %d. Sending exception response.", number_of_registers); @@ -188,8 +200,19 @@ void ModbusController::on_modbus_write_registers(uint8_t function_code, const st this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE); return; } + if (data.size() < 5 + payload_size) { + ESP_LOGW(TAG, "Write multiple registers payload truncated (%zu bytes, expected %u)", data.size(), + 5 + payload_size); + this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE); + return; + } payload_offset = 5; } else if (function_code == ModbusFunctionCode::WRITE_SINGLE_REGISTER) { + if (data.size() < 4) { + ESP_LOGW(TAG, "Write single register data too short (%zu bytes)", data.size()); + this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE); + return; + } number_of_registers = 1; payload_offset = 2; } else { diff --git a/esphome/components/nextion/nextion_upload_arduino.cpp b/esphome/components/nextion/nextion_upload_arduino.cpp index e03f1f470b..6c454ab745 100644 --- a/esphome/components/nextion/nextion_upload_arduino.cpp +++ b/esphome/components/nextion/nextion_upload_arduino.cpp @@ -86,6 +86,12 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_, EspClass::getFreeHeap()); upload_first_chunk_sent_ = true; + if (recv_string.empty()) { + ESP_LOGW(TAG, "No response from display during upload"); + allocator.deallocate(buffer, 4096); + buffer = nullptr; + return -1; + } if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request char hex_buf[format_hex_pretty_size(NEXTION_MAX_RESPONSE_LOG_BYTES)]; ESP_LOGD( diff --git a/esphome/components/nextion/nextion_upload_esp32.cpp b/esphome/components/nextion/nextion_upload_esp32.cpp index 1014c728a8..166bbcc86a 100644 --- a/esphome/components/nextion/nextion_upload_esp32.cpp +++ b/esphome/components/nextion/nextion_upload_esp32.cpp @@ -108,6 +108,12 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r static_cast(esp_get_free_heap_size())); #endif upload_first_chunk_sent_ = true; + if (recv_string.empty()) { + ESP_LOGW(TAG, "No response from display during upload"); + allocator.deallocate(buffer, 4096); + buffer = nullptr; + return -1; + } if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request char hex_buf[format_hex_pretty_size(NEXTION_MAX_RESPONSE_LOG_BYTES)]; ESP_LOGD( diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp index 8628faac5a..1b5eaf6367 100644 --- a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp @@ -199,7 +199,7 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c } break; case DISTANCE_TYPE_BUFFER: - if (data[0] != 0) { + if (length >= 1 && data[0] != 0) { if (this->distance_sensor_ != nullptr && length >= 8) { uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]); float distance_float; diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp index c77d738ace..2a460d1a07 100644 --- a/esphome/components/usb_host/usb_host_client.cpp +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -492,6 +492,11 @@ bool USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u ESP_LOGE(TAG, "Too many requests queued"); return false; } + if (length > trq->transfer->data_buffer_size) { + ESP_LOGE(TAG, "transfer_in: data length %u exceeds buffer size %u", length, trq->transfer->data_buffer_size); + this->release_trq(trq); + return false; + } trq->callback = callback; trq->transfer->callback = transfer_callback; trq->transfer->bEndpointAddress = ep_address | USB_DIR_IN;