Skip to content

Guard xTaskCreate() stack allocation against size_t overflow - #1488

Open
JacksonBopp wants to merge 1 commit into
FreeRTOS:mainfrom
JacksonBopp:fix/task-stack-depth-overflow
Open

Guard xTaskCreate() stack allocation against size_t overflow#1488
JacksonBopp wants to merge 1 commit into
FreeRTOS:mainfrom
JacksonBopp:fix/task-stack-depth-overflow

Conversation

@JacksonBopp

Copy link
Copy Markdown

Fixes #1472.

prvCreateTask() computes the dynamic stack allocation size as uxStackDepth * sizeof(StackType_t) with no overflow check. On a target where size_t and StackType_t are both 32 bits, a large enough uxStackDepth wraps the multiplication to a small allocation. prvInitialiseNewTask() then continues to use the original, larger uxStackDepth for stack pointer arithmetic (pxStack[uxStackDepth - 1] and the end-of-stack pointer), which reads and writes past the undersized allocation.

This adds tskSTACK_DEPTH_WILL_OVERFLOW() and checks it at both dynamic allocation sites in prvCreateTask() (the portSTACK_GROWTH > 0 and <= 0 branches), falling back to the existing NULL-allocation failure path instead of calling pvPortMallocStack() with a size that has already wrapped.

Verification: I reproduced the exact 32-bit wraparound described in the issue (depth 0x40000020 words) in a standalone program modeling the same arithmetic, confirming the allocation silently wraps to 128 bytes without the guard and is correctly rejected with it, with no effect on a normal in-range depth. I also compiled the patched tasks.c clean (-Wall -Wextra, no warnings) against the POSIX/Linux port, which exercises the portSTACK_GROWTH <= 0 branch. I wasn't able to run the full CMock unit test suite in this environment (FreeRTOS/Test/CMock lives in the umbrella FreeRTOS/FreeRTOS repo and needs Ruby + make, neither of which I had available), so I'm flagging that gap rather than claiming coverage I don't have.

uxStackDepth * sizeof(StackType_t) was computed without an overflow
check before being passed to pvPortMallocStack(). On a target where
size_t and StackType_t are both 32 bits, a large enough uxStackDepth
wraps the multiplication to a small allocation, while
prvInitialiseNewTask() still uses the original, larger uxStackDepth
for stack pointer arithmetic (pxStack[uxStackDepth - 1] and the
end-of-stack pointer), producing an out-of-bounds access.

Add tskSTACK_DEPTH_WILL_OVERFLOW() and check it at both dynamic
allocation sites in prvCreateTask() (portSTACK_GROWTH > 0 and <= 0),
falling back to the existing NULL-allocation failure path rather than
calling pvPortMallocStack() with a size that has already wrapped.

Verified with a standalone reproduction of the exact 32-bit
arithmetic against the depth from the report (0x40000020 words),
confirming the wrap without the guard and rejection with it, and
compiled tasks.c clean under the POSIX/Linux port
(portSTACK_GROWTH <= 0 branch).
@sonarqubecloud

sonarqubecloud Bot commented Sep 5, 2026

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.

[BUG] xTaskCreate() unsigned integer overflow when calculating stack size

1 participant