Skip to content

Bug: UART reconfiguration can expose temporary callback or data-mask state to RX IRQ handlers #22409

Description

@andrewlihhh

Description

Unlike the callback re-init cases, this issue is about in-place UART state updates.

Here the problem is that UART reconfiguration updates shared RX state in place, while RX IRQ delivery is still possible. The IRQ handler can then observe temporary values that belong to the middle of reconfiguration, not to the final UART mode.

Common sequence:

  1. RX IRQ is enabled for one UART instance
  2. thread context enters UART mode or callback reconfiguration
  3. shared RX state is rewritten in place
  4. RX IRQ arrives in that window
  5. ISR uses a temporary data_mask, callback pointer, or callback argument value

Related issues:

  • Tracking issue: #22405

Scope

  • RIOT source snapshot: bbe5252d00f98658fc6493ede1a3e9f8f4102f9d (2026-06-08)
  • Affected files:
    • cpu/stm32/periph/uart.c
    • cpu/gd32v/periph/uart.c
  • Grouped cases:
    • stm32-uart-unique-1-isr-ctx-0-data-mask
    • stm32-uart-unique-2-isr-ctx-0-data-mask
    • stm32-uart-unique-3-isr-ctx-0-data-mask
    • stm32-uart-unique-4-isr-ctx-0-data-mask
    • stm32-uart-unique-5-isr-ctx-0-data-mask
    • gd32v-uart-unique-3-isr-ctx-0-data-mask
    • gd32v-uart-unique-4-isr-ctx-0-data-mask
    • gd32v-uart-unique-5-isr-ctx-0-rx-cb
    • gd32v-uart-unique-6-isr-ctx-0-arg
    • gd32v-uart-unique-8-isr-ctx-0-rx-cb
    • gd32v-uart-unique-9-isr-ctx-0-data-mask

Representative code paths

  • STM32 UART: uart_mode() -> UART_x_ISR() -> irq_handler()
  • GD32V UART mode: uart_mode() -> _uart_isr() -> _irq_handler()
  • GD32V UART callback setup: uart_init() -> _uart_isr() -> _irq_handler()

Representative source snippets

cpu/stm32/periph/uart.c rewrites IRQ-visible RX state in place:

isr_ctx[uart].rx_cb     = rx_cb;
isr_ctx[uart].arg       = arg;
isr_ctx[uart].data_mask = 0xFF;

uart_mode() can later update the same data_mask while RX IRQ delivery is possible:

isr_ctx[uart].data_mask = 0xFF;
...
isr_ctx[uart].data_mask = 0x3F;
...
isr_ctx[uart].data_mask = 0x7F;

The RX IRQ handler consumes that state:

isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
                    (uint8_t)dev(uart)->RDR_REG & isr_ctx[uart].data_mask);

cpu/gd32v/periph/uart.c has the same data_mask pattern:

isr_ctx[uart].data_mask = 0xFF;
...
isr_ctx[uart].data_mask = 0x3F;
...
isr_ctx[uart].data_mask = 0x7F;
isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
                    (uint8_t)dev(uart)->DATA & isr_ctx[uart].data_mask);

Local evidence

  • A representative GD32V UART case was replayed locally with a QEMU-based harness.
  • A replay log or minimized reproducer is available for the GD32V UART case.

Expected results

RX IRQ handlers should only observe final stable RX callback/configuration state for the selected UART mode.

Actual results

RX IRQ handlers may observe temporary in-progress state during UART reconfiguration.

Analysis

This group is about in-place shared-state updates, not only re-init.

UART mode or callback reconfiguration rewrites IRQ-visible RX state field by field. If RX IRQ fires in middle of that update, ISR can use a temporary data_mask, callback pointer, or callback argument value that does not match final UART mode.

Suggested Fix

  • prevent RX IRQ delivery while shared RX state is being rewritten, or
  • build complete new RX state first and switch to it atomically, or
  • split callback/config state so ISR never sees partially updated values

Metadata

Metadata

Assignees

No one assigned

    Labels

    AI: VibedPR/Issue appears to be more AI than Human.Type: bugThe issue reports a bug / The PR fixes a bug (including spelling errors)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions