From 7a5716cfe616481e2502768bf65cc162826a990a Mon Sep 17 00:00:00 2001 From: yi chen <94xhn1@gmail.com> Date: Fri, 10 Jul 2026 14:57:25 +0800 Subject: [PATCH] Fix UTIL_SEQ_CONF_TASK_NBR overflow guard silently passing (#52) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UTIL_SEQ_CONF_TASK_NBR is frequently redefined in an application's utilities_conf.h to an enum sentinel value, e.g.: #define UTIL_SEQ_CONF_TASK_NBR CFG_SEQ_Task_NBR where CFG_SEQ_Task_Id_t is an enum whose last member (CFG_SEQ_Task_NBR) represents the task count. Per C99 6.10.1p4, an identifier that is not a macro is replaced by 0 when it appears in a preprocessor #if expression, since the preprocessor has no visibility into enum values. The existing guard: #if UTIL_SEQ_CONF_TASK_NBR > 32 #error "UTIL_SEQ_CONF_TASK_NBR must be less than or equal to 32" #endif therefore evaluates as "0 > 32", which is always false, so the #error never fires even when an application defines more than 32 tasks. Since several call sites in this file compute a task's bit position via "1U << CurrentTaskIdx" (e.g. lines building UTIL_SEQ_SetTask_Mask and similar masks), a task index >= 32 causes undefined behavior (shift amount >= width of the shifted type) that this guard was meant to prevent. Replace the preprocessor guard with a compile-time array-size assertion, which is evaluated by the compiler proper during semantic analysis and does see the real (expanded) enum value: typedef char UTIL_SEQ_CONF_TASK_NBR_Assert[(UTIL_SEQ_CONF_TASK_NBR <= 32) ? 1 : -1]; A classic negative-array-size assertion was used instead of _Static_assert/static_assert to avoid depending on C11 support across all toolchains this project ships with (MDK-ARM/EWARM project files still allow selecting pre-C11 dialects). Verified with a standalone host-side reproduction (not on hardware, no board/toolchain access in this environment): a minimal harness mirroring the exact macro-to-enum indirection pattern, compiled with gcc -std=c89. Before the fix, with a 33-member task enum (one over the limit), the file compiles cleanly (exit 0) — confirming the original guard is silently bypassed. After the fix, the same 33-task case fails to compile with "size of array ... is negative" (exit 1), while a normal case (4 tasks, well under the limit) still compiles cleanly (exit 0), confirming no regression for valid configurations. Fixes #52 Generative AI (Claude) was used to help investigate this issue, implement, and verify this fix. All changes were reviewed by me before submission. Signed-off-by: yi chen <94xhn1@gmail.com> --- Utilities/sequencer/stm32_seq.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Utilities/sequencer/stm32_seq.c b/Utilities/sequencer/stm32_seq.c index 56fb2c26..d9876fb9 100644 --- a/Utilities/sequencer/stm32_seq.c +++ b/Utilities/sequencer/stm32_seq.c @@ -90,9 +90,17 @@ typedef struct #define UTIL_SEQ_CONF_TASK_NBR (32) #endif /* UTIL_SEQ_CONF_TASK_NBR */ -#if UTIL_SEQ_CONF_TASK_NBR > 32 -#error "UTIL_SEQ_CONF_TASK_NBR must be less than or equal to 32" -#endif /* UTIL_SEQ_CONF_TASK_NBR */ +/* NOTE: applications commonly set UTIL_SEQ_CONF_TASK_NBR to an enum + * constant (e.g. #define UTIL_SEQ_CONF_TASK_NBR CFG_SEQ_Task_NBR). + * An enum constant is not visible to the preprocessor, so a + * preprocessor "#if UTIL_SEQ_CONF_TASK_NBR > 32" would silently + * evaluate the unresolved identifier as 0 and never trigger, per the + * C standard's handling of #if. Use a compile-time assertion evaluated + * by the compiler proper (which does see enum values) instead. A + * negative-size array is used rather than _Static_assert/static_assert + * to stay compatible with the C89/C99 toolchains still selectable in + * this project's MDK-ARM/EWARM configurations. */ +typedef char UTIL_SEQ_CONF_TASK_NBR_Assert[(UTIL_SEQ_CONF_TASK_NBR <= 32) ? 1 : -1]; /** * @brief default value of priority number.