Skip to content

Fixed the kernel object leaks on the static creation error paths - #584

Merged
fdesbiens merged 1 commit into
eclipse-threadx:devfrom
fdesbiens:fix/static-create-kernel-object-leaks
Aug 9, 2026
Merged

Fixed the kernel object leaks on the static creation error paths#584
fdesbiens merged 1 commit into
eclipse-threadx:devfrom
fdesbiens:fix/static-create-kernel-object-leaks

Conversation

@fdesbiens

@fdesbiens fdesbiens commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Fixes the two sibling leaks noted in #582, now with test coverage from #583 rather than by inspection.

The defect

xQueueCreateStatic() and xTaskCreateStatic() take their storage from the caller, so neither leaks memory. Both still create ThreadX objects, and both still return NULL when a later step fails. The caller is left without a handle and cannot call the matching delete function, so an object already created stays registered in the kernel, pointing into a caller buffer the application is now free to reuse or discard.

Three paths were affected:

Function Failing step Left behind
xQueueCreateStatic write_sem creation read_sem
xTaskCreateStatic tx_thread_create notification_sem
xTaskCreateStatic tx_thread_resume notification_sem and the thread

The third one was not in the original list; it turned up while writing the tests for the second.

The fix

Each path now deletes what it had already created. The resume path terminates the thread before deleting it, since a thread created with TX_DONT_START is suspended rather than terminated and tx_thread_delete() would otherwise refuse it. That is the same order the idle task uses when it reaps a deleted task, so the layer is consistent with itself.

Eight lines of product code in total.

Verification

Six new checks, three of which are the paths above. The suite from #583 gained thread resume as an injectable entry point to reach the last one.

Against the layer as it stands on dev, exactly the three new count checks fail, and they name what was abandoned:

static write_sem failure unwinds read_sem            FAIL
                                                       got      alloc=0 release=0 create=2 delete=0
                                                       expected alloc=0 release=0 create=2 delete=1
static thread failure unwinds the semaphore          FAIL
                                                       got      alloc=0 release=0 create=2 delete=0
                                                       expected alloc=0 release=0 create=2 delete=1
static resume failure unwinds thread and semaphore   FAIL
                                                       got      alloc=0 release=0 create=2 delete=0
                                                       expected alloc=0 release=0 create=2 delete=2

With the fix, all three tests pass.

Each static failure case now uses its own control block. Sharing one made the numbers lie: on the unfixed layer, the semaphore abandoned by the thread-failure case poisoned the buffer the next case cleared and reused, so that case reported create=1 instead of create=2 and pointed at the wrong thing. Independent buffers mean a future regression on one path cannot mislead the diagnosis of another.

One thing left alone

xTaskCreate() handles a tx_thread_resume() failure differently from the static variant it otherwise mirrors: it calls TX_FREERTOS_ASSERT_FAIL(), then increments the task count and returns pdPASS. The caller is told the task started when it did not, and holds a handle to a task that will never run.

That is a behavioural question rather than a leak — deciding whether the resume failure should propagate means deciding what a caller can expect from a task that exists but is not scheduled — so it is out of scope here.

xQueueCreateStatic() and xTaskCreateStatic() take their storage from the
caller, so neither leaks memory, but both create ThreadX objects and both
return NULL when a later step fails. The caller is left without a handle and
cannot call the matching delete function, so any object already created stays
registered in the kernel, pointing into a caller buffer that the application
is now free to reuse or discard.

Three paths were affected. xQueueCreateStatic() abandoned the read semaphore
when the write semaphore could not be created. xTaskCreateStatic() abandoned
the notification semaphore when the thread could not be created, and abandoned
both the semaphore and the thread when the thread could not be resumed.

Delete what was already created before returning on each of them. The resume
path terminates the thread before deleting it, since a thread created with
TX_DONT_START is suspended rather than terminated, which is the same order the
idle task uses when it reaps a deleted task.

Extend the regression suite to cover all three paths, and add thread resume to
the set of entry points the harness can force to fail. Each static failure case
now uses its own control block, so a future regression on one path cannot carry
damage into the next case and report misleading counts there.

Verified against the layer as it stands on dev, where the three new checks fail
with the objects left behind, and against the fixed layer, where the suite
passes.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
@fdesbiens
fdesbiens merged commit cfed1d8 into eclipse-threadx:dev Aug 9, 2026
1 check passed
@fdesbiens
fdesbiens deleted the fix/static-create-kernel-object-leaks branch August 9, 2026 12:44
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