Compiler-instrumented Stack Canaries
With stack protector enabled, the compiler saves a guard value in selected
stack frames and checks it again before returning. If the value has changed,
the function calls __stack_chk_fail(). In OP-TEE, the reference guard is
stored in __stack_chk_guard.
The compiler arguments below are standard stack protector options supported by both GCC and LLVM/Clang.
Compiler argument |
Effect |
|---|---|
|
Protects functions selected by the compiler’s basic stack protector heuristics. |
|
Extends protection to more functions, including ones with local arrays,
|
|
Protects all functions. |
|
Disables stack protector instrumentation. |
Note
This is separate from the thread stack boundary canaries in
core/kernel/thread.c controlled by CFG_WITH_STACK_CANARIES.
Pseudo TAs are part of the OP-TEE core image and therefore follow core stack protector settings, not TA stack protector settings.
Stack canaries for OP-TEE core
How to enable stack canaries for OP-TEE core
The OP-TEE core build accepts the following options:
The CFG_* option enables the matching compiler argument. The Build
logic column shows where that argument is added to the build. These options
are defined in mk/config.mk.
These flags are added by the common core build logic in core/core.mk. They
are not specific to RISC-V; Arm uses the same selection mechanism.
CFG name |
Compiler argument |
Build logic |
Default |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
With none of the three options selected, the core build uses
-fno-stack-protector.
Core implementation on RISC-V
RISC-V platforms use the default weak implementation of
plat_get_random_stack_canaries() from core/kernel/boot.c. It fills one
or more canaries with crypto_rng_read() and then clears the low byte of
each value. This leaves a null byte in the guard, which helps against some
string-based overwrite patterns.
With CFG_NS_VIRTUALIZATION=y, the default weak implementation cannot use
the core RNG at that point and falls back to a fixed value, while printing a
warning. Platforms using virtualization should override
plat_get_random_stack_canaries() if compiler stack protection or thread
stack boundary canaries are required.
After boot_init_primary_final(), the RISC-V entry code in
core/arch/riscv/kernel/entry.S fetches one native-word canary and writes
it to __stack_chk_guard. The guard variable and __stack_chk_fail()
live in lib/libutils/isoc/stack_check.c. A failed check prints
stack smashing detected and then panics the core.
Stack canaries for user mode TAs
How to enable stack canaries for TAs
User mode TAs and the TA dev kit libraries accept the following options:
The CFG_* option enables the matching compiler argument. The Build
logic column shows where that argument is added to the build. These options
are defined in mk/config.mk.
These flags are added by the common TA build logic in ta/ta.mk. They are
not specific to RISC-V, and the same mechanism is used for other supported TA
architectures.
CFG name |
Compiler argument |
Build logic |
Default |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
With none of the three options selected, the TA build uses
-fno-stack-protector.
The TA startup code in ta/user_ta_header.c initializes the guard lazily in
__ta_entry() before dispatching to __utee_entry().
__ta_entry() is marked __no_stack_protector so it can seed the guard
before any compiler-generated canary check applies to that function itself.
When _CFG_TA_STACK_PROTECTOR is enabled and the TA enters for the first
time, __ta_entry() calls _utee_cryp_random_number_generate() for a
uintptr_t canary, clears its low byte to leave a null byte in the guard,
stores the result into __stack_chk_guard, and records that the guard has
been initialized.
Subsequent entries reuse the same guard for the lifetime of the TA instance.
If a protected TA function detects corruption, __stack_chk_fail() in
lib/libutils/isoc/stack_check.c prints stack smashing detected and
calls _utee_panic(TEE_ERROR_OVERFLOW).
TA implementation on RISC-V
RISC-V user mode TAs use the generic TA stack protector initialization path in
ta/user_ta_header.c.
User mode TA support is still platform-specific on RISC-V:
Platform file |
User mode TA support |
|---|---|
|
Exposes |
|
Exposes |
|
Forces |