[uart] Limit a received packet to the receive buffer

The receive buffer is sized by code generation to max_packet_size, but
set_max_packet_size() can raise the limit afterwards, and the received
bytes would then overflow the buffer. The link now takes the buffer as a
span and stops at its size too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
clydebarrow
2026-10-19 14:40:08 +11:00
co-authored by Claude Opus 5.5
parent 333090f803
commit ed6c54efcb
3 changed files with 21 additions and 5 deletions
@@ -24,7 +24,7 @@ void UartPacketInterface::loop() {
if (byte == FLAG_BYTE) {
// A flag that ends a frame also starts the next, so an unfinished frame cannot hide the one after it
if (this->rx_started_ && this->rx_length_ != 0)
this->on_receive_from_interface_(PacketBuffer(this->rx_buffer_, this->rx_length_), {});
this->on_receive_from_interface_(PacketBuffer(this->rx_buffer_.data(), this->rx_length_), {});
this->rx_started_ = true;
this->rx_length_ = 0;
this->rx_control_ = false;
@@ -40,7 +40,7 @@ void UartPacketInterface::loop() {
byte ^= ESCAPE_XOR;
this->rx_control_ = false;
}
if (this->rx_length_ >= this->max_packet_size_) {
if (this->rx_length_ >= this->max_packet_size_ || this->rx_length_ >= this->rx_buffer_.size()) {
ESP_LOGD(TAG, "Packet too large, discarding");
this->rx_started_ = false;
this->rx_length_ = 0;
@@ -1,5 +1,7 @@
#pragma once
#include <span>
#include "esphome/components/packet_interface/packet_interface.h"
#include "esphome/components/uart/uart.h"
@@ -14,15 +16,16 @@ namespace esphome::uart {
*/
class UartPacketInterface : public packet_interface::PacketInterface, public UARTDevice {
public:
/// @param rx_buffer Holds max_packet_size bytes of unescaped received data; must outlive the link
explicit UartPacketInterface(uint8_t *rx_buffer) : rx_buffer_(rx_buffer) {}
/// @param rx_buffer Holds the unescaped data being received, sized by code generation to max_packet_size; must
/// outlive the link. A larger max_packet_size set later is limited to it.
explicit UartPacketInterface(std::span<uint8_t> rx_buffer) : rx_buffer_(rx_buffer) {}
void loop() override;
bool send_to_interface(const packet_interface::PacketBuffer &data,
const packet_interface::PacketMetaData &meta_data) override;
protected:
uint8_t *const rx_buffer_;
const std::span<uint8_t> rx_buffer_;
size_t rx_length_{0};
bool rx_started_{};
bool rx_control_{};
@@ -338,6 +338,19 @@ TEST_F(UartPacketInterfaceTest, AllByteValues) {
}
// Test a packet larger than max_packet_size is not sent
TEST_F(UartPacketInterfaceTest, ReceiveLimitedToBuffer) {
uint8_t small_buffer[4]{};
UartPacketInterface interface(small_buffer);
interface.set_uart_parent(&this->uart_);
// Raised above the buffer after code generation sized it
interface.set_max_packet_size(100);
std::vector<std::vector<uint8_t>> received;
interface.add_packet_interface_listener(
[&received](const PacketBuffer &data, const PacketMetaData &) { received.push_back(to_vector(data)); });
feed(this->uart_, interface, {0x7E, 1, 2, 3, 4, 5, 6, 0x7E, 7, 8, 0x7E});
EXPECT_EQ(received, (std::vector<std::vector<uint8_t>>{{7, 8}}));
}
TEST_F(UartPacketInterfaceTest, OversizedPacketNotSent) {
interface_.set_max_packet_size(4);
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0x04, 0x05};