192 lines
8.3 KiB
ReStructuredText
192 lines
8.3 KiB
ReStructuredText
################################################
|
|
Fixing implicit casting for C enumeration values
|
|
################################################
|
|
|
|
:Authors: Hugues de Valon
|
|
:Organization: Arm Limited
|
|
:Contact: hugues.devalon@arm.com
|
|
:Status: Accepted
|
|
|
|
********
|
|
Abstract
|
|
********
|
|
|
|
C enumerations provide a nice way to increase readability by creating new
|
|
enumerated types but the developer should take extra care when mixing
|
|
enumeration and integer values.
|
|
This document investigates C enumerations safety and proposes strategies on how
|
|
to fix the implicit casting of the enumeration values of TF-M with other types.
|
|
|
|
**************************
|
|
C99 Standard point of view
|
|
**************************
|
|
|
|
In TF-M many implicit casts are done between integer types (``uint32_t``,
|
|
``int32_t``, ``size_t``, etc), enumerated types (``enum foobar``) and
|
|
enumeration constants (``FOOBAR_ENUM_1``).
|
|
|
|
According to the C99 standard [1]_:
|
|
|
|
**§6.2.5, 16**:
|
|
An enumeration comprises a set of named integer constant values. Each
|
|
distinct enumeration constitutes a different numerated type.
|
|
|
|
**§6.7.2.2, 2**:
|
|
The expression that defines the value of an enumeration constant shall be an
|
|
integer constant expression that has a value representable as an int.
|
|
|
|
**§6.7.2.2, 3**:
|
|
The identifiers in an enumerator list are declared as constants that have
|
|
type int and may appear wherever such are permitted.
|
|
|
|
**§6.7.2.2, 4**:
|
|
Each enumerated type shall be compatible with char, a signed integer type,
|
|
or an unsigned integer type. The choice of type is implementation-defined,
|
|
but shall be capable of representing the values of all the members of the
|
|
enumeration.
|
|
|
|
From these four quotes from the C99 standard [1]_, the following conclusions can
|
|
be made:
|
|
|
|
* an enumeration defines a new type and should be treated as such
|
|
* the enumeration constants must only contains value representable as an ``int``
|
|
* the enumeration constants have type ``int``
|
|
* the actual type of the enumeration can be between ``char``, signed and
|
|
unsigned ``int``. The compiler chooses the type it wants among those that can
|
|
represent all declared constants of the enumeration.
|
|
|
|
Example::
|
|
|
|
enum french_cities {
|
|
MARSEILLE,
|
|
PARIS,
|
|
LILLE,
|
|
LYON
|
|
};
|
|
|
|
In that example, ``MARSEILLE``, ``PARIS``, ``LILLE`` and ``LYON`` are
|
|
enumeration constants of type ``int`` and ``enum french_cities`` is a enumerated
|
|
type which can be of actual type ``char``, ``unsigned int`` or ``int``
|
|
(the compiler chooses!).
|
|
|
|
For these reasons, doing an implicit cast between an enumeration and another
|
|
type is the same as doing an implicit cast between two different types. From a
|
|
defensive programming point of view, it should be checked that the destination
|
|
type can represent the values from the origin type. In this specific case it
|
|
means four things for enumerations:
|
|
|
|
* it is always safe to assign an enumeration constant to an int, but might be
|
|
better to cast to show intent.
|
|
* when casting an enumeration constant to another type, it should be checked
|
|
that the constant can fit into the destination type.
|
|
* when casting from an integer type (``uint32_t``, ``int32_t``, etc) to an
|
|
enumeration type, it should be checked that the integer's value is one of the
|
|
enumeration constants. The comparison needs to be done on the biggest type of
|
|
the two so that no information is lost. C integer promotion should
|
|
automatically do that for the programmer (check §6.3.1.8, 1 for the rules).
|
|
* when casting from an enumeration type to an integer type, it should be checked
|
|
that the enumeration type value fits into the integer type. The value of a
|
|
variable which has the type of an enumeration type is not limited to the
|
|
enumeration constants of the type. An enumeration constant will always fit
|
|
into an ``int``.
|
|
|
|
*****************
|
|
Strategies to fix
|
|
*****************
|
|
|
|
0. Replace the enumerated type with an integer type and replace the enumeration
|
|
constant with preprocessor constants.
|
|
1. Whenever possible, try to use matching types to avoid implicit casting.
|
|
It happens, for example, for arithmetic operations, function calls and
|
|
function returns. This strategy always have the lowest performance impact.
|
|
2. When using an enumeration constant in an arithmetic operation with another
|
|
type, verify that the constant can fit in the other type and cast it.
|
|
3. When converting an integer to an enumeration type, use a conversion function
|
|
to check if the integer matches an enumeration constant. To not impact
|
|
performance too much, this function should be an inline function. If it does
|
|
not match, use (or add) the error constant or return an error value.
|
|
4. When converting an enumeration type to an integer, use a conversion function
|
|
to check that the integer type can contain the enumeration value.
|
|
|
|
************************
|
|
Design proposal for TF-M
|
|
************************
|
|
|
|
In TF-M, an action will be taken for all enumerated types and enumeration
|
|
constants that are used for implicit casting. The goal of this proposal is to
|
|
remove all implicit casting of enumeration values in TF-M.
|
|
|
|
The following enumerated types will be removed and replaced with preprocessor
|
|
constants (strategy 0). These enumerated types are not used in TF-M
|
|
but only the constants they declare.
|
|
|
|
* ``enum spm_part_state_t``
|
|
* ``enum spm_part_flag_mask_t``
|
|
* ``enum tfm_partition_priority``
|
|
|
|
The following enumerated types will be kept because they are used in the
|
|
prototypes of functions and are useful for debugging. Whenever possible,
|
|
strategy 1 will be applied to remove implicit casting related with those
|
|
enumerations but dynamic conversions will be used if the first option would
|
|
create too much change in the code base.
|
|
|
|
* ``enum tfm_status_e``: the return type of the following functions will be
|
|
changed to return the ``enum tfm_status_e`` type. These functions are already
|
|
returning the enumeration constants, but implicitly casted to an integer type
|
|
like ``int32_t``.
|
|
|
|
* ``int32_t tfm_core_set_secure_exception_priorities``
|
|
* ``int32_t check_address_range``
|
|
* ``int32_t has_access_to_region``
|
|
* ``int32_t tfm_core_check_sfn_parameters``
|
|
* ``int32_t tfm_start_partition``
|
|
* ``int32_t tfm_return_from_partition``
|
|
* ``int32_t tfm_check_sfn_req_integrity``
|
|
* ``int32_t tfm_core_check_sfn_req_rules``
|
|
* ``int32_t tfm_spm_sfn_request_handler``
|
|
* ``int32_t tfm_spm_sfn_request_thread_mode``
|
|
* ``enum tfm_buffer_share_region_e``: the following function prototypes will be
|
|
changed:
|
|
|
|
* ``tfm_spm_partition_set_share(uint32_t partition_idx, uint32_t share)`` -> ``tfm_spm_partition_set_share(uint32_t partition_idx, enum tfm_buffer_share_region_e share)``
|
|
* ``enum tfm_memory_access_e``
|
|
* ``enum attest_memory_access_t``
|
|
* ``enum engine_cipher_mode_t``
|
|
* ``mbedtls_cipher_type_t``
|
|
|
|
The following enumerated types are used for error code values of Secure service
|
|
calls. They should be kept as they are part of the interface and might be used
|
|
by external parties in Non-Secure code. For the Initial Attestation service,
|
|
the enumeration is defined in the PSA Attestation API specifications.
|
|
|
|
* ``enum psa_attest_err_t``
|
|
* ``enum psa_audit_err``
|
|
* ``enum tfm_platform_err_t``
|
|
|
|
Implicit casting related with these enumerations is happening in two locations
|
|
of TF-M and need conversion functions in those locations, because the types can
|
|
not be changed:
|
|
|
|
* In the Non-Secure Client library, all of the Secure Service functions
|
|
implicitly cast the ``uint32_t`` returned by ``tfm_ns_lock_dispatch`` to
|
|
these enumerated types. Strategy 3 is needed here.
|
|
* In all of the veneer functions, there is an implicit cast from the ``int32_t``
|
|
value returned by the SVC request function (``tfm_core_*_request``) to these
|
|
enumerated types. Strategy 3 is needed here as well. The implicit cast will
|
|
eventually be removed if all of the services are using the Uniform Signatures
|
|
Prototypes so that the veneer functions all return ``psa_status_t`` which is
|
|
an ``int32_t``.
|
|
|
|
If the interface of those services can be changed, these enumerations could be
|
|
removed and replaced with the ``psa_status_t`` type to remove the implicit
|
|
casting.
|
|
|
|
.. [1] C99 standard: http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
|
|
|
|
|
|
--------------
|
|
|
|
|
|
*Copyright (c) 2019-2020, Arm Limited. All rights reserved.*
|
|
|