10. C29x Security Model

The TI C29x provides the ability to protect individual calls and frames. At the assembly level, protected calls use a separate CALL.PROT opcode, as opposed to CALL, which is used for unprotected calls. Protected frames must end with a RET.PROT instruction, as opposed to RET, which is used by unprotected returns.

Protected calls behave as follows:

  • All registers (including the stack pointer) are zeroed out, except those denoted by a PRESERVE instruction that can occur in parallel (for call arguments).

  • A context containing the current stack pointer is pushed onto a hardware stack.

  • The call cannot be delayed.

Protected frames behave as follows:

  • All registers are zeroed out, except those denoted by a PRESERVE instruction that can occur in parallel (return values).

  • Pops the context that was pushed by a protected call.

  • The return cannot be delayed.

10.1. Compiler Support for Protected Calls

C/C++ functions can be declared/defined using the c29_protected_call function attribute. This attribute causes the c29clang compiler to act as if calls to that function or function type cross a security STACK. At the assembly level, such calls CALL.PROT, RET.PROT, and their associated handshake instructions. See Function Attributes for more about function attributes.

For example:

void my_function() __attribute__((c29_protected_call));

void normal_function();
void (*protected_fn_ptr)() __attribute__((c29_protected_call)) = &normal_function; // Suppressible warning in C

// Be aware: This call will use CALL.PROT
// If normal_function is defined in C, it will not have the required handshake instructions at its start.
protected_fn_ptr();

The c29clang compiler identifies and produced an error for any protected function that cannot be called due to stack requirements. Example causes for such errors include passing excessive numbers of integer, pointer, or floating-point arguments in registers, returning a structure type by value, and use of variadic arguments.

10.2. Linker Support for Protected Calls

The linker accepts the SECURE_GROUP attribute in the SECTIONS directive to define a section to contain protected calls. The syntax is as follows:

SECURE_GROUP ( name [, (public|private)])

The following example causes the .text_caller section to contain the protected calls:

SECTIONS {
    .text_caller : SECURE_GROUP(CALLER_GROUP) { *(.text.caller) } > RO_CODE
}

The following example creates a public SECURE_GROUP instead of a private SECURE_GROUP:

SECTIONS {
    .text_caller : SECURE_GROUP(CALLER_GROUP, public) { *(.text.caller) } > RO_CODE
}

By default, SECURE_GROUPs are private. Output sections without a SECURE_GROUP are assumed to be callable by any code in the application.

The linker emits an error if it encounters any of the following cases:

  • The caller and callee are in different SECURE_GROUPs, and the caller is not a protected call.

  • The caller and callee are in different SECURE_GROUPs, and the callee does not have a protected frame.

  • The caller and callee are in the same SECURE_GROUPs, but they do not agree with regards to call protection.

To correct these errors, mark both the caller and callee with the c29_protected_call function call attribute.

The linker avoids these errors if the callee SECURE_GROUP is marked public, unless the callee requires the stack for any of its arguments.