From 21c4f307de66a80d96bfce29a60bcf4b1c863de3 Mon Sep 17 00:00:00 2001 From: Renz Bagaporo Date: Mon, 13 Jul 2026 15:45:00 +0900 Subject: [PATCH] fix(esp_timer): avoid dump buffer overflow print_timer_info advanced the dump cursor by snprintf's return value. When a timer line was truncated, snprintf returned the full would-be length, which could move the cursor past the heap buffer and wrap the remaining size before the next write. Add a bounded append helper that clamps truncation to the end of the buffer while preserving the NUL terminator. --- components/esp_timer/src/esp_timer.c | 46 ++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/components/esp_timer/src/esp_timer.c b/components/esp_timer/src/esp_timer.c index cf8d8abd83c..9d0c2db5f2c 100644 --- a/components/esp_timer/src/esp_timer.c +++ b/components/esp_timer/src/esp_timer.c @@ -5,6 +5,7 @@ */ #include +#include #include #include "soc/soc.h" #include "esp_types.h" @@ -695,28 +696,54 @@ esp_err_t esp_timer_deinit(void) return ESP_OK; } +static void append_to_buffer(char** dst, size_t* dst_size, const char* format, ...) +{ + /* Defensive: avoid underflow in the truncation path below if no space remains. */ + if (*dst_size == 0) { + return; + } + + va_list args; + va_start(args, format); + int cb = vsnprintf(*dst, *dst_size, format, args); + va_end(args); + + if (cb < 0) { + return; + } + + /* On truncation, snprintf returns the full would-be length. Keep the + * cursor inside the buffer and preserve the terminating NUL byte. + */ + if ((size_t) cb >= *dst_size) { + *dst += *dst_size - 1; + *dst_size = 1; + return; + } + + *dst += cb; + *dst_size -= cb; +} + static void print_timer_info(esp_timer_handle_t t, char** dst, size_t* dst_size) { #if WITH_PROFILING - size_t cb; // name is optional, might be missed. if (t->name) { - cb = snprintf(*dst, *dst_size, "%-20.20s ", t->name); + append_to_buffer(dst, dst_size, "%-20.20s ", t->name); } else { - cb = snprintf(*dst, *dst_size, "timer@%-10p ", t); + append_to_buffer(dst, dst_size, "timer@%-10p ", t); } - cb += snprintf(*dst + cb, *dst_size - cb, "%-10lld %-12lld %-12d %-12d %-12d %-12lld\n", - (uint64_t)t->period, t->alarm, t->times_armed, - t->times_triggered, t->times_skipped, t->total_callback_run_time); + append_to_buffer(dst, dst_size, "%-10lld %-12lld %-12d %-12d %-12d %-12lld\n", + (uint64_t)t->period, t->alarm, t->times_armed, + t->times_triggered, t->times_skipped, t->total_callback_run_time); /* keep this in sync with the format string, used in esp_timer_dump */ #define TIMER_INFO_LINE_LEN 103 #else - size_t cb = snprintf(*dst, *dst_size, "timer@%-14p %-10lld %-12lld\n", t, (uint64_t)t->period, t->alarm); + append_to_buffer(dst, dst_size, "timer@%-14p %-10lld %-12lld\n", t, (uint64_t)t->period, t->alarm); #define TIMER_INFO_LINE_LEN 47 #endif - *dst += cb; - *dst_size -= cb; } esp_err_t esp_timer_dump(FILE* stream) @@ -750,6 +777,7 @@ esp_err_t esp_timer_dump(FILE* stream) * slightly more and the output will be truncated if that is not enough. */ size_t buf_size = TIMER_INFO_LINE_LEN * (timer_count + 3); + /* buf_size is the snprintf size, including NUL; keep one extra byte as slack. */ char* print_buf = calloc(1, buf_size + 1); if (print_buf == NULL) { return ESP_ERR_NO_MEM;