stm32f7xx_ll_gpio.h – shift-count-overflow UB detected by GCC -fanalyzer in LL_GPIO_GetPinMode / LL_GPIO_SetPinMode and related functions
Hi,
**Environment**
- STM32F7xx LL Driver (STM32F7xx_HAL_Driver)
- GCC arm-none-eabi 15.2 (also reproducible with GCC ≥ 12)
- Compiler flag: `-fanalyzer`
- File: `STM32F7xx_HAL_Driver/Inc/stm32f7xx_ll_gpio.h`
---
**Problem**
When building with `-fanalyzer`, GCC reports the following warning for several inline functions in stm32f7xx_ll_gpio.h:
```
warning: shift by count ('64') >= precision of type ('32')
[-Wanalyzer-shift-count-overflow]
```
**Root cause**
`POSITION_VAL(VAL)` is defined as `__CLZ(__RBIT(VAL))`. When the analyzer considers `Pin = 0` as a possible runtime value (which it cannot rule out without additional constraints), then:
- `__RBIT(0)` → `0`
- `__CLZ(0)` → `32` (implementation-defined, GCC arm-none-eabi returns 32)
- `32 * 2U` → `64` → shift of a `uint32_t` by 64 = **undefined behavior** per C11 §6.5.7
The affected functions all follow the same pattern:
// e.g. LL_GPIO_GetPinMode (line 307–311)
return (uint32_t)(READ_BIT(GPIOx->MODER,
(GPIO_MODER_MODER0 << (POSITION_VAL(Pin) * 2U)))
>> (POSITION_VAL(Pin) * 2U));Additionally, `POSITION_VAL(Pin)` is evaluated **twice**, which is redundant.
**Affected functions** (non-exhaustive):
- `LL_GPIO_SetPinMode`
- `LL_GPIO_GetPinMode`
- `LL_GPIO_SetPinSpeed`
- `LL_GPIO_GetPinSpeed`
- `LL_GPIO_SetPinPull`
- `LL_GPIO_GetPinPull`
---
**Proposed fix**
Introduce a local `pos` variable and mask the shift count to a maximum of 30 (`Pin` 15 → bit 15 → `POSITION_VAL` = 15 → `15 * 2 = 30`). This eliminates the UB and the double evaluation:
// Before
__STATIC_INLINE uint32_t LL_GPIO_GetPinMode(GPIO_TypeDef *GPIOx, uint32_t Pin)
{
return (uint32_t)(READ_BIT(GPIOx->MODER,
(GPIO_MODER_MODER0 << (POSITION_VAL(Pin) * 2U)))
>> (POSITION_VAL(Pin) * 2U));
}
// After
__STATIC_INLINE uint32_t LL_GPIO_GetPinMode(GPIO_TypeDef *GPIOx, uint32_t Pin)
{
uint32_t pos = (POSITION_VAL(Pin) & 0xFU) * 2U; /* Pin 0..15 → shift 0..30 */
return (uint32_t)(READ_BIT(GPIOx->MODER, (GPIO_MODER_MODER0 << pos)) >> pos);
}
The same pattern should be applied to all other affected functions. The `& 0xFU` mask clamps the pin index to 15, making the maximum shift 30, which is always within the 32-bit precision. The mask has no effect on valid `LL_GPIO_PIN_x` inputs and adds no runtime overhead (compiler optimizes it away for constant pins).
The same class of issue likely exists in the STM32F4xx, STM32H7xx, STM32L4xx and other series LL GPIO headers.
---
Best regards,
André
