Skip to content

[Bug]: HAL_SD_IRQHandler never reaches its error branch on data error in IT mode (IRQ storm) #347

Description

@mrjimenez

Bug Summary

HAL_SD_IRQHandler never reaches its error branch when a data error occurs
during an interrupt-mode transfer, causing the SD interrupt to refire forever
and lock up the system.

Detailed Description

In stm32h7xx_hal_sd.c, HAL_SD_IRQHandler() tests its conditions in a single
if / else if chain, and the FIFO branches come before the error branch:

void HAL_SD_IRQHandler(SD_HandleTypeDef *hsd)
{
  /* Check for SDMMC interrupt flags */
  if ((__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF) != RESET) && ((context & SD_CONTEXT_IT) != 0U))
  {
    SD_Read_IT(hsd);
  }
  ...
  else if ((__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXFIFOHE) != RESET) && ((context & SD_CONTEXT_IT) != 0U))
  {
    SD_Write_IT(hsd);
  }
  ...
  else if (__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT |
                                  SDMMC_FLAG_RXOVERR  | SDMMC_FLAG_TXUNDERR) != RESET)
  {
    /* error handling: never reached in the scenario below */
  }
}

TXFIFOHE and RXFIFOHF are level-triggered status flags that cannot be
cleared by software — they only change when the FIFO is drained or filled. If a
data error (TXUNDERR, RXOVERR, DCRCFAIL, DTIMEOUT) occurs while such a
flag is asserted, the handler keeps taking the FIFO branch, the error branch is
never evaluated, the error flags are never cleared, and the interrupt refires
immediately and indefinitely.

A second, related problem: when the error branch does run, its
__HAL_SD_DISABLE_IT() mask does not include SDMMC_IT_TXFIFOHE /
SDMMC_IT_RXFIFOHF, so the IRQ line can remain asserted even after the error is
handled.

How it was observed. An SD card stopped accepting data mid-write (a physical
card fault; only a power cycle recovers it). The resulting TXUNDERR left
TXFIFOHE asserted and the board became completely unresponsive: the ISR
starved every thread, so networking and the console died while interrupts kept
firing. Diagnosed via SWD — the CPU was permanently inside HAL_SD_IRQHandler.

Suggested fix, which has been running in our firmware since 2026-07-10
(including a 10-minute stress test writing 512 KB blocks continuously while
acquiring sensor data, with no regression):

  1. skip the FIFO branches while any data error flag is pending, so the error
    branch becomes reachable;
  2. add SDMMC_IT_TXFIFOHE / SDMMC_IT_RXFIFOHF to the disable mask on the
    error path.

The patch is proposed to the Zephyr HAL mirror as a temporary fix in
zephyrproject-rtos/hal_stm32#395, whose maintainer asked that the issue be
reported here first.

Note on other series. The same if / else if ordering is present in the
HAL_SD_IRQHandler of the STM32F7, H5, H7RS, L4, L5, MP13, MP2, N6, U3 and U5
drivers. I could only test on STM32H7.

Expected Behavior

A data error during an interrupt-mode transfer is handled: the error flags are cleared, the interrupts are disabled, and the transfer is aborted with the error reported to the application.

Actual Behavior

The error branch is never reached; the interrupt refires forever and starves the entire system, which requires a reset to recover.

Environment

STM32H747I-DISCO (STM32H747XI, Cortex-M7), STM32CubeH7 v1.12.1, SDMMC1 in interrupt mode (not IDMA), GCC (Zephyr SDK), Zephyr RTOS 4.4.1.

Severity

Critical

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions