From 7ab701e16232abe1c272a4b86192cda6e8933922 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Apr 2026 04:13:30 -0500 Subject: [PATCH] [api] Gate 48-bit MAC range check behind ESPHOME_DEBUG_API Copilot flagged that encode_varint_raw_48bit/calc_uint64_48bit_force would silently truncate for uint64 values >= 2^48. In practice the (mac_address) option is only applied to fields populated by the BLE stack, which always fits in 48 bits -- so the runtime upper-bound check added in ba362a7c95 regressed CodSpeed by up to 8.7pp on CalculateSize_BLERawAdvs12 for a scenario that can't happen. Move the value-fits-in-48-bits check to a debug assert guarded by ESPHOME_DEBUG_API, and express 48 via MAC_ADDRESS_SIZE * 8 so the threshold tracks the existing MAC size constant. Release builds are back to the original fast path; debug builds catch misuse. --- esphome/components/api/proto.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 30efdfbb619..3ff65029e1b 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -344,13 +344,17 @@ class ProtoEncode { } /// Encode a 48-bit MAC address (stored in a uint64) as varint. /// Real MAC addresses occupy the full 48 bits (OUI in upper 24), so the - /// fast path -- any non-zero bit in bits [42..47] with value fitting in - /// 48 bits -- emits exactly 7 bytes with no per-byte branch. Values outside - /// [1<<42, 1<<48) fall back to the general encoder to preserve correctness - /// if this helper is ever misapplied to a non-MAC uint64. + /// fast path -- any non-zero bit in the top 6 of 48 -- emits exactly 7 bytes + /// with no per-byte branch. Falls back to the general loop otherwise. + /// Caller must guarantee value fits in 48 bits (checked in debug builds). static inline void ESPHOME_ALWAYS_INLINE encode_varint_raw_48bit(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value) { - if (value >= (1ULL << 42) && value < (1ULL << 48)) [[likely]] { +#ifdef ESPHOME_DEBUG_API + assert(value < (1ULL << (MAC_ADDRESS_SIZE * 8)) && "encode_varint_raw_48bit: value exceeds 48 bits"); +#endif + // 7-byte varint holds 49 bits (7 * 7), so a 48-bit value needs all 7 bytes + // whenever bit 42 or higher is set (i.e. value >= 1 << (48 - 6)). + if (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6))) [[likely]] { PROTO_ENCODE_CHECK_BOUNDS(pos, 7); pos[0] = static_cast(value | 0x80); pos[1] = static_cast((value >> 7) | 0x80); @@ -840,12 +844,12 @@ class ProtoSize { return field_id_size + varint(value); } /// 48-bit MAC address variant: matches encode_varint_raw_48bit's fast path. - /// When value is in [1<<42, 1<<48) the encoded varint is 7 bytes; values - /// outside that range fall back to the general size calculation so the - /// result stays correct if this helper is ever misapplied. + /// When any of the top 6 of 48 bits is set the encoded varint is 7 bytes; + /// otherwise fall back to the general size calculation. + /// Caller must guarantee value fits in 48 bits (encoder asserts in debug). static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_48bit_force(uint32_t field_id_size, uint64_t value) { - return field_id_size + ((value >= (1ULL << 42) && value < (1ULL << 48)) ? 7 : varint(value)); + return field_id_size + (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6)) ? 7 : varint(value)); } static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len) { return len ? field_id_size + varint(static_cast(len)) + static_cast(len) : 0;