From abd6a4f6a90ff1b8f6517ac1a0b11d7f12f93ef6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Mar 2026 18:01:48 -1000 Subject: [PATCH] [core] Fix Callback::create memcpy from function reference When Callback::create receives a function reference (e.g. void(&)(int)), &callable gives the address of the function's machine code, not a pointer variable. The memcpy then reads bytes from executable code memory instead of copying a function pointer value. Fix by decaying the callable into a local variable before memcpy, which converts function references to function pointers stored on the stack. No current callers trigger this bug (all pass lambdas), but this prevents incorrect behavior if bare function names are ever passed. --- esphome/core/helpers.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index a703b5a5f37..47cc35cb957 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1757,7 +1757,10 @@ template struct Callback { // Safe under C++20 (P0593R6): byte copy into aligned storage implicitly // creates objects of implicit-lifetime types (trivially copyable qualifies). Callback cb; // fn and ctx are zero-initialized by default - __builtin_memcpy(&cb.ctx_, &callable, sizeof(DecayF)); + // Decay callable to a local variable first. When F is a function reference + // (e.g. void(&)(int)), &callable would point at machine code, not a pointer variable. + DecayF decayed = std::forward(callable); + __builtin_memcpy(&cb.ctx_, &decayed, sizeof(DecayF)); cb.fn_ = [](void *c, Ts... args) { alignas(DecayF) char buf[sizeof(DecayF)]; __builtin_memcpy(buf, &c, sizeof(DecayF));