Skip to content

Commit dae8157

Browse files
committed
fix(IWatchdog): guard reload/window register computation against underflow
IWatchdogClass::set() computes the IWDG reload register with reload = (uint32_t)(t_sec / div) - 1; (and the equivalent for the optional 'window' register), with no check that (uint32_t)(t_sec / div) is non-zero before subtracting 1. IWDG_TIMEOUT_MIN, the documented and enforced lower bound for the 'timeout' argument, is defined as (4*1000000)/LSI_VALUE using integer division. On most series (LSI_VALUE 32000 or 40000) this divides evenly, so calling begin(IWDG_TIMEOUT_MIN) is always safe. On STM32L0xx/STM32L1xx (LSI_VALUE = 37000), 4000000/37000 is not an integer (108.108...), so the macro truncates down to 108 -- a value IS_IWDG_TIMEOUT() accepts, but that is below the true precision needed: t_sec works out to 3.996 there, so (uint32_t)(t_sec / 4) floors to 0, and "0 - 1" underflows reload to 0xFFFFFFFF. LL_IWDG_SetReloadCounter() then masks that into the 12-bit RLR register as 0x0FFF (4095), the maximum possible value, instead of 0. Net effect: on STM32L0/L1, IWatchdog.begin(IWDG_TIMEOUT_MIN) -- the shortest legal, documented watchdog timeout -- silently programs a timeout of about 443 ms instead of about 108 us (roughly 4100x longer), with no error indication. The same underflow shape is reachable through the optional 'window' parameter for the same reason. Guard both computations with an explicit "ticks > 0" check before subtracting, returning 0 (the safe minimum reload) instead of underflowing, without changing the public API or behavior on any series where the computation was already safe. Signed-off-by: 94xhn <87560781+94xhn@users.noreply.github.com>
1 parent 46d4cc5 commit dae8157

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

libraries/IWatchdog/src/IWatchdog.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,17 @@ void IWatchdogClass::set(uint32_t timeout, uint32_t window)
9494
if (--prescaler > LL_IWDG_PRESCALER_256) {
9595
return;
9696
}
97-
reload = (uint32_t)(t_sec / div) - 1;
97+
{
98+
uint32_t ticks = (uint32_t)(t_sec / div);
99+
// 'ticks' can floor to 0 right at the documented minimum timeout on
100+
// series whose LSI_VALUE does not divide IWDG_TIMEOUT_MIN evenly
101+
// (e.g. STM32L0xx/L1xx, LSI_VALUE=37000): guard against the unsigned
102+
// underflow that would otherwise wrap 'reload' to 0xFFFFFFFF and,
103+
// once masked into the 12-bit RLR register, silently program the
104+
// watchdog for close to its longest possible timeout instead of the
105+
// shortest one that was actually requested.
106+
reload = (ticks > 0) ? (ticks - 1) : 0;
107+
}
98108

99109
// Enable register access by writing 0x0000 5555 in the IWDG_KR register
100110
LL_IWDG_EnableWriteAccess(IWDG);
@@ -111,7 +121,9 @@ void IWatchdogClass::set(uint32_t timeout, uint32_t window)
111121
// Reset window value
112122
reload = IWDG_WINR_WIN;
113123
} else {
114-
reload = (uint32_t)(((float)window / 1000000 * LSI_VALUE) / div) - 1;
124+
// Same underflow risk as the 'timeout' -> reload conversion above.
125+
uint32_t window_ticks = (uint32_t)(((float)window / 1000000 * LSI_VALUE) / div);
126+
reload = (window_ticks > 0) ? (window_ticks - 1) : 0;
115127
}
116128
LL_IWDG_SetWindow(IWDG, reload);
117129
}

0 commit comments

Comments
 (0)