mirror of
https://github.com/esphome/esphome.git
synced 2025-08-09 11:57:46 +00:00
safety
This commit is contained in:
parent
04b54353f1
commit
956959fc32
@ -60,6 +60,10 @@ void Component::set_interval(const std::string &name, uint32_t interval, std::fu
|
||||
App.scheduler.set_interval(this, name, interval, std::move(f));
|
||||
}
|
||||
|
||||
void Component::set_interval(const char *name, uint32_t interval, std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_interval(this, name, interval, std::move(f));
|
||||
}
|
||||
|
||||
bool Component::cancel_interval(const std::string &name) { // NOLINT
|
||||
return App.scheduler.cancel_interval(this, name);
|
||||
}
|
||||
@ -77,6 +81,10 @@ void Component::set_timeout(const std::string &name, uint32_t timeout, std::func
|
||||
App.scheduler.set_timeout(this, name, timeout, std::move(f));
|
||||
}
|
||||
|
||||
void Component::set_timeout(const char *name, uint32_t timeout, std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_timeout(this, name, timeout, std::move(f));
|
||||
}
|
||||
|
||||
bool Component::cancel_timeout(const std::string &name) { // NOLINT
|
||||
return App.scheduler.cancel_timeout(this, name);
|
||||
}
|
||||
|
@ -260,6 +260,22 @@ class Component {
|
||||
*/
|
||||
void set_interval(const std::string &name, uint32_t interval, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Set an interval function with a const char* name.
|
||||
*
|
||||
* IMPORTANT: The provided name pointer must remain valid for the lifetime of the scheduler item.
|
||||
* This means the name should be:
|
||||
* - A string literal (e.g., "update")
|
||||
* - A static const char* variable
|
||||
* - A pointer with lifetime >= the scheduled task
|
||||
*
|
||||
* For dynamic strings, use the std::string overload instead.
|
||||
*
|
||||
* @param name The identifier for this interval function (must have static lifetime)
|
||||
* @param interval The interval in ms
|
||||
* @param f The function to call
|
||||
*/
|
||||
void set_interval(const char *name, uint32_t interval, std::function<void()> &&f); // NOLINT
|
||||
|
||||
void set_interval(uint32_t interval, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Cancel an interval function.
|
||||
@ -328,6 +344,22 @@ class Component {
|
||||
*/
|
||||
void set_timeout(const std::string &name, uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Set a timeout function with a const char* name.
|
||||
*
|
||||
* IMPORTANT: The provided name pointer must remain valid for the lifetime of the scheduler item.
|
||||
* This means the name should be:
|
||||
* - A string literal (e.g., "init")
|
||||
* - A static const char* variable
|
||||
* - A pointer with lifetime >= the timeout duration
|
||||
*
|
||||
* For dynamic strings, use the std::string overload instead.
|
||||
*
|
||||
* @param name The identifier for this timeout function (must have static lifetime)
|
||||
* @param timeout The timeout in ms
|
||||
* @param f The function to call
|
||||
*/
|
||||
void set_timeout(const char *name, uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
void set_timeout(uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Cancel a timeout function.
|
||||
|
@ -17,6 +17,41 @@ static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10;
|
||||
// Uncomment to debug scheduler
|
||||
// #define ESPHOME_DEBUG_SCHEDULER
|
||||
|
||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||
// Helper to validate that a pointer looks like it's in static memory
|
||||
static void validate_static_string(const char *name) {
|
||||
if (name == nullptr)
|
||||
return;
|
||||
|
||||
// This is a heuristic check - stack and heap pointers are typically
|
||||
// much higher in memory than static data
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(name);
|
||||
|
||||
// Create a stack variable to compare against
|
||||
int stack_var;
|
||||
uintptr_t stack_addr = reinterpret_cast<uintptr_t>(&stack_var);
|
||||
|
||||
// If the string pointer is near our stack variable, it's likely on the stack
|
||||
// Using 8KB range as ESP32 main task stack is typically 8192 bytes
|
||||
if (addr > (stack_addr - 0x2000) && addr < (stack_addr + 0x2000)) {
|
||||
ESP_LOGW(TAG,
|
||||
"WARNING: Scheduler name '%s' at %p appears to be on the stack - this is unsafe!\n"
|
||||
" Stack reference at %p",
|
||||
name, name, &stack_var);
|
||||
}
|
||||
|
||||
// Also check if it might be on the heap by seeing if it's in a very different range
|
||||
// This is platform-specific but generally heap is allocated far from static memory
|
||||
static const char *static_str = "test";
|
||||
uintptr_t static_addr = reinterpret_cast<uintptr_t>(static_str);
|
||||
|
||||
// If the address is very far from known static memory, it might be heap
|
||||
if (addr > static_addr + 0x100000 || (static_addr > 0x100000 && addr < static_addr - 0x100000)) {
|
||||
ESP_LOGW(TAG, "WARNING: Scheduler name '%s' at %p might be on heap (static ref at %p)", name, name, static_str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// A note on locking: the `lock_` lock protects the `items_` and `to_add_` containers. It must be taken when writing to
|
||||
// them (i.e. when adding/removing items, but not when changing items). As items are only deleted from the loop task,
|
||||
// iterating over them from the loop task is fine; but iterating from any other context requires the lock to be held to
|
||||
@ -50,6 +85,12 @@ void HOT Scheduler::set_timeout_impl_(Component *component, const NameType &name
|
||||
item->set_name(name.c_str(), make_copy);
|
||||
} else {
|
||||
item->set_name(name, make_copy);
|
||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||
// Validate static strings in debug mode
|
||||
if (!make_copy && name != nullptr) {
|
||||
validate_static_string(name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
item->type = SchedulerItem::TIMEOUT;
|
||||
@ -118,6 +159,12 @@ void HOT Scheduler::set_interval_impl_(Component *component, const NameType &nam
|
||||
item->set_name(name.c_str(), make_copy);
|
||||
} else {
|
||||
item->set_name(name, make_copy);
|
||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||
// Validate static strings in debug mode
|
||||
if (!make_copy && name != nullptr) {
|
||||
validate_static_string(name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
item->type = SchedulerItem::INTERVAL;
|
||||
@ -238,9 +285,10 @@ void HOT Scheduler::call() {
|
||||
this->pop_raw_();
|
||||
this->lock_.unlock();
|
||||
|
||||
const char *name = item->get_name();
|
||||
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64,
|
||||
item->get_type_str(), item->get_source(), item->get_name(), item->interval, item->next_execution_ - now,
|
||||
item->next_execution_);
|
||||
item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
|
||||
item->next_execution_ - now, item->next_execution_);
|
||||
|
||||
old_items.push_back(std::move(item));
|
||||
}
|
||||
|
@ -14,10 +14,33 @@ class Scheduler {
|
||||
public:
|
||||
// Public API - accepts std::string for backward compatibility
|
||||
void set_timeout(Component *component, const std::string &name, uint32_t timeout, std::function<void()> func);
|
||||
|
||||
/** Set a timeout with a const char* name.
|
||||
*
|
||||
* IMPORTANT: The provided name pointer must remain valid for the lifetime of the scheduler item.
|
||||
* This means the name should be:
|
||||
* - A string literal (e.g., "update")
|
||||
* - A static const char* variable
|
||||
* - A pointer with lifetime >= the scheduled task
|
||||
*
|
||||
* For dynamic strings, use the std::string overload instead.
|
||||
*/
|
||||
void set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func);
|
||||
|
||||
bool cancel_timeout(Component *component, const std::string &name);
|
||||
|
||||
void set_interval(Component *component, const std::string &name, uint32_t interval, std::function<void()> func);
|
||||
|
||||
/** Set an interval with a const char* name.
|
||||
*
|
||||
* IMPORTANT: The provided name pointer must remain valid for the lifetime of the scheduler item.
|
||||
* This means the name should be:
|
||||
* - A string literal (e.g., "update")
|
||||
* - A static const char* variable
|
||||
* - A pointer with lifetime >= the scheduled task
|
||||
*
|
||||
* For dynamic strings, use the std::string overload instead.
|
||||
*/
|
||||
void set_interval(Component *component, const char *name, uint32_t interval, std::function<void()> func);
|
||||
|
||||
bool cancel_interval(Component *component, const std::string &name);
|
||||
|
Loading…
x
Reference in New Issue
Block a user