Skip to content

[Bug] pin_api_attach_irq legacy path publishes hdr and args without IRQ protection #11784

Open

Description

@andrewlihhh

RT-Thread Version

v5.3.0 (master branch, commit 54e5164)

Affected area

Device drivers

Hardware/BSP vendor

Not applicable / Other

Architecture

Not applicable / Other

Board and hardware details

This issue affects any board where the GPIO controller does not implement the pin_attach_irq operation, forcing the code to fall back to the legacy ISR handler path.

Develop Toolchain

Other

Describe the bug

In components/drivers/pin/dev_pin_dm.c, when gpio->ops->pin_attach_irq is NULL (legacy fallback path), the function pin_api_attach_irq() writes the handler pair without masking IRQs:

legacy_isr->hdr = hdr;
legacy_isr->args = args;

Meanwhile, pin_pic_handle_isr() reads these fields from ISR context:

if (legacy_isr->hdr)
{
    legacy_isr->hdr(legacy_isr->args);
}

Steps to reproduce the behavior

Race condition scenario:

  1. Application calls pin_api_attach_irq() to update the pin interrupt handler
  2. The function writes legacy_isr->hdr = new_handler
  3. Before writing legacy_isr->args = new_args, a pin interrupt fires
  4. pin_pic_handle_isr() executes and calls new_handler(old_args) with mismatched arguments

This can lead to:

  • Incorrect argument being passed to the new handler
  • Potential crashes if the new handler expects a different argument type/structure
  • Unpredictable behavior

Expected behavior

The handler function pointer and its argument should be updated atomically. Either both should reflect the old values, or both should reflect the new values. No intermediate state should be visible to the ISR.

Other additional context

Affected code path: This bug only applies to the legacy fallback path when the GPIO controller does not implement the pin_attach_irq operation. Modern controllers that provide this operation are not affected.

Root cause: The two stores (hdr and args) are not atomic. On any architecture, an interrupt can occur between them.

Suggested fix: Protect the critical section with IRQ masking:

rt_base_t level = rt_hw_interrupt_disable();
legacy_isr->hdr = hdr;
legacy_isr->args = args;
rt_hw_interrupt_enable(level);

Or use the pin lock with rt_spin_lock_irqsave if appropriate.

Note: The volatile qualifier does not make the pair of stores atomic against interrupts.

Activity

  1. added
    bugThis PR/issue is a bug in the current code.
    on Sep 7, 2026
  2. github-actions commented on Sep 7, 2026

    @github-actions

    如需认领并处理此 Issue,请评论 /claim。

    To claim and work on this issue, comment /claim.

  3. github-actions commented on Sep 7, 2026

    @github-actions

    以下 Issue 可能与当前问题相关;这是自动检索结果,尚未确认重复:

    The following issues may be related. These automated suggestions are not confirmed duplicates:

  4. andrewlihhh commented on Sep 7, 2026

    @andrewlihhh
    Author

    For clarification, #11783 is the initial version of this report. I closed it after realizing that it did not follow the repository's bug issue template, and resubmitted the issue here using the proper template.

    #11784 should be treated as the current report.

  5. GuEe-GUI commented on Sep 9, 2026

    @GuEe-GUI
    Contributor

    I can think of only two scenarios where you would call pin_api_attach_irq:

    1. You are requesting a pin IRQ for the first time: this happens either when the target PIN is currently disabled, or when there is a bug in the existing implementation that needs to be fixed.
    2. You are replacing an existing ISR: in this case, users are expected to disable the old IRQ before attaching the new ISR.

    As long as the API is invoked correctly by the user, pin_api_attach_irq should always operate in an IRQ-safe state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions