This commit is contained in:
J. Nick Koston
2026-02-21 21:38:03 -06:00
parent b6e8e92416
commit 1183aae319
3 changed files with 8 additions and 12 deletions

View File

@@ -1705,10 +1705,10 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
// Null-terminate state in-place for safe c_str() usage (e.g., parse_number in callbacks).
// Safe: decode is complete, byte after string data was already consumed during parse,
// and frame helpers reserve RX_BUF_NULL_TERMINATOR extra byte in rx_buf_.
// const_cast is safe: msg references rx_buf_ data which is mutable; the const& handler
// const_cast is safe: msg references mutable rx_buf_ data; the const& handler
// signature is a generated protobuf pattern, not a true immutability contract.
if (!msg.state.empty()) {
const_cast<StringRef &>(msg.state).null_terminate_in_place();
const_cast<char *>(msg.state.c_str())[msg.state.size()] = '\0';
}
for (auto &it : this->parent_->get_state_subs()) {
@@ -1731,11 +1731,11 @@ void APIConnection::on_execute_service_request(const ExecuteServiceRequest &msg)
// Null-terminate string args in-place for safe c_str() usage in YAML service triggers.
// Safe: full ExecuteServiceRequest decode is complete, all bytes in rx_buf_ consumed,
// and frame helpers reserve RX_BUF_NULL_TERMINATOR extra byte for the last field.
// const_cast is safe: msg references rx_buf_ data which is mutable; the const& handler
// const_cast is safe: msg references mutable rx_buf_ data; the const& handler
// signature is a generated protobuf pattern, not a true immutability contract.
for (auto &arg : const_cast<ExecuteServiceRequest &>(msg).args) {
if (!arg.string_.empty()) {
arg.string_.null_terminate_in_place();
const_cast<char *>(arg.string_.c_str())[arg.string_.size()] = '\0';
}
}
bool found = false;

View File

@@ -194,18 +194,19 @@ APIError APINoiseFrameHelper::try_read_frame_() {
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
// 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;
bool is_data = (state_ == State::DATA);
uint16_t limit = is_data ? MAX_MESSAGE_SIZE : MAX_HANDSHAKE_SIZE;
if (msg_size > limit) {
state_ = State::FAILED;
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;
return is_data ? APIError::BAD_DATA_PACKET : APIError::BAD_HANDSHAKE_PACKET_LEN;
}
// Reserve space for body (+ null terminator in DATA state so protobuf
// StringRef fields can be safely null-terminated in-place after decode.
// During handshake, rx_buf_.size() is used in prologue construction, so
// the buffer must be exactly msg_size to avoid prologue mismatch.)
uint16_t alloc_size = msg_size + (state_ == State::DATA ? RX_BUF_NULL_TERMINATOR : 0);
uint16_t alloc_size = msg_size + (is_data ? RX_BUF_NULL_TERMINATOR : 0);
if (this->rx_buf_.size() != alloc_size) {
this->rx_buf_.resize(alloc_size);
}

View File

@@ -81,11 +81,6 @@ class StringRef {
operator std::string() const { return str(); }
/// Write a null terminator at base_[len_] in-place.
/// Caller must guarantee that the byte at base_[len_] is writable memory
/// (e.g., the RX_BUF_NULL_TERMINATOR byte reserved by frame helpers after decode).
void null_terminate_in_place() { const_cast<char *>(base_)[len_] = '\0'; }
/// Find first occurrence of substring, returns std::string::npos if not found.
/// Note: Requires the underlying string to be null-terminated.
size_type find(const char *s, size_type pos = 0) const {