From b20d629e97d94c873967c6d752ba641b548230b5 Mon Sep 17 00:00:00 2001 From: clydebarrow <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 19 Oct 2026 09:32:57 +1100 Subject: [PATCH] [packet_interface] Fail the XXTEA layer when its buffers cannot be allocated Co-Authored-By: Claude Opus 5.5 --- .../packet_interface/xxtea_encryption.cpp | 19 ++++++++++++------- .../packet_interface/xxtea_encryption.h | 8 ++++---- .../packet_interface/xxtea_encryption.cpp | 8 ++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/esphome/components/packet_interface/xxtea_encryption.cpp b/esphome/components/packet_interface/xxtea_encryption.cpp index 0e2dee0efa8..90b284f9922 100644 --- a/esphome/components/packet_interface/xxtea_encryption.cpp +++ b/esphome/components/packet_interface/xxtea_encryption.cpp @@ -53,8 +53,13 @@ void XxteaEncryption::setup() { return; } size_t words = this->lower_->get_max_packet_size() / 4; - this->tx_buffer_ = std::make_unique(words); - this->rx_buffer_ = std::make_unique(words); + RAMAllocator allocator; + this->tx_buffer_ = allocator.allocate(words * 2); + if (this->tx_buffer_ == nullptr) { + this->mark_failed(LOG_STR("Not enough memory for the packet buffers")); + return; + } + this->rx_buffer_ = this->tx_buffer_ + words; if (this->rolling_code_) { this->pref_ = global_preferences->make_preference(fnv1a_hash_extend(PREF_HASH, this->name_), true); this->pref_.load(&this->boot_); @@ -85,7 +90,7 @@ void XxteaEncryption::next_boot_() { } bool XxteaEncryption::send_to_interface(const PacketBuffer &data, const PacketMetaData &meta_data) { - if (this->is_failed()) + if (this->tx_buffer_ == nullptr) return false; size_t size = data.size(); if (size > this->max_packet_size_) { @@ -96,7 +101,7 @@ bool XxteaEncryption::send_to_interface(const PacketBuffer &data, const PacketMe if (size + padding + this->trailer_size_ < MIN_LENGTH) padding += 4; size_t length = size + padding + this->trailer_size_; - auto *buf = reinterpret_cast(this->tx_buffer_.get()); + auto *buf = reinterpret_cast(this->tx_buffer_); data.copy_to(buf, size); memset(buf + size, 0, padding); uint8_t *check = buf + length - CHECK_SIZE; @@ -111,7 +116,7 @@ bool XxteaEncryption::send_to_interface(const PacketBuffer &data, const PacketMe memcpy(check, CHECK_BYTES, sizeof(CHECK_BYTES)); } check[CHECK_SIZE - 1] = padding; - xxtea::encrypt(this->tx_buffer_.get(), length / 4, this->key_.data()); + xxtea::encrypt(this->tx_buffer_, length / 4, this->key_.data()); return this->lower_->send_to_interface(PacketBuffer(buf, length), meta_data); } @@ -132,9 +137,9 @@ void XxteaEncryption::receive_(const PacketBuffer &data, const PacketMetaData &m this->note_received_(false); return; } - auto *buf = reinterpret_cast(this->rx_buffer_.get()); + auto *buf = reinterpret_cast(this->rx_buffer_); data.copy_to(buf, length); - xxtea::decrypt(this->rx_buffer_.get(), length / 4, this->key_.data()); + xxtea::decrypt(this->rx_buffer_, length / 4, this->key_.data()); const uint8_t *check = buf + length - CHECK_SIZE; const uint8_t *expected = this->rolling_code_ ? ROLLING_CHECK_BYTES : CHECK_BYTES; size_t padding = check[CHECK_SIZE - 1]; diff --git a/esphome/components/packet_interface/xxtea_encryption.h b/esphome/components/packet_interface/xxtea_encryption.h index 62be17d807c..82db1a83d90 100644 --- a/esphome/components/packet_interface/xxtea_encryption.h +++ b/esphome/components/packet_interface/xxtea_encryption.h @@ -4,7 +4,6 @@ #ifdef USE_PACKET_INTERFACE_XXTEA #include #include -#include #include "esphome/core/preferences.h" #include "packet_interface.h" @@ -43,9 +42,10 @@ class XxteaEncryption : public PacketInterface { PacketInterface *lower_; const size_t trailer_size_; std::array key_{}; - // Separate buffers, as a layer above may send a reply while a received packet is being passed up - std::unique_ptr tx_buffer_; - std::unique_ptr rx_buffer_; + // Separate buffers, as a layer above may send a reply while a received packet is being passed up; both are in one + // block allocated in setup(), and tx_buffer_ stays null if that fails + uint32_t *tx_buffer_{nullptr}; + uint32_t *rx_buffer_{nullptr}; ESPPreferenceObject pref_; uint32_t boot_{0}; uint32_t counter_{0}; diff --git a/tests/components/packet_interface/xxtea_encryption.cpp b/tests/components/packet_interface/xxtea_encryption.cpp index 960c8ab2931..09e206e64df 100644 --- a/tests/components/packet_interface/xxtea_encryption.cpp +++ b/tests/components/packet_interface/xxtea_encryption.cpp @@ -258,6 +258,14 @@ TEST_F(XxteaEncryptionTest, LinkTooSmallFailsSetup) { EXPECT_FALSE(encryption.send_to_interface(PacketBuffer(packet), {})); } +TEST_F(XxteaEncryptionTest, SendWithoutBuffersRefused) { + MockLink link(64); + TestEncryption encryption(&link, false); + Packet packet{1}; + EXPECT_FALSE(encryption.send_to_interface(PacketBuffer(packet), {})); + EXPECT_TRUE(link.sent.empty()); +} + TEST_F(XxteaEncryptionTest, CounterWrapStartsNewBoot) { Peer a, b(0x12345678); a.introduce(b);