@page bsl-developers BSL Developer Topics
This page covers information for Backend Developers, or more specifically those who may be implementing a BPSec backend, or a new Security Context or Policy Provider.
This page contains conventions necessary for BSL developers and helpful for BSL users to understand.
Generally, all functions should be "namespaced" with BSL_ or a similar variant.
- Structs should take the form of
BSL_MyStruct_t. - Functions operating on structs should take the form of
BSL_MyStruct_FunctionOnStruct. - All structures should provide an
_Init,_Deinit,_IsConsistent, and_Sizeof. - Functions not operating over a context struct should use
BSL_ModuleName_Function.
Generally, functions returning ints indicate error codes.
- A code of
0(BSL_SUCCESS) means no error. - A negative number ALWAYS indicates an error.
- A positive number can be something else depending on context.
Enumerations take the format of BSL_EnumName_e (with a typedef).
Variants of the enum should be "namespaced" with the enum, to manage clutter.
E.g.,
typedef enum {
BSL_MYEXAMPLEENUM_OPTION1 = 1,
BSL_MYEXAMPLEENUM_OPTION2
// ...
} BSL_MyExampleEnum_e;
More notes forthcoming.
- Most functions that return an
int(or a typedef over anint) should be interpreted as returning a status code. - A negative status code always indicates a failure. This enables simple checking for failure:
if (BSL_MyFunc(arg) < 0) ... - A zero indicates non-error, with a typedef
BSL_SUCCESS. - A positive number may indicate a non-failure with optional supplementary data (e.g., number of bytes consumed).
- Functions returning integers that do not indicate error codes will generally return a
size_toruint64_t.
- A NULL pointer for almost any function argument is considered an anomaly and a programmer-error.
- Note the @nullable doxygen command to indicate whenever a parameter may be NULL.
- The public front-end API is more gracious to NULL arguments (returns error code). It is considered a runtime anomaly.
- Code further in the backend performs more
assertchecking for NULL values. A NULL argument here is not a runtime anomaly, but rather an indication the programmer made a mistake. - If you are not already familiar, see the "Billion Dollar Mistake".
- *Note: The
GetBlockMetadatafunction does permit NULL arguments, as it only populates arguments that are requested. - *Note: Certain functions that wrap OpenSSL functionality may also permit NULLs to be consistent with its interface.
- The BSL is intended for flight systems, mission-critical systems, and security-critical systems.
- Safety and correctness are the primacy concerns.
- Basic idea: All functions validate their context's state, and only continue to execute if the state is valid. I.e., "If it's not in a proper state, it's not going to run". It is safer to crash than continue with undefined behavior.
- All function arguments are aggressively checked. Macros help indicate argument sanity, property checks and pre/post conditions.
- Note all structs have an
_IsConsistent()function to check for validity of its state. - Allocated memory for structs must be zero at initialization time (for security and to check for accidental re-use).
- Generally, once backend function produces and populates a struct, it should not be modified.
- Any operations over a struct must ensure that any pointers contained within it point to objects whose lifetime equals or exceeds its own.
- Any dynamically allocated memory should be released in the same function.
- The caller of a function pre-allocates the memory for the called function.
- Description
- The C standard library does not provide containers. Arrays is all we have, so that's what we have to work with.
- Third party libraries providing containers may be more hassle and risk than they are worth.
- Keep M*Lib usage to the BSL backend, and use standard/primitive structs for frontend API. The frontend should not include any M*Lib headers.
All top-level public API must have inline Doxygen comment blocks (e.g., /** docs */) preferably within the same header in which the API is declared.
For reference, Doxygen comment blocks can contain complex markup based on a large set of available commands.
When M*LIB macros are used to declare type-safe containers, the Doxygen inspection of the macro-expanded code should be inhibited but there should also be a explicit Doxygen block to provide explanation of the purpose of the struct and a reference to the type of its contents.
An example of this is below (corresponding to @ref BSL_SecCtxDict_t).
@verbatim /** @struct BSL_SecCtxDict_t
- Stable dict of security context descriptors (key: context id | value: descriptor struct) */ /// @cond Doxygen_Suppress DICT_DEF2(BSL_SecCtxDict, uint64_t, M_BASIC_OPLIST, BSL_SecCtxDesc_t, M_POD_OPLIST) /// @endcond @endverbatim
For definitions from IETF RFCs and other sources, @cite [source] in the Doxygen header.
If possible (e.g. for RFCs) include the document number in the text for convenience, as in RFC XXXX @cite rfcXXXX.
Within the BSL source tree, all library contents are under the src directory and all tests are under the test directory.
Within the src directory the tree structure of all header files is the same as when the library is installed to the host filesystem.
File names follow CFE-style conventions.
The top of each file should contain a Doxygen block for the file itself, including an association with a specific module. An example of this is below.
@verbatim /** @file
- @ingroup frontend
- Brief summary.
- Detailed description follows. */ @endverbatim
Symbol names follow the C99 convention of lowercase names with underscore separators.
All public API prefixed with BSL_ to provide a virtual 'namespace' to the API.
All public API functions should follow the general [noun]_[verb] convention for naming.
More generally, public API functions should follow the pattern BLS_[StructureContext]_[VerbPhraseFunctionName]
When a set of functions are related to a struct, they should have the same noun prefix as the struct name (e.g., the struct example_s with typedef example_t should have corresponding functions named like example_XYZ()).
Beyond the common naming, functions related to a struct should be indicated using the @memberof Doxygen command.
An example of this is below.
@verbatim /** Brief summary.
- Detailed description follows. */ typedef struct BSL_Example_s{ ... } BSL_Example_t;
/** Brief summary.
- Detailed description follows.
- @memberof BSL_Example_t
- ... @param and @return ... */ int BSL_Example_DoThing(bsl_example_t *obj, ...); @endverbatim
Functions that cannot fail should have void return type.
Functions that can fail should have int return type and use the following common values:
Negative Values: ALWAYS indicates failure. Specific error code can be captured by the actual value of the returned integer.
Zero: Means success (unless clearly indicated otherwise in exceptional use-cases)
Positive: Implies success, with some supplementary data. For example, a _CreateBlock() function, upon success, would return a positive integer containing the ID of the block just created.
NOTE!! This pattern is being adapted. A negative value indicates error, zero indicates success. There may be times when there may be meaningful context associated with a positive value (e.g., number of bytes written).
Much of the public API involves state kept in a struct with associated functions to inspect and manipulate the state of that struct.
Generally, users of the API should not access struct members directly. But specific documentation on each struct will define its specific public API.
All BSL structs must have an associated initialization function. Members of the struct cannot be accessed before its initialization and functions called on the struct will have undefined behavior.
All BSL structs must have an associated de-initialization function. After its de-initialization the members of the struct will no longer have well defined state.
To help with troubleshooting, de-initialization should set pointers set to NULL and other values to a well-defined state. One option is to use memset() to zeroize the entire struct.
When heap memory is needed at BSL runtime, the following functions are used and have the same signature and semantics as the corresponding C99 functions indicated below.
- [BSL_malloc](@ref BSL_malloc) as
malloc() - [BSL_realloc](@ref BSL_realloc) as
realloc() - [BSL_calloc](@ref BSL_calloc) as
calloc() - [BSL_free](@ref BSL_free) as
free()
These can be modified using the @ref BSL_DynMemHostDescriptors_t interface.
This section contains references to commonly used macros defined for the BSL
To help with the error reporting conventions above, the following macros can be used to simplify function precondition checking. The precondition checks (on function parameters or on any other state generally) should be the first thing inside the function definition.
- [BSL_CHKRET(cond, val)](@ref BSL_CHKRET) for general error values
- [BSL_CHKNULL(cond)](@ref BSL_CHKNULL) when the function has a pointer return type
- [BSL_CHKERR1(cond)](@ref BSL_CHKERR1) when the function has an
intreturn type - [BSL_CHKVOID(cond)](@ref BSL_CHKVOID) when the function has an
voidreturn type - [BSL_CHKFALSE(cond)](@ref BSL_CHKFALSE) when the function has an
boolreturn type
Enumerations with explicit values must be justified with citations, for example the declarations of @ref BSL_BundleBlockTypeCode_e. Otherwise, they should not be given values.
Whenever possible, enum values starting with zero should be avoided (since many variables default to zero, we want to avoid the case of matching an enum variant with an uninitialized, zeroed-out, value)
Enumerations should be typedef-ed with a _e suffix.
Enum values should be full SCREAMING_CASE matching the name of the struct.
@verbatim
typedef enum {
BSL_MYENUM_OPTION1 = 1,
BSL_MYENUM_OPTION2,
...
} BSL_MyEnum_e;
@endverbatim
Conventions for unit testing using the Unity testing library are:
- The name of the test source file should be the same name as the unit-being-tested prefixed by
test_. - Where possible, name of the test functions should be the name of the function prefixed by
test_and suffixed by the test condition, either_validor_invalidor similar.