Compare commits

...
Author SHA1 Message Date
J. Nick Koston e65276bf3d Merge remote-tracking branch 'origin/dev' into ezo-reply-overread
# Conflicts:
#	esphome/components/ezo/ezo.cpp
#	tests/components/ezo/test_ezo_reply.cpp
2026-11-04 21:30:21 -06:00
J. Nick Koston 66b77d0abd Merge remote-tracking branch 'origin/dev' into HEAD 2026-10-31 13:12:14 -10:00
J. Nick Koston d70d1c20df [ezo] Check the reply read size and silence the callback trampoline copy warning
Ts... in the callback trampolines must match fn_, so a by-value std::string argument
cannot become a const reference; clang-tidy first sees this through the reply test.
2026-10-31 10:33:07 -10:00
J. Nick Koston 5ee4c9fb2b [ezo] Take the reply by reference in the test callback 2026-10-30 15:19:54 -10:00
J. Nick Koston 269d0dbfe2 [ezo] Read replies up to 52 characters and always terminate them
The 32-byte read cut off longer replies (EZO-RGB sends up to 52
characters) and left no terminator when the reply filled the read, so the
string ran past the end of the stack buffer.
2026-10-28 23:25:05 -10:00
2 changed files with 8 additions and 2 deletions

No files matched your search

+6 -2
View File
@@ -1977,7 +1977,8 @@ template<typename... Ts> struct Callback<void(Ts...)> {
// (e.g. void(&)(int)), &callable would point at machine code, not a pointer variable.
DecayF decayed = std::forward<F>(callable);
__builtin_memcpy(&cb.ctx_, &decayed, sizeof(DecayF));
cb.fn_ = [](void *c, Ts... args) {
// Ts... must match fn_ exactly, so a by-value argument cannot become a const reference here
cb.fn_ = [](void *c, Ts... args) { // NOLINT(performance-unnecessary-value-param)
alignas(DecayF) char buf[sizeof(DecayF)];
__builtin_memcpy(buf, &c, sizeof(DecayF));
(*std::launder(reinterpret_cast<DecayF *>(buf)))(args...);
@@ -1988,7 +1989,10 @@ template<typename... Ts> struct Callback<void(Ts...)> {
// Intentionally never freed: callbacks in ESPHome are registered during setup()
// and live for device lifetime. Same lifetime as the previous std::function approach.
auto *stored = new DecayF(std::forward<F>(callable));
return {[](void *c, Ts... args) { (*static_cast<DecayF *>(c))(args...); }, static_cast<void *>(stored)};
return {[](void *c, Ts... args) { // NOLINT(performance-unnecessary-value-param)
(*static_cast<DecayF *>(c))(args...);
},
static_cast<void *>(stored)};
}
}
+2
View File
@@ -45,6 +45,8 @@ static std::vector<std::string> run_custom(const std::string &reply) {
ezo.loop();
delay(10);
}
// One byte short of the 54-byte buffer, so its last byte stays the terminator
EXPECT_EQ(bus.read_sizes, std::vector<size_t>{53});
return replies;
}