Skip to content

Add timer delete completion callback - #1469

Open
Zepp-Hanzj wants to merge 1 commit into
FreeRTOS:mainfrom
Zepp-Hanzj:fix/timer-delete-completion-callback
Open

Add timer delete completion callback#1469
Zepp-Hanzj wants to merge 1 commit into
FreeRTOS:mainfrom
Zepp-Hanzj:fix/timer-delete-completion-callback

Conversation

@Zepp-Hanzj

Copy link
Copy Markdown
Contributor

Description

Add a timer-delete completion callback that the timer daemon invokes while the
timer control block, callback function, and pvTimerID are still valid.

This supports API wrappers that own resources referenced by a timer and need to
release them only after the asynchronous tmrCOMMAND_DELETE has actually been
processed.

Problem observed in the field

This fixes a real failure encountered on an embedded product using CMSIS-RTOS2
timers. Intermittent HardFaults/system crashes were most reproducible when:

  • stopping and deleting a timer close to its expiry time;
  • creating a timer and deleting it immediately; and
  • deleting timers while the timer daemon command queue had a backlog.

The failure sequence was:

  1. osTimerDelete() calls xTimerDelete().
  2. xTimerDelete() returns pdPASS after placing tmrCOMMAND_DELETE in the
    timer command queue. The timer has not necessarily been deleted yet.
  3. The wrapper immediately frees the dynamically allocated callback context
    referenced by pvTimerID.
  4. Before the daemon handles the delete command, the timer can expire and the
    wrapper callback dereferences the freed context.

Depending on whether the freed block had been reused, this caused a
use-after-free, invalid function/argument access, and eventually a HardFault or
system hang.

Root cause

FreeRTOS software timer commands are asynchronous. A successful return from
xTimerDelete() means the delete command was queued; it does not mean the timer
daemon has processed it. Wrapper-owned callback context must therefore remain
alive until tmrCOMMAND_DELETE is handled.

Fix

  • Add TimerDeleteCallbackFunction_t and
    vTimerDeleteCallbackRegister().
  • Invoke the registered callback from the timer daemon immediately before the
    deleted timer control block is freed.
  • Pass the timer handle, callback function, and pvTimerID, allowing a wrapper
    to identify and release only resources it owns.

This avoids using a second queued cleanup command. Such an approach is not
atomic with xTimerDelete() and can fail when the timer queue has only one free
slot.

The corresponding CMSIS-FreeRTOS wrapper fix is
ARM-software/CMSIS-FreeRTOS#198.

Test Steps

  • Configured and built examples/cmake_example with GCC and Ninja.
  • On STM32F407ZG hardware, the old behavior reproduced the callback-context
    use-after-free in 32/32 iterations.
  • With the daemon delete callback, 8300+ timer stress iterations completed with
    no new use-after-free, exactly one delete callback per live callback, stable
    heap usage, and no HardFault/assert/stack overflow.
  • Verified that native FreeRTOS timer IDs were not released by the CMSIS cleanup
    callback.

Checklist:

  • I have tested my changes. No regression in existing tests.
  • I have modified and/or added unit-tests to cover the code changes in this Pull Request.

Related Issue

ARM-software/CMSIS-FreeRTOS#198

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Signed-off-by: hanzhijian <hanzhijian@zepp.com>
@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant