From ef1c12c21f0c747385f1d8b4f57c44aa7d88a49c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Oct 2025 17:37:50 -0500 Subject: [PATCH 1/7] adjust --- esphome/components/api/api_frame_helper.h | 11 ++++++----- .../components/api/api_frame_helper_noise.cpp | 16 +++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 815064c973..9aaada3cf7 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -19,13 +19,14 @@ namespace esphome::api { //#define HELPER_LOG_PACKETS // Maximum message size limits to prevent OOM on constrained devices -// Voice Assistant is our largest user at 1024 bytes per audio chunk -// Using 2048 + 256 bytes overhead = 2304 bytes total to support voice and future needs -// ESP8266 has very limited RAM and cannot support voice assistant +// Handshake messages are limited to a small size for security +static constexpr uint16_t MAX_HANDSHAKE_SIZE = 128; + +// Data message limits vary by platform based on available memory #ifdef USE_ESP8266 -static constexpr uint16_t MAX_MESSAGE_SIZE = 512; // Keep small for memory constrained ESP8266 +static constexpr uint16_t MAX_MESSAGE_SIZE = 8192; // 8 KiB for ESP8266 #else -static constexpr uint16_t MAX_MESSAGE_SIZE = 2304; // Support voice (1024) + headroom for larger messages +static constexpr uint16_t MAX_MESSAGE_SIZE = 32768; // 32 KiB for ESP32 and other platforms #endif // Forward declaration diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index 6bb9b68f89..1213e65948 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -168,18 +168,12 @@ APIError APINoiseFrameHelper::try_read_frame_() { // read body uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2]; - if (state_ != State::DATA && msg_size > 128) { - // for handshake message only permit up to 128 bytes + // Check against size limits to prevent OOM: MAX_HANDSHAKE_SIZE for handshake, MAX_MESSAGE_SIZE for data + uint16_t limit = (state_ == State::DATA) ? MAX_MESSAGE_SIZE : MAX_HANDSHAKE_SIZE; + if (msg_size > limit) { state_ = State::FAILED; - HELPER_LOG("Bad packet len for handshake: %d", msg_size); - return APIError::BAD_HANDSHAKE_PACKET_LEN; - } - - // Check against maximum message size to prevent OOM - if (msg_size > MAX_MESSAGE_SIZE) { - state_ = State::FAILED; - HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, MAX_MESSAGE_SIZE); - return APIError::BAD_DATA_PACKET; + HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, limit); + return (state_ == State::DATA) ? APIError::BAD_DATA_PACKET : APIError::BAD_HANDSHAKE_PACKET_LEN; } // Reserve space for body From cb578c219857d06afb64fc6697b9aa0819d23ad1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Oct 2025 18:16:20 -0500 Subject: [PATCH 2/7] Update test_oversized_payloads.py --- tests/integration/test_oversized_payloads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_oversized_payloads.py b/tests/integration/test_oversized_payloads.py index 22167118af..bf2722159f 100644 --- a/tests/integration/test_oversized_payloads.py +++ b/tests/integration/test_oversized_payloads.py @@ -162,7 +162,7 @@ async def test_oversized_payload_noise( assert device_info.name == "oversized-noise" # Create an oversized payload (>2304 bytes which is our new limit) - oversized_data = b"Y" * 3000 # ~3KiB, exceeds the 2304 byte limit + oversized_data = b"Y" * 32769 # ~32KiB, exceeds the 32 Kbyte limit # Access the internal connection to send raw data frame_helper = client._connection._frame_helper From b9e2a30a388eba8b89950572d398b3f6ff25cd96 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Oct 2025 18:17:17 -0500 Subject: [PATCH 3/7] Update test_oversized_payloads.py --- tests/integration/test_oversized_payloads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_oversized_payloads.py b/tests/integration/test_oversized_payloads.py index bf2722159f..3e0a7b655c 100644 --- a/tests/integration/test_oversized_payloads.py +++ b/tests/integration/test_oversized_payloads.py @@ -161,7 +161,7 @@ async def test_oversized_payload_noise( assert device_info is not None assert device_info.name == "oversized-noise" - # Create an oversized payload (>2304 bytes which is our new limit) + # Create an oversized payload (>32Kbytes which is our new limit) oversized_data = b"Y" * 32769 # ~32KiB, exceeds the 32 Kbyte limit # Access the internal connection to send raw data From 7682b4e9a3f37bc957ee3d25b62e897b9126bc1f Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 7 Oct 2025 19:35:42 -0400 Subject: [PATCH 4/7] [audio] Update esp-audio-libs 2.0.1 to use new FLAC decoder (#10974) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .clang-tidy.hash | 2 +- esphome/components/audio/__init__.py | 2 +- esphome/components/audio/audio_decoder.cpp | 18 +++++++++--------- platformio.ini | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.clang-tidy.hash b/.clang-tidy.hash index 60780b684b..4901c0ccac 100644 --- a/.clang-tidy.hash +++ b/.clang-tidy.hash @@ -1 +1 @@ -ab49c22900dd39c004623e450a1076b111d6741f31967a637ab6e0e3dd2e753e +049d60eed541730efaa4c0dc5d337b4287bf29b6daa350b5dfc1f23915f1c52f diff --git a/esphome/components/audio/__init__.py b/esphome/components/audio/__init__.py index f657cb5da3..7b03e4b6a7 100644 --- a/esphome/components/audio/__init__.py +++ b/esphome/components/audio/__init__.py @@ -165,4 +165,4 @@ def final_validate_audio_schema( async def to_code(config): - cg.add_library("esphome/esp-audio-libs", "1.1.4") + cg.add_library("esphome/esp-audio-libs", "2.0.1") diff --git a/esphome/components/audio/audio_decoder.cpp b/esphome/components/audio/audio_decoder.cpp index 90ba1aec1e..d1ad571a52 100644 --- a/esphome/components/audio/audio_decoder.cpp +++ b/esphome/components/audio/audio_decoder.cpp @@ -229,18 +229,18 @@ FileDecoderState AudioDecoder::decode_flac_() { auto result = this->flac_decoder_->read_header(this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available()); - if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) { - return FileDecoderState::POTENTIALLY_FAILED; - } - - if (result != esp_audio_libs::flac::FLAC_DECODER_SUCCESS) { - // Couldn't read FLAC header + if (result > esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) { + // Serrious error reading FLAC header, there is no recovery return FileDecoderState::FAILED; } size_t bytes_consumed = this->flac_decoder_->get_bytes_index(); this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed); + if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) { + return FileDecoderState::MORE_TO_PROCESS; + } + // Reallocate the output transfer buffer to the smallest necessary size this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes(); if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) { @@ -256,9 +256,9 @@ FileDecoderState AudioDecoder::decode_flac_() { } uint32_t output_samples = 0; - auto result = this->flac_decoder_->decode_frame( - this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available(), - reinterpret_cast(this->output_transfer_buffer_->get_buffer_end()), &output_samples); + auto result = this->flac_decoder_->decode_frame(this->input_transfer_buffer_->get_buffer_start(), + this->input_transfer_buffer_->available(), + this->output_transfer_buffer_->get_buffer_end(), &output_samples); if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) { // Not an issue, just needs more data that we'll get next time. diff --git a/platformio.ini b/platformio.ini index 70b562adff..44b466a2b3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -147,7 +147,7 @@ lib_deps = makuna/NeoPixelBus@2.8.0 ; neopixelbus esphome/ESP32-audioI2S@2.3.0 ; i2s_audio droscy/esp_wireguard@0.4.2 ; wireguard - esphome/esp-audio-libs@1.1.4 ; audio + esphome/esp-audio-libs@2.0.1 ; audio build_flags = ${common:arduino.build_flags} @@ -170,7 +170,7 @@ lib_deps = ${common:idf.lib_deps} droscy/esp_wireguard@0.4.2 ; wireguard kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word - esphome/esp-audio-libs@1.1.4 ; audio + esphome/esp-audio-libs@2.0.1 ; audio build_flags = ${common:idf.build_flags} -Wno-nonnull-compare From a99176877228b8b4752d20159752cad93dddfe30 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Oct 2025 14:02:39 -1000 Subject: [PATCH 5/7] missed python --- esphome/components/mdns/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index ce0241677d..3fa4d2ebef 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -61,7 +61,7 @@ CONFIG_SCHEMA = cv.All( def mdns_txt_record(key: str, value: str): return cg.StructInitializer( MDNSTXTRecord, - ("key", key), + ("key", cg.RawExpression(f"MDNS_STR({cg.safe_exp(key)})")), ("value", value), ) @@ -71,8 +71,8 @@ def mdns_service( ): return cg.StructInitializer( MDNSService, - ("service_type", service), - ("proto", proto), + ("service_type", cg.RawExpression(f"MDNS_STR({cg.safe_exp(service)})")), + ("proto", cg.RawExpression(f"MDNS_STR({cg.safe_exp(proto)})")), ("port", port), ("txt_records", txt_records), ) @@ -114,7 +114,7 @@ async def to_code(config): txt = [ cg.StructInitializer( MDNSTXTRecord, - ("key", txt_key), + ("key", cg.RawExpression(f"MDNS_STR({cg.safe_exp(txt_key)})")), ("value", await cg.templatable(txt_value, [], cg.std_string)), ) for txt_key, txt_value in service[CONF_TXT].items() From e3fadb1858b0b1d66898caed7d5a62479730021e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Oct 2025 14:05:22 -1000 Subject: [PATCH 6/7] missed python --- esphome/components/mdns/mdns_component.cpp | 2 -- esphome/components/mdns/mdns_component.h | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 5dfaa7fd4b..8945053b7d 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -9,7 +9,6 @@ #include // Macro to define strings in PROGMEM on ESP8266, regular memory on other platforms #define MDNS_STATIC_CONST_CHAR(name, value) static const char name[] PROGMEM = value -#define MDNS_STR(name) (reinterpret_cast(name)) // Helper to convert PROGMEM string to std::string for TemplatableValue // Only define this function if we have services that will use it #if defined(USE_API) || defined(USE_PROMETHEUS) || defined(USE_WEBSERVER) || defined(USE_MDNS_EXTRA_SERVICES) @@ -24,7 +23,6 @@ static std::string mdns_str_value(PGM_P str) { #else // On non-ESP8266 platforms, use regular const char* #define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char name[] = value -#define MDNS_STR(name) (reinterpret_cast(name)) #define MDNS_STR_VALUE(name) std::string(name) #endif diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index ef366cd31a..09f6d36a80 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -12,6 +12,9 @@ namespace mdns { // Helper struct that identifies strings that may be stored in flash storage (similar to LogString) struct MDNSString; +// Macro to cast string literals to MDNSString* (works on all platforms) +#define MDNS_STR(name) (reinterpret_cast(name)) + #ifdef USE_ESP8266 #include #define MDNS_STR_ARG(s) ((PGM_P) (s)) From 43c7ebcab4b79e895b497e4fd56a52d1abaf00ac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Oct 2025 14:06:28 -1000 Subject: [PATCH 7/7] missed python --- esphome/components/mdns/mdns_component.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 09f6d36a80..b1f73fbb32 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -13,7 +13,7 @@ namespace mdns { struct MDNSString; // Macro to cast string literals to MDNSString* (works on all platforms) -#define MDNS_STR(name) (reinterpret_cast(name)) +#define MDNS_STR(name) (reinterpret_cast(name)) #ifdef USE_ESP8266 #include