[api] Drop connection instead of crashing when overflow buffer allocation fails (#18802)

This commit is contained in:
J. Nick Koston
2026-08-26 19:46:13 -05:00
committed by GitHub
parent 5df1c7f1d3
commit 3361d031de
3 changed files with 16 additions and 4 deletions
+1 -1
View File
@@ -172,7 +172,7 @@ APIError APIFrameHelper::write_raw_iov_(const struct iovec *iov, int iovcnt, uin
// Queue unsent data into overflow buffer
if (!this->overflow_buf_.enqueue_iov(iov, iovcnt, total_write_len, static_cast<uint16_t>(sent))) {
HELPER_LOG("Overflow buffer full, dropping connection");
HELPER_LOG("Overflow buffer full or out of memory, dropping connection");
this->state_ = State::FAILED;
return APIError::SOCKET_WRITE_FAILED;
}
+14 -2
View File
@@ -1,6 +1,7 @@
#include "api_overflow_buffer.h"
#ifdef USE_API
#include <cstring>
#include <new>
namespace esphome::api {
@@ -61,9 +62,18 @@ bool APIOverflowBuffer::enqueue_iov(const struct iovec *iov, int iovcnt, uint16_
return false;
uint16_t buffer_size = total_len - skip;
// nothrow: a failed allocation returns nullptr so the connection is dropped
// cleanly instead of plain new's crash or abort on OOM
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
auto *entry = new Entry{new uint8_t[buffer_size], buffer_size, 0};
this->queue_[this->tail_] = entry;
auto *data = new (std::nothrow) uint8_t[buffer_size];
if (data == nullptr)
return false;
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
auto *entry = new (std::nothrow) Entry{data, buffer_size, 0};
if (entry == nullptr) {
delete[] data;
return false;
}
uint16_t to_skip = skip;
uint16_t write_pos = 0;
@@ -80,6 +90,8 @@ bool APIOverflowBuffer::enqueue_iov(const struct iovec *iov, int iovcnt, uint16_
}
}
// Publish only after the copy completes so a half-built entry is never reachable
this->queue_[this->tail_] = entry;
this->tail_ = (this->tail_ + 1) % API_MAX_SEND_QUEUE;
this->count_++;
return true;
+1 -1
View File
@@ -61,7 +61,7 @@ class APIOverflowBuffer {
/// Enqueue unsent IOV data into the backlog.
/// Copies iov data starting at byte offset `skip` into a new entry.
/// Returns false if the queue is full (caller should fail the connection).
/// Returns false if the queue is full or allocation fails (caller should fail the connection).
bool enqueue_iov(const struct iovec *iov, int iovcnt, uint16_t total_len, uint16_t skip);
protected: