Anssi-Guide-Rules For Secure C Language Software Development-V1.4
Anssi-Guide-Rules For Secure C Language Software Development-V1.4
SOFTWARE DEVELOPMENT
ANSSI GUIDELINES
ANSSI-PA-073
24/03/2022
TARGETED AUDIENCE:
Warning
This document, written by ANSSI, the French National Information Security Agency,
presents the “Rules for secure C language software development”. It is freely available
at www.ssi.gouv.fr/en/.
It is an original creation from ANSSI and it is placed under the “Open Licence v2.0” published
by the Etalab mission [ETALAB].
According to the Open Licence v2.0, this guide can be freely reused, subject to mentionning its
paternity (source and date of last update). Reuse means the right to communicate, distribute,
redistribute, publish, transmit, reproduce, copy, adapt, modify, extract, transform and use,
including for commercial purposes
These recommendations are provided as is and are related to threats known at the publica-
tion time. Considering the information systems diversity, ANSSI cannot guarantee direct
application of these recommendations on targeted information systems. Applying the fol-
lowing recommendations shall be, at first, validated by IT administrators and/or IT security
managers.
This document is a courtesy translation of the initial French document “Règles de pro-
grammation pour le développement sécurisé de logiciels en langage C”, available at
www.ssi.gouv.fr. In case of conflicts between these two documents, the latter is considered
as the only reference.
Document changelog:
VERSION DATE CHANGELOG
1.4 24/03/2022 First English version
2 Coding convention 8
5 Compilation 27
5.1 Mastery of the compilation phase . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2 Compilation without errors nor warnings . . . . . . . . . . . . . . . . . . . . . . . . 28
5.3 Use of security features provided by compilers . . . . . . . . . . . . . . . . . . . . . 30
5.4 Debug and release modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
10 Expressions 82
10.1 Integer expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
10.2 Readability of arithmetic operations . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
10.3 Use of parentheses to make explicit the order of the operators . . . . . . . . . . . . 85
10.4 No multiple comparison of variables without parentheses . . . . . . . . . . . . . . 86
10.5 Parentheses around elements of a boolean expression . . . . . . . . . . . . . . . . . 87
10.6 Implicit comparison with 0 prohibited . . . . . . . . . . . . . . . . . . . . . . . . . . 88
10.7 Bitwise operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
10.8 Boolean assignment and expression . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.9 Multiple assignment of variables prohibited . . . . . . . . . . . . . . . . . . . . . . 92
10.10 Only one statement per line of code . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
10.11 Use of floating-point numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
10.12 Complex numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
13 Functions 107
13.1 Correct and consistent declaration and definition . . . . . . . . . . . . . . . . . . . 107
13.2 Documentation of functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
13.3 Validation of input parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
19 Miscellaneous 149
19.1 Comment format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
19.2 Implementation of a “canary ” mechanism . . . . . . . . . . . . . . . . . . . . . . . 149
19.3 Assertions of development and assertions of integrity . . . . . . . . . . . . . . . . . 150
19.4 Last line of a non-empty file must end with a line break . . . . . . . . . . . . . . . . 151
Index 168
Bibliography 176
Restrictions must therefore be set on the use of C language in order to identify the
various risky or non-portable constructions and to limit or even prohibit their use.
The restrictions defined in this guide are intended to encourage the production of
more secure, safer, more robust software, and also to encourage their portability
from one system to another, whether PC type or embedded.
This guide defines a set of rules, recommendations and good practices dedicated to secure C lan-
guage development. In this document, we currently restrict ourselves to the 2 standards C90 2 and
C99, which are the most widely used.
When a rule is directly associated with one specific standard, this is clearly indicated to avoid any
confusion. If not specified, both standards are concerned.
Rule
A rule must always be respected; no exceptions are tolerated.
Recommendation
A recommendation should be respected except in certain exceptional cases, which
implies a clear and precise justification from the developer. Recommendations are
abbreviated to “RECO”.
This guide also contains good practices. These are often somewhat more subjective points like
coding conventions, such as the indentation of the code, for example.
n increasing the security, quality and reliability of the source code produced, by identifying bad
or dangerous programming practices;
n facilitating the analysis of the source code during peer review or using static analysis tools;
The idea of this guide is not to reinvent the wheel, but rather to use existing documents (method-
ological guides, language standard references, etc.) to extract, modify and specify a set of recom-
mendations for the secure development of the C language. The reference documents used are as
follows:
n MISRA-C: 2012 Guidelines for the use of the C language in critical systems [Misra2012],
n C ANSI 90 [AnsiC90],
n C ANSI 99 [AnsiC99],
Each developer has his or hers own programming habits, code layout and variable naming. How-
ever, when producing software, these different programming habits between developers result in
a heterogeneous set of source files, which are more difficult to audit and maintain.
RULE
RULE — Application of clear and explicit coding conventions
1 Coding conventions must be defined and documented at the software project level.
These conventions must define at least the following points: encoding of source files,
code layout and indentation, standard types to be used, naming (libraries, files, func-
tions, types, variables, etc.), documentation format.
These conventions must be followed by each developer.
This rule regarding development conventions is certainly obvious and the aim here is not to impose,
for example, a choice of variable naming (such as snake-case versus camel-case), but to ensure that
a choice has indeed been made at the beginning of the development project and that it is explicit.
Appendix E provides examples of coding conventions that can be used or adapted as required.
[Misra2012] Dir. 4.5 Identifiers in the same name space with overlapping visibility should be typo-
graphically unambiguous.
[Cert] Rec. DCL02-C Use visually distinct identifiers.
[IsoSecu] Using Identifiers that are reserved for the implementation [resident].
Undefined behaviour
An undefined behaviour is a behaviour for which nothing is imposed by the C stan-
dard, and which follows an error in the construction of the program, a non-portable
construction or an incorrect use of data. An example is a signed integer overflow.
Unspecified behaviour
An unspecified behaviour is a behaviour for which the C standard provides at least
two alternative behaviours that are accepted, but none of which are imposed. An
example is the order of evaluation of the operations # and ## during the substitution
of a macro.
Information
The exhaustive list of all the unspecified behaviours and all the undefined behaviours
is available in appendices G and J of standards C90 [AnsiC90] and C99 [AnsiC99].
This guide only considers a C coding environment compliant with the C90 or C99 standards.
Information
Concurrent programming is not covered in this version of the guide, but will be cov-
ered in a later version.
RULE
RULE — Only C coding in accordance with the standard is authorised
2 No violation of C constraints and syntax as defined in the C90 or C99 standards is
authorised.
References
[Misra2012] Rule 1.1 The program shall contain no violations of the standard C syntax and con-
straints and shall not exceed the implementation translation limits.
[Misra2012] Rule 1.2 Language extensions should not be used.
[Misra2012] Rule 1.3 There shall be no occurrence of undefined or critical unspecified behaviour.
[Cwe] CWE-710 Improper Adherence to coding standard.
If the inclusion of header files is not minimised, this generates unnecessary dependencies, increases
compilation time, and makes the subsequent analysis of the code more complex (whether manual
or tool-based). In order to reduce dependencies and unnecessary propagation of declarations,
header file inclusions should be made in a “.c” file and not in a “.h” header file. However, in some
cases, such as typical type definition, the inclusion of header files from the standard library (such
as stddef.h and stdint.h) in another header file is justifiable.
RECO
RECOMMENDATION — Limit and justify header file inclusions in another
3
header file
Header files should be included as needed during development and not “automatically” by the
developer.
RULE
RULE — Only the necessary header files should be included
4
In addition, the header file inclusion mechanism can result in multiple inclusions of the same
header file, making proofreading of the code difficult at best. Defining a specific symbol for each
header file using the preprocessor directive (#define) and checking that this symbol has not already
been defined (#ifndef) avoids repeated inclusion of a header file. This is referred to as a multiple
include guard macro. Be sure to define a unique symbol for each file. The name of this symbol can
be constructed by taking the file name and substituting the “.” with a “_”.
RULE
RULE — Use multiple include guard macros for a file
5 A guard macro against multiple inclusions of a file should be used to prevent the
content of a header file from being included more than once:
// start of header file
Warning
The use of the #pragma once directive is widespread but is not standardized. This
solution is therefore not recognised in this guide, although it is supported by most
compilers. Its use can be problematic since this directive is specific to each com-
piler (especially when managing header files that are duplicated in multiple physical
sources or mount points).
Finally, for reasons of readability, the location of header file inclusions must comply with certain
specific rules.
RULE
RULE — Header file inclusions are grouped at the beginning of the file
6 All header file inclusions must be grouped at the beginning of the file or just after
preprocessor comments or directives, but always before the definition of global vari-
ables or functions.
RECO
RECOMMENDATION — System header file inclusions are made before user
7
header file inclusions
GOOD GOOD PRACTICE — Use alphabetical order in the inclusion of each type of
PRACTICE header files
8 To avoid any redundancy in system or user header file inclusions, the developer can
use the alphabetical order, which offers a deterministic inclusion order and facilitates
the code review.
Information
Although these last three rules, recommendations and good practices address a prob-
lem of readability and maintainability, and not directly a security problem in the
strict sense of the term, these first two aspects remain essential for any type of devel-
opment.
When the inclusion of a header file is omitted, the compiler may provide a warning about the use
of an implicitly declared function.
Information
Implicit function declarations are detected with GCC and CLANG using the option
-Wimplicit-function-declaration.
/* file.c */
# include <string.h>
# include <stdint.h>
# include <stdlib.h>
# include "header.h"
# define BUFFER_LEN 8U
Good example
The example below includes in the header file only the necessary definitions, and an
include guard is present. Be careful however, when using the include guard, not to
use an already reserved identifier as a name in the macro, which is a classic error
when using the include guard.
/* header .h */
# ifndef HEADER_H /* include guard to avoid ultiple inclusion */
# define HEADER_H
# endif /* HEADER_H */
/* file.c */
# include <string.h>
# include <stdint.h>
# include <stdlib.h>
# include "header.h"
# define BUFFER_LEN 8U
if (NULL != val) {
memcpy(buffer , val , min(BUFFER_LEN , length));
...
}
}
4.1.1 References
If the purpose of including one module within another is to take advantage of the compiler’s inter-
procedural optimizations (inlining, constant propagation, etc.), it is preferable to rely on Link Time
Optimization (LTO), for instance with GCC by using the -flto flag when compiling and at link time:
gcc -o binary -flto file1.c file2.c.
RULE
RULE — Do not include a source file in another source file
9 Only the inclusion of header files is authorised in a source file.
Bad example
In the following example, a source file inclusion is performed, which is prohibited.
/* file1 .c */
# include <stdint.h>
/* file2 .c */
# include "file1.c" /* prohibited */
void bar() {
foo(MAGIC_VALUE);
}
Good example
The example below correctly breaks down the code into different modules.
/* header .h */
# ifndef HEADER_H
# define HEADER_H
#endif
/* file2.c */
# include <stdint.h>
# include "header.h"
void bar() {
foo(MAGIC_VALUE);
}
When an operating system-specific path separator is used, this prevents the portability of the source
code. When an #include directive includes a path to the header file to be included, the separating
character for the path components must be the slash “/”, not the backslash “\”, to ensure portability
of the source. In addition, the character “\”, as well as the following characters or sequences of
characters: ', ", /* and // located between opening and closing chevrons (< and >) or between
double quotes (i.e. ") lead to undefined behaviour.
RULE
RULE — File paths must be portable and case sensitive
10 File paths, whether for an #include inclusion directive or not, must be portable
while respecting the case of directory names.
Bad example
In the following example, portability is not assured and leads to undefined behaviour.
# include <sys\stat.h>
# include "Module_A\Sub_Module_A\Header.h"
Good example
The example below uses a correct format for including header files.
# include <sys/stat.h>
# include "module_a/sub_module_a/header.h"
In addition, certain specific rules must be respected in the header file name to avoid undefined
behaviour.
4.3.1 References
[Misra2012] Rule 20.2: The ”,” or ”\” characters and the ”/*” and ”//” character sequences shall not
occur in a header file name.
[Misra2012] Rule 20.3: The #include directive shall be followed by either a <filename> or
"filename" sequence.
[AnsiC99] Sections 6.4.7 and 6.10.2.
RECO
RECOMMENDATION — Preprocessor blocks must be commented on
12 Preprocessor block directives must be commented on in order to clarify the cases
being dealt with and, in the case of “intermediate” and “closing” directives, these
must also be associated with the corresponding “opening” directive by means of a
comment.
For reasons of readability, double negation in preprocessor directive expressions should be avoided,
typically by using #ifndef and a “non-mode” (i.e. a mode defined via the negation of another mode
such as NDEBUG).
Finally, it is essential that all the directives associated with a preprocessor block (i.e. “opening”,
“intermediate” and “closing” directives) are present in the same file. Furthermore, it should only
be possible to evaluate the control conditions used in these directives to 1 or 0.
RULE
RULE — Definition of a preprocessor block in a single file
14 For a preprocessor block, all associated directives must be found in the same file.
Bad example
The following code should be modified to add comments and not use double nega-
tion for the #else and #endif directives.
/* file1.c */
# ifdef A /* no # endif */
# include "log.h"
/* log.h */
# ifndef LOG_H
# define LOG_H
typedef enum {
DEBUG = 0,
WARN ,
INFO ,
ERROR ,
FATAL
} LogLevel_T;
Good example
In the following example, the directives are correctly commented on and the associ-
ated directives are in the same file.
/* file1.c */
# ifdef A
# include "log.h"
# endif
/* log.h */
# ifndef LOG_H
# define LOG_H
typedef enum {
DEBUG = 0,
WARN ,
INFO ,
4.4.1 References
[Misra2012] Rule 20.8 The controlling expression of a #if or #elif preprocessing directive shall eval-
uate to 0 or 1.
[Misra2012] Rule 20.9 All identifiers used in the controlling expression of #if or #elif preprocessing
directives shall be #defined before evaluation.
[Misra2012] Rule 20.14 All #else, #elif and #endif preprocessor directives shall reside in the same
file as the #if, #ifdef or #ifndef directive to which they are related.
RULE
RULE — Do not use more than one of the preprocessor operators # and
16
## in the same expression
It is also important, with these two operators, to have a good understanding of how it works, i.e.
the steps resulting from the macro replacement.
RULE
RULE — Understand the macro replacement when using the preprocessor
17
operators # and ##
int main(void)
{
MYPRINT(TWO); /* prints "TWO" */
return 1;
}
Good example
# include <stdio.h>
...
# define MYPRINT2(s) PRINT(s) /* Additional indirection to expand
"TWO" */
# define PRINT(s) printf (#s)
# define TWO 2
int main(void)
{
MYPRINT2(TWO); /* prints "2" */
return 1;
}
4.5.1 References
[Misra2012] Rule 20.10 The # and ## preprocessor operators should not be used.
[Misra2012] Rule 20.11 A macro parameter immediately following a # operator shall not immedi-
ately be followed by a ## operator.
[Misra2012] Rule 20.12 A macro operator used as an operand to the # or ## operators which is
itself subject to further macro replacement, shall only be used as an operand to these operators.
[Cert] PRE05-C Understand macro replacement when concatenating tokens or performing stringi-
fication.
[AnsiC90] Section 6.8.3.
[AnsiC99] Section 6.10.3.
Furthermore, when a macro is not named in upper case, there is a risk that the name corresponds
to a real function name or even a reserved word in the C language. This can therefore lead to the
substitution of a function call with the code replaced by the preprocessor, or even to undefined
behaviour.
Bad example
In the following example, the macro naming rule is not followed.
# define cte 0x7EU /* lower case */
# define _my_squared(a) ((a)*(a)) /* lower case and starts with _ */
Good example
The following example shows suitable naming of the preprocessor macros.
# define CTE 0x7EU
# define MY_SQUARED(a) ((a)*(a))
4.6.1 References
[Misra2012] Rule 5.4 Macro identifiers shall be distinct.
[Misra2012] Rule 5.5 Identifiers shall be distinct from macro names.
[Misra2012] Rule 21.1: A #define or #undef shall not be used on a reserved identifier or reserved
macro name.
[Misra2012] Rule 21.2: A reserved identifier or macro name shall not be declared.
[Misra2012] Rule 20.4: A macro shall not be defined with the same name as a keyword.
[IsoSecu] Using Identifiers that are reserved for the implementation [resident].
[Cert] Rule DCL37-C Do not declare or define a reserved identifier.
[Misra2012] Dir. 4.5 Identifiers in the same name space with overlapping visibility should be typo-
graphically unambiguous.
[Cert] Rec. DCL02-C Use visually distinct identifiers.
RULE
RULE — Do not end a macro with a semicolon
19 The final semicolon should be omitted at the end of the definition of a macro.
on expansion, we have:
# define SQUARED(n) (n) = (n) * (n);
...
if (x >= 0)
x= x * x;
; /* empty statement */
else /* parsing error before the else */
x = -x;
...
Good example
The macro is corrected:
# define SQUARED(n) (n) = (n) * (n)
/* ... */
if (x >= 0)
{
SQUARED(x);
}
else
{
x = -x;
}
on expansion, we have:
#define SQUARED(n) (n) = (n) * (n)
/* ... */
if (x >= 0)
{
x = x * x;
}
else
{
x = -x;
}
4.7.1 References
[Cert] Rec. PRE11-C Do not conclude macro definitions with a semicolon.
The use of a static inline function to replace these “function type” macros prevents errors in
the order of operator evaluation when inlining macros, and makes the code easier to read.
Information
It is important to note that unused static inline functions may cause warnings to
be issued on compilation for some compiler versions. In particular, some versions of
the CLANG compiler issue warnings in this case, unlike the GCC compiler. This differ-
ence in behaviour between compilers occurs when the -Wunused-function option is
enabled at compile-time, either explicitly or via other options such as -Wall. When
the associated code really cannot be deleted (in a library for example), the developer
may have to use compiler extensions to silence these warnings, but these additions
must be clearly commented on and justified.
RECO
RECOMMENDATION — Use static inline functions instead of
20
multi-statement macros
In addition to the above recommendations and rules, it is important to add that macros whose
replacement defines functions in the code should not be used. The associated risk of error is too
great and the readability of the code can suffer from this kind of practice.
RULE
RULE — The replacement of a developer-defined macro must not create a
21
function
4.8.1 References
[Misra2012] Dir. 4.9: A function should be used in preference to a function-like macro where they
are interchangeable.
[Cert] Rec. PRE00-C Prefer inline or static functions to function-like macros.
RULE
RULE — Macros containing multiple statements must use a
22
do { ... } while(0) loop for their definition
Bad example
The macro does not implement the rule.
# define HALF_SUM(a,b,c,d) \
(a) = ((c) + (d)) / 2; \
(b) = ((c) - (d)) / 2
/* leads to a different behaviour to the one required with a call in a conditional
statement without braces */
if(c > d)
HALF_SUM(a, b, c, d);
else
/* ... */
the replacement of the macro in the same conditional is always problematic because
of the lack of braces in the conditional and the “;” following the call of the macro:
if(c>d)
{ (a)=((c) + (d)) / 2; (b) = ((c) - (d)) / 2 };
else
/* ... */
Good example
In the following example the macro is correctly defined using a do-while(0) loop struc-
ture.
# define HALF_SUM(a, b, c, d) \
do { \
(a) = ((c) + (d)) / 2; \
(b) = ((c) - (d)) / 2; \
} while (0)
4.9.1 References
[Cert] Rec. PRE10-C Wrap multistatement macros in a do-while loop.
RULE
RULE — Mandatory parentheses around the parameters used in the body
23
of a macro
The parameters of a macro must always be enclosed in parentheses when used, in
order to preserve the desired order of evaluation of the expressions.
In general, it is best to avoid arguments of a macro resulting in an operation in the broadest sense.
Apart from the side effects, even if the operation performed as an argument is constant for a given
input, the performance of the code is not optimal.
RECO
RECOMMENDATION — Arguments of a macro carrying out an operation
24
should be avoided
Moreover, if the operation carried out by the arguments of a macro leads to a side effect in the
sense of compilation, this can also lead to unexpected behaviors such as multiple evaluations of
the arguments of the macro or even to no evaluation at all.
RULE
RULE — Arguments in a macro must not contain side effects
25 Macro arguments with side effects can lead to unwanted multiple evaluations.
Finally, the use of preprocessor directives (#define, #ifdef ...) in macro arguments leads to unde-
fined behaviour and is therefore to be avoided.
RULE
RULE — Do not use preprocessor directives in macro arguments
26
Bad example
In the following example, the result will not be the one expected upon execution.
# define ABS(x) (x >= 0 ? x : -x)
a = c + ABS(a - b) + d;
/* result : a = c + (a - b >= 0 ? a - b : -a -b) + d */
m=ABS(n++);
/* additional increment of n: m = ((n++ < 0) ? - n++ : n++) */
Good example
The following code defines a macro with parentheses correctly placed around its
argument.
# define ABS(x) (((x) >= 0) ? (x) : -(x))
a = c + ABS(a - b) + d;
p=n++;
m=ABS(p);
4.10.1 References
[Misra2012] Rule 20.7: Expressions resulting from the expansion of macro parameters shall be
enclosed in parentheses.
[Cert] Rec. PRE01-C Use parenthesis within macros around parameters names.
[Cert] Rec. PRE02-C Macro replacement lists should be parenthesized.
[Cert] Rule EXP30-C Do not depend on the order of evaluation for side effects.
[Cert] Rule. PRE31-C Avoid side effects in arguments to unsafe macros.
[Cert] Rule. PRE32-C Do not use preprocessor directives in invocations of function-like macros.
[Cert] Rec. PRE12-C Do not define unsafe macros.
The use of #undef may result from the risk of a clash in the name chosen for a preprocessor symbol.
The symbol name must then be changed to prevent this clash.
RULE
RULE — The #undef directive should not be used
27
4.11.1 References
[Misra2012] Rule 20.5 #undef should not be used.
In addition, to avoid confusion with a trigraph, all comments, strings and other literals should not
contain two successive question marks.
RECO
RECOMMENDATION — Successive question marks should not be used
29 This rule applies to all parts of the code, but also to comments.
Information
The -Wtrigraphs option issues an alert when a trigraph is detected.
Information
By default, trigraphs are disabled in GCC.
4.12.1 References
[Misra2012] Rule 4.2 Trigraphs should not be used.
[Cert] Rec. PRE07-C Avoid using repeated question marks.
Warning
In this guide and a fortiori this chapter, two compilers are frequently used for illustra-
tion purposes: GCC [GccRef] and CLANG [ClangRef]. This choice is largely motivated
by the popularity of these compilers, which on top of that are open source. It doesn’t
mean in any way that this guide only recommends using one of these two compil-
ers. Any alternative may be proposed but the developper shall transpose the various
options presented in this guide himself.
RULE
RULE — Precisely define compilation options
30 Options used for compiling must be precisely defined for the whole software sources.
These options should in particular accurately establish:
n the C standard version in use (for instance C99 or C90);
Moreover, any developer enabling compiler or linker options must be fully aware of the conse-
quences regarding security of the generated executable or library.
Warning
In particular, the use of compilation options such as -fno-strict-overflow,
-fwrapv, -fwrapv-pointer, -fno-delete-null-pointer-checks or
-fno-strict-aliasing is most of the time indicative of a risky usage of the C
language.
Using a build automation software like make, CMake or Meson facilitates management of compila-
tion options. The latter may be defined globally and applied to all source files to compile.
5.1.1 References
[Misra2012] Subsection 4.2. Understanding the compiler.
[Cert] MSC06-C Beware of compiler optimizations.
[Cert] PRE13-C Use the standard predefined macros to test for versions and features.
[Cwe] CWE-14 Compiler Removal of Code to Clear Buffers.
RULE
RULE — Compile the code without any error nor warning while enabling
33
strict compilation options
High warning and error levels of the compiler and the linker must be enabled to
ensure, as much as possible, the absence of potential issues related to incorrect use
of the programming language.
All warnings and all errors reported by the compiler and the linker must be dealt
with. It is incidentally very much advised, if using GCC or CLANG, to use the -Werror
option in order to turn any warning into a compilation error, hence not running the
The relevance and accuracy of warnings emitted by the compiler directly depends on the precision
of analyzes it conducts, which in turn rely on the various optimizations the compiler is able to
perform. Specifying a reasonably high optimization level is thus beneficial.
RULE
RULE — Enable a reasonably high optimization level
34 For GCC and CLANG, the optimization level must be at least -O1, and ideally -O2 or
-Os.
Warning
It is the responsibility of the developer to make sure that a high optimization level
does not suppress defensive code or manually added software countermeasures.
Information
For example, the minimal command line for compiling with GCC or CLANG is:
gcc/clang -O1 -Wall a -Wextra b -Wpedantic c -Werror -std=c99/c90 d file.c
-o file.exe
RECO
RECOMMENDATION — Use the strictest compilation options
35 If a compilation option proves to be too strict for a given development and a choice
is made to disable it, a justification shall be provided to explain it.
Information
Appendix B.2 draws up a non-exhaustive list of extra warnings for GCC and CLANG,
which can serve as a starting point to the developer.
In order to suppress errors and warnings, the first thing to do is of course to fix the source code,
while imperatively commenting on any resulting code edit. However, if it appears to be a false
positive, several methods exist. Firstly, the complexity of a code snippet may occasionally suffice
to mislead the compiler analysis, and it is then beneficial in general to simply rewrite this hunk
in a more intelligible form, while obviously making sure it is semantically equivalent. Then, if it
turns out that a warning cannot be eliminated by fixing the source code, and if the compiler allows
for it (via a #pragma directive for example), this warning may be disabled locally.
a. Enables all the warnings about risky language constructions that are easy to avoid. See the GCC and CLANG compilers manuals
for a complete listing.
b. Enables some extra warning flags that are not enabled by -Wall. See the GCC and CLANG compilers manuals for a complete
listing.
c. Enables all the warnings demanded by the C standard; disables all compiler extensions, including the ones that do not conflict
with the standard.
d. Specifies the C standard used by the compiler. See appendix B.1.
In case the developer opts for warning suppression, a clear justification needs to be provided with
a comment:
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused -variable"
/* code */
/* some variables are not used in the new algorithm anymore but are kept for API compatibility */
/* warnings about such unused variables are thus disabled in the code following the pragma */
# pragma GCC diagnostic pop
5.2.1 References
[Misra2012] Dir. 2.1: All sources files shall compile without any compilation errors.
[Misra2012] Dir. 4.1: Run-time failures shall be minimized.
[Cert] MSC00-C: Compile cleanly at high warning levels.
[Cwe] CWE-563 Unused variable.
[Cwe] CWE-570 Expression is always false.
[Cwe] CWE-571 Expression is always true.
RULE
RULE — Make use of security features offered by compilers
36 Developers must, as much as possible, take advantage of compilation options that
allow for improving the security of the final software product.
Warning
Throughout this section, when GCC or CLANG options are given as examples, it is
necessary to keep in mind that:
n these options apply to GCC 11 and CLANG 13 respectively;
n the accuracy of warnings emitted by the compiler may depend upon the selected
optimization level;
RULE
RULE — Enable warnings that focus on detecting security bugs and deal
37
with any reported issue
For instance, the -Wformat=2 a GCC and CLANG compilation option must be enabled
and any reported issue needs to be methodically dealt with.
GCC options -Wformat-overflow=2 and -Wformat-truncation=2 may also be used.
Information
The GCC and CLANG compilers nowadays embed static source code analyzers capable
of running deeper and more precise — but more expensive — analyses on programs
in order to detect more programming errors, especially potential security vulnerabil-
ities.
Even though these embedded analyzers are still rather basic and in the end yet quite
experimental compared to standalone tools that are dedicated to static source code
analysis, it may be convenient and interesting to use them. The interested developer
may refer to the documentation [GccRef] of GCC option -fanalyzer as well as the
Clang Static Analyzer section from the CLANG documentation [ClangRef].
RULE
RULE — Enable the use of hardened variants of unsafe functions
38 For instance, when using GCC to compile a program intended for a GNU/Linux system
running the glibc, the _FORTIFY_SOURCE macro must be defined. The optimization
level must be greater or equal to -O1 for the added checks to be effective.
Run-time checks terminate the program as soon as an overflow is detected.
a. This option adds additional compile-time checks on format strings, as well as on function calls that take them as arguments.
RULE
RULE — Enable compiler warnings related to the use of uninitialized vari-
39
ables
In particular, options -Wuninitialized a , -Winit-self and
b
-Wmaybe-uninitialized must be enabled when GCC is used.
As for CLANG, options -Wuninitialized c and -Wconditional-uninitialized must
be enabled.
Warning
It should be noted that these warnings do not detect all instances of uninitialized
automatic variable uses, especially when such variables are passed to other functions
by reference.
Furthermore, some compilers support automatic initialization of such variables. In practice, this
forced initialization can use either the value zero or another particular value, called pattern. Au-
tomatic initialization to zero should be chosen for release builds (cf. next section 5.4) because it
usually limits the exploitability of this type of bug. In contrast, during development, testing and
debugging phases, pattern initialization is preferable since it is more likely to uncover certain bugs.
Selecting the right pattern is then essential: for example, for pointer-type variables, it may be a
non-canonical address so that any memory access through an uninitialized pointer systematically
faults.
RULE
RULE — Enable forced initialization of automatic variables by the compiler
40 CLANG supports automatic initialization with the two aforementioned approaches:
n -ftrivial-auto-var-init=pattern for development, tests and debugging;
3. An automatic variable is a variable defined within a function, without the static storage class specifier. Its storage is allocated
and deallocated automatically on the call stack.
a. Automatically enabled by -Wall
b. Automatically enabled by -Wall
c. Automatically enabled by -Wall
RECO
RECOMMENDATION — Enable compiler options that allow for detecting
41
signed integer overflows
In particular, GCC and CLANG both support option -ftrapv, which makes the com-
piler instrument the source code in such a way as to emit a trap during program
execution for any signed integer overflow on addition, subtraction or multiplication
operations.
Warning
Even though unsigned integer overflows are actually a well-defined behavior of the
C language, they are not any less dangerous and may just as well lead to the intro-
duction of bugs and vulnerabilities in a piece of software. The developer should thus
remain particularly careful when performing operations prone to overflows, even
with unsigned operands.
Information
GCC and CLANG support many other options that are useful for detecting integer
overflows, but they are part of the UBSan a sanitizer, the use of which as well as of
other sanitizers is not addressed by this guide.
RULE
RULE — Do not use executable stack
42 In particular, GCC nested functions should not be used. Besides, for software in-
tended for GNU/Linux or FreeBSD environments:
n option -z execstack of BFD, gold and lld linkers should not be used;
Stack buffer overflows certainly rank among the oldest and most common memory corruptions.
Positioning guard variables with a random value, usually called canaries 6 , makes it possible for
example to detect some linear overflow attempts that try to overwrite the value of a return address
saved on the stack. If need be, program execution is automatically terminated.
RULE
RULE — Enable stack canaries
43 With GCC and CLANG, compilation option -fstack-protector-strong must be en-
abled.
Section 19.2 also tackles manual implementation of a canary mechanism.
Depending on hardware architectures, platforms and compilers, it is also possible to use a guard
variable whose value is different for each thread within the same process. This allows for reducing
the severity of a potential leak of the canary value.
RECO
RECOMMENDATION — Use per-thread canaries
44 In particular, for x86 architectures, GCC and CLANG support option
-mstack-protector-guard=tls, relying on glibc Thread Local Storage.
4. More precisely, it is the use of closures, implemented by GCC by means of such nested functions and of trampolines located on
the stack, that is problematic.
5. It is a language extension anyway, offered by a compiler, yet, as a reminder, only C code compliant with the C90 or C99 standards
is allowed by the present guide.
6. Alternatively known as cookies
To allow for relocation of shared libraries and executables, the dynamic loader must be able to mod-
ify some of their sections. If the corresponding memory mappings consequently remain writable
during program execution, they may prove useful to an attacker trying to exploit a software vul-
nerability. However, it is possible at link-time to mark said sections so that the dynamic loader
makes the underlying memory mappings read-only as soon as possible. This is referred to as relro
or partial relro mode.
RULE
RULE — Use relro mode of linkers
46 For instance, with BFD and gold linkers, option -z relro must be used. lld enforces
relro mode by default thus no extra option is required.
Nevertheless, since function symbols resolution usually happens in the course of program execu-
tion (lazy binding), a number of sections within shared libraries or executables remain writable
regardless of relro mode. Is is then possible to force the dynamic loader to resolve all symbols
when the program is started 7 so that it can go on to make the underlying memory mappings read-
only. This is referred to as full relro or BIND_NOW mode.
RECO
RECOMMENDATION — Do not use lazy binding
47 For instance, option -z now of BFD, gold and lld causes these linkers to mark gener-
ated executables and libraries so as to tell the dynamic loader it needs to resolve all
symbols at program startup.
Information
When relro mode is used and lazy binding is disabled, some linkers reorder sections
inside generated binaries in order to prevent overflows of data located in one section
from overwriting the contents of sensitive sections.
This is notably the case with BFD, gold and lld.
7. Disabling lazy binding might thus slow down a large software’s startup. It should also be noted that this becomes of little
importance in practice for a daemon.
Debug mode enables the developer to better understand how a program works and fix reported
errors whereas release mode is required for delivery for performance or program size reasons. For
instance, in debug mode, some compilers make sure that all variables are automatically initialized
to 0, while in release mode this only applies to global variables, as per the standard.
Information
If NDEBUG is defined as a macro name at the point in the source file where the stan-
dard library header assert.h is included, then the assert macro is redefined to be
disabled.
RULE
RULE — All production-ready code must be compiled in release mode
49 Compiling in release mode is mandatory when putting software into production.
This can seem redundant with rules and recommandations from sections 5.1 and 5.2 but it is def-
initely a rather common mistake in software engineering. In addition to different behaviors re-
garding memory management and code optimizations, debug mode may even sometimes increase
software attack surface. It is thus very important that the developer make use of these modes in
full knowledge of the cause.
RECO
RECOMMENDATION — Only multiple declarations of simple variables of the
51
same type are authorised
In order to also avoid errors in the initialisation of variables, initialisations coupled with a multiple
declaration are to be prohibited. Indeed, in the case of a single initialisation at the end of the
multiple declaration, only the last variable declared is actually initialised.
RULE
RULE — Do not make multiple variable declarations associated with an
52
initialisation
Initialisations associated (i.e. consecutive and in the same statement) with a multiple
declaration are prohibited.
Bad example
In the code below, several variables are declared in the same statement, but only the
last variable is initialised.
uint32_t abs , ord = 0; /* caution , the variable abs is not set to zero here ! */
uint32_t a, *b; /* to be prohibited : mix of simple variable and pointer
declaration */
struct blob_t g, h[35]; /* to be prohibited : mix of simple variable , pointer and
array declaration */
6.1.1 References
[Cert] Rec. DCL04-C Do not declare more than one variable per declaration.
RECO
RECOMMENDATION — Group variable declarations at the beginning of the
53
block in which they are used
For reasons of readability and to avoid redefinition, variable declarations have to
be grouped at the beginning of the file, of the function or of the block statement
according to their scope.
Information
This recommendation is not strictly related to security but has an impact on the
readability, portability and/or maintainability of the code, and concerns all types of
development.
The -Wdeclaration-after-statement GCC and CLANG compiler option can help
with enforcing this recommendation.
A very common practice for loop counters is to declare them directly in the associated loop. This
“on the fly” declaration is accepted, but a special care must be taken to ensure that the associated
variable does not mask one of the other variables used in the body of the loop.
Bad example
In the following code, the variables are declared “on the fly” and not in a grouped
and structured manner. This type of practice makes it more complex to identify all
the variables declared, thus increasing the risk of variable shadowing.
# include <stdint.h>
uint8_t glob_var; /* global variable */
uint8_t fonc(void)
{
Good example
The variables are declared in a grouped and structured way at the beginning of the
blocks, which makes them easier to read.
# include <stdint.h>
uint8_t fonc(void)
{
uint8_t var1; /* local variables declared together at the beginning of the
function */
uint8_t var2;
if (glob_var >= 0)
{
/* ... */
}
else
{
var1 = glob_var;
}
/* ... */
}
void main(void)
{
uint8_t x = fonc ();
/* ... */
}
RULE
RULE — Do not use hard-coded values
54 The values used in the code must be declared as constants.
The constant declaration rule is also to be applied for all types of values appearing several times in
the source code. Therefore, if a character or string is repeated several times, it must also be defined
using a global const variable or, failing that, using a preprocessor macro.
RULE
RULE — Declare constants in upper case
56
Constants that do not require type checking are declared with the keyword #define.
RULE
RULE — Constants that do not require type checking are declared with the
57
#define preprocessing directive
RULE
RULE — Constants requiring explicit type checking must be declared with
58
the keyword const
RULE
RULE — Constant values must be associated with a suffix depending on
59
the type
To avoid misinterpretation, constant values must use a suffix based on their type:
n the suffix U must be used for all unsigned integer type constants;
n to indicate a long (or long long for C99) type constant, the suffix L (or LL re-
spectively) must be used instead of l (or ll respectively) in order to avoid any
ambiguity with the number 1;
n floating values are by default considered as double; use the suffix f for the float
type (or d for the double type respectively).
Warning
By default, integer values are considered int type and floating values are considered
double type.
To avoid any confusion, octal constants are to be prohibited. Some cases can be tolerated such as
UNIX file modes, but they will be systematically identified and commented on.
RECO
RECOMMENDATION — Prohibit octal constants
61 Do not use octal constants or escape sequences.
Bad example
The following example does not centralise the definition of the constants. Certain
constants have not been declared. There is also an absence of specific naming for
the constants.
# define octal_const 075 /* octal base numerical constant and name in lower case */
Good example
The following code applies the different rules and recommendations for the declara-
tion of constants.
const uint32_t INIT_VALUE = 0x1294U; /* declared constant and with the necessary
type check */
# define BUFFER_SIZE 0x82U /* declared constant with type check not necessary */
const int64_t B = 0L; /* correction of the suffix and specific naming of the
constant */
uint8_t buffer[BUFFER_SIZE ];
uint16_t i;
6.3.1 References
[Misra2012] Rule 11.8 A cast shall not remove a const or volatile qualification from the type pointed
to by a pointer.
In addition, the use of global variables can quickly lead to problems of concurrency in the case of a
multi-tasking application. For each of the global variables, the developer must study the possibility
of limiting the scope of the variable systematically.
RULE
RULE — Limit global variables to what is strictly necessary
62 Limit the use of global variables and give preference to function parameters in order
to propagate a data structure through an application.
Bad example
The following code uses a global variable. However, its use could easily be avoided.
static uint32_t g_state;
void foo(void) {
...
void bar(void) {
...
g_state = 2;
}
Good example
The following example does not use a global variable. The variable state is propa-
gated from function to function by passing it as a parameter:
void foo(uint32_t* state) {
...
(* state) = 1;
}
6.4.1 References
[Cert] Rec. DCL02-C Use visually distinct identifiers.
[Cert] Rule DCL30-C Declare objects with appropriate storage durations.
[Cert] Rec. DCL19-C Minimize the scope of variables and functions.
[Misra2012] Dir. 4.5 Identifiers in the same name space with overlapping visibility should be typo-
graphically unambiguous.
[Misra2012] Rule 8.9 An object should be defined at block scope if its identifier only appears in a
single function.
RULE
RULE — Systematically use the static specifier for declarations
63 The static storage-class specifier must be used for all global functions and variables
that are not used outside the source file in which they are defined.
6.5.1 References
[Cert] Rec. DCL15-C Declare file-scope objects or functions that do not need external linkage as
static.
[Cert] Rule MSC40-C Do not violate constraints.
[Misra2012] Rule 8.7 Functions and objects should not be defined with external linkage if they are
referenced in only one translation unit.
[Misra2012] Rule 8.8 The static storage class specifier shall be used in all declarations of objects
and functions that have internal linkage.
RULE
RULE — Only variables that can be modified outside the implementation
64
should be declared volatile
Only variables associated with input/output ports or asynchronous interrupt func-
tions should be declared as volatile to prevent optimisation or reorganisation on
compilation.
Moreover, to avoid undefined behaviour, only a pointer that is itself qualified as volatile can
access a volatile variable.
RULE
RULE — Only volatile-qualified pointers can access volatile
65
variables
6.6.1 References
[Cert] Rec. DCL17-C Beware of miscompiled volatile-qualified variables.
Information
In practice, compilers issue a warning (-Wimplicit-int) but implicitly assume that
the type is int.
RULE
RULE — No type omission is accepted when declaring a variable
66 All variables used must have been explicitly declared before use.
is also prohibited. Firstly, this type of declaration is obsolete and, secondly, it reduces the readabil-
ity of the code and therefore, potentially, the checks made at compiler level.
Bad example
The following code (C90) contains several implicit type declarations.
...
const ACONST = 42; /* prohibited : type of constant not explicitly defined (
implicite int) */
unsigned e; /* prohibited : type of e not explicitly defined ( implicit unsigned int)
*/
signed f; /* prohibited : type of constant not explicitly defined
( implicit signed int) */
...
int foo(char a, const b) /* prohibited : type of b not explicitly defined ( implicit
int) */
{
...
}
Good example
All types are now explicit.
...
const int ACONST = 42;
unsigned int e;
signed int f;
...
int foo( unsigned char a, const int b)
{
...
}
int bar( unsigned char c, const int d)
{
...
}
6.7.1 References
[Cert] Rule DCL31-C Declare identifier before using them.
[Misra2012] Rule 8.1 Types shall be explicitly specified.
Attempting to access the associated object outside of its scope will result in undefined behaviour.
It is therefore essential to fully understand the scope associated with this type of construction.
RECO
RECOMMENDATION — Limit the use of compound literals
67 Due to the risk of mishandling compound literals, their use must be limited, docu-
mented and special attention must be paid to their scope.
Bad example
# include <stdio.h>
# include <stdint.h>
# define MAX 10
struct point {
uint8_t x,y;
};
Good example
# include <stdio.h>
# include <stdint.h>
struct point {
uint8_t x,y;
};
# define MAX 10
int main(void)
{
uint8_t i;
struct point tab[MAX];
for (i = 0; i < MAX; i++){
tab[i] = (struct point){i, 2*i};
}
for (i = 0; i < MAX; i++){
printf("%d\n", tab[i].x);
}
...
}
6.8.1 References
[Cert] Rec. DCL21-C Understand the storage of compound literals.
[Cert] Rule DCL30-C Declare objects with appropriate storage durations.
[IsoSecu] Escaping of the address of an automatic object [addrescape].
6.9 Enumerations
The non-explicit value of a constant in an enumeration is 1 higher than the value of the previous
constant. If the first constant value is not explicit then it is 0. If all the values in the enumeration are
implicit, no problem arises, but if the developer makes certain values explicit, an error is possible.
It is therefore better to avoid mixing constants with explicit and implicit values. If constants of
the same enumeration have the same value, this leads to undefined behaviour. If values are made
explicit then all values of the enumeration constants must be made explicit to ensure that none of
the given values are repeated.
The constants of an enumeration are also subject to the rules of section 6.3, such as the use of
upper case for the declaration of constants.
Enumerations are not made for this purpose; it is a misuse that can make the code harder to
understand.
RULE
RULE — Do not use anonymous enumerations
69
Bad example
enum une_enum {
enum1=1,
enum2 ,
enum3 ,
enum4 =3 /* enum4 et enum3 ont la même valeur */
};
Good example
All constants have a unique value and are in upper case.
enum une_enum {
ENUM1=0,
ENUM2=1,
ENUM3=2,
ENUM4 =3
};
6.9.1 References
[Misra2012] Rule 8.12 Within an enumerator list, the value of an implicitly-specified enumeration
shall be unique.
[Cert] Rec. INT09-C Ensure enumeration constants maps to unique values.
Information
Global and static variables are automatically initialised when they are defined, but
with a default value specified by the standard. Due to the possible lack of knowledge
of these default values, it is recommended to explicitly initialise all variables.
One easy way to ensure this is to do it systematically when declaring a variable if it is declared
alone, or immediately after declaration for multiple declarations.
RECO
RECOMMENDATION — Variables should be initialised at or immediately
70
after declaration
All variables should be systematically initialised when they are declared, or immedi-
ately afterwards in the case of multiple declarations.
Information
The compiler can detect certain missing initialisations. GCC for example provides the
-Wuninitialized option. Subsection 5.3.3 gives more details and also highlights the
limits of such options. In particular, the absence of warning raised by this option is
not sufficient to guarantee that all variables are properly initialized.
Bad example
In the following example, variables are not initialized when they are used.
/* declarations in the body of a function */
uint32_t a;
uint32_t b;
uint32_t c;
Good example
In the following code, the variables are correctly initialised before being used (as
soon as they are declared here).
/* declarations in the body of the function */
uint32_t a = 0;
uint32_t b = 0;
uint32_t c = 0;
a = b + c;
6.10.1 References
RULE
RULE — Use only one initialisation syntax for structured variables
71 For the initialisation of a structured variable, only one initialisation syntax must be
chosen and used.
Bad example
int tab [10] = { 0, [4] = 3, 5, 6, [1] = 1, 2 };
struct type_t o = { .a = 10, 0, "bob" };
Good example
int tab [10] = { 0, 1, 2, 3, 5, 6, 0, 0, 0, 0 };
struct type_t o = { 10, 0, "bob" };
struct type_t p = { .a = 10, .b = 0, .c ="bob" };
This initialisation ensures that all elements/fields of the structured variable are initialised to zero.
Warning
However, the semantics of this notation should not be misunderstood:
int tab[N] = {1}; /* does not mean that all the elements are 1, but that all are at
zero and only the first element is 1 */
RULE
RULE — Structured variables must not be initialised without specifying the
72
initialisation value and each field/element of the structured variable must
be initialised
Non-scalar variables must be initialised explicitly: each element must be initialised
with a clear identification, without superfluous initialisation values. Alternatively,
the {0} initializer can be used in the declaration. Finally, arrays must have their size
explicitly set when initializing them.
Bad example
The initialisations are not precise in the following example: there are non-explicit ini-
tialisations of the elements of the structured variables and superfluous initialisation
values.
int32_t y[5] = {1, 2, 3}; /* the initialisation is misleading here - in reality ,
the last two elements are initialised at zero */
int16_t vv[5] = { [0] = -2, [1] = -9, [3] = -8, [2] = 18 }; /* source of error , the
indexes 2 and 3 are not in increasing order and 4 is forgotten */
struct person {
unsigned char name [20];
uint16_t roll;
float marks;
int grades [10];
};
Good example
Initialisations are now explicit and encompass all elements of the structured vari-
ables without superfluous initialisation values.
int32_t y[5] = { 1, 2, 3, 4, 5 }; /* full initialisation */
int16_t vv[5] = { [0] = -2, [1] = -9, [2] = 18, [3] = -8, [4] = 33 }; /* ok */
struct person {
unsigned char name [20];
uint16_t roll;
float marks;
int grades [10];
};
struct person p1 = { .name = "titi", .roll = 12, .marks = 10.0f, .note = {0}};
/* all elements are explicitly initialised */
6.11.1 References
[Misra2012] Rule 9.2 The initializer for an aggregate or union shall be enclosed in braces.
[Misra2012] Rule 9.3 Arrays shall not be partially initialized.
[Misra2012] Rule 9.4 An element of an object shall not be initialized more than once.
[Misra2012] Rule 9.5 Where designated initializers are used to initialize an array object the size of
the array shall be specified explicitly.
[Cert] Rec. ARR02-C Explicitly specify array bounds, even if implicitly defined by an array initial-
izer.
[Cwe] CWE-665 Incorrect or incomplete initialization.
Information
GCC and CLANG compilation options such as -Wunused-variable and
-Wunused-parameter make it possible to detect this kind of patterns.
RECO
RECOMMENDATION — Every declaration must be used
73 All declared identifiers must be used, whether they are variables, functions, labels,
function parameters or anything.
Warning
When developing a library, not all declared identifiers are necessarily used : functions
and variables exported by the library may obviously not be used by the library itself.
Bad example
In the following code, variables declared but not used must be deleted.
uint32_t init_list(list_t ** pp_list) {
list_t* p_list = NULL;
list_element_t* p_element = NULL;
uint32_t ui32_list_len = 0;
if (NULL == pp_list) {
return 0;
}
(* pp_list)->p_head = NULL;
(* pp_list)->p_tail = NULL;
return 1;
}
Good example
In the following example, all declared variables are used.
uint32_t init_list(list_t ** pp_list) {
if (NULL == pp_list) {
return 0;
}
if (NULL == (* pp_list)) {
return 0;
}
(* pp_list)->p_head = NULL;
(* pp_list)->p_tail = NULL;
return 1;
}
6.12.1 References
[Misra2012] Rule 2.2 There shall be no dead code.
[Misra2012] Rule 2.3 A project should not contain unused type declarations.
[Misra2012] Rule 2.4 A project should not contain unused tag declarations.
[Misra2012] Rule 2.5 A project should not contain unused macro declarations.
[Misra2012] Rule 2.6 A project should not contain unused label declarations.
[Misra2012] Rule 2.7 There should be no unused parameters in functions.
[Cert] Rec. MSC07-C Detect and remove dead code.
[Cert] Rec. MSC13-C Detect and remove unused values.
[Cert] Rec. MSC12-C Detect and remove code that has no effect or is never executed.
RULE
RULE — Use separate variables for sensitive data and non-sensitive data
74
RULE
RULE — Use different variables for sensitive data that are protected in
75
confidentiality and/or integrity than the ones used for unprotected sensitive
data
These rules are more of a principle of secure coding to avoid handling non-sensitive, encrypted
sensitive and unencrypted sensitive data in the same variable.
It goes without saying that hard-coding any sensitive information of any kind (password, login,
encryption key, etc.) is forbidden.
RULE
RULE — Never hard-code sensitive data
76
Bad example
The code below does not use a naming convention.
#define KEY_SIZE 32U
#define BUFFER_SIZE 512U
size_t key_len = 0;
size_t clear_data1_len = 0;
size_t encrypted_data2_len = 0;
uint8_t key[KEY_SIZE ];
uint8_t data1[BUFFER_SIZE ];
uint8_t data2[BUFFER_SIZE ];
uint32_t error_code = 0;
error_code = cipher_data(clear_data , clear_data_len , key , key_len ,
encrypted_data , encrypted_data_len );
Good example
In the following example, a naming convention is used so that the same variables are
not used for encrypted or unencrypted sensitive data.
#define KEY_SIZE 32U
#define BUFFER_SIZE 512U
/* conventions :
suffix s for sensitive data variables‘’
clear prefix for unencrypted data‘’
encrypted prefix for encrypted data */
size_t encrypted_key_len_s = 0;
size_t clear_data_len_s = 0;
size_t encrypted_data_len_s = 0;
uint8_t encrypted_key_s[KEY_SIZE ];
uint8_t clear_data_s[BUFFER_SIZE ];
uint8_t encrypted_data_s[BUFFER_SIZE ];
uint32_t encrypted_error_code_s = 0;
Therefore, use of the int type is risky since it is necessary to be sure of the associated size and
possible values to avoid any overflow or unexpected behaviour such as a value wrap (for unsigned
integers).
It is therefore best to avoid using this type unless the developer is certain that the associated value
range is contained within its range (e.g. in loop counters).
The type name must include its size on the target machine explicitly, like those defined in the
header file stdint.h, available in C99. Its use is to be preferred to the generic int type. In C90,
equivalent types must be defined and used. The redefinition of integer types is possible but this
redefinition must be explicit on both the associated size and sign.
RECO
RECOMMENDATION — Only integer types with an explicit size and sign
77
should be used
Furthermore, the plain char type should not be used for numeric values as its sign is not speci-
fied by the C standard and is implementation-defined. This type must be restricted to character
handling.
RULE
RULE — Only signed char and unsigned char types must be
78 used to handle numeric values
Bad example
# define MAXUINT16 65535U
int value;
char c = 35; /* sign is not specified */
Good example
# include <stdint.h> /* if C99 */
# define MAXUINT16 65535U
unsigned char c = 35U; /* sign is explicit , for numeric value manipulation */
...
uint32_t value; /* if C99 */
7.1.1 References
[Misra2012] Rule 10.1 Operands shall not be of an inappropriate essential type.
[Misra2012] Rule 10.3 The value of an expression should not be assigned to an object with a nar-
rower essential type or of a different essential type category.
[Misra2012] Rule 10.4 Both operands of an operator in which the usual arithmetic conversions are
performed shall have the same essential type category.
[Misra2012] Rule 8.1 Types shall be explicitly specified.
[Misra2012] Directive 4.6 typedef that indicate size and signedness should be used in place of the
basic numerical types.
[Cert] Rec. INT00-C Understand the data model used by your implementation(s).
[Cert] Rec. INT07-C Use only explicitly signed or unsigned char type for numeric values.
[Cert] Rule INT35-C Use correct integer precisions.
[Cert] Rec STR00-C Represent characters using an appropriate type.
[Cwe] CWE-682 Incorrect calculation.
RECO
RECOMMENDATION — Do not redefine type aliases
79
Good example
In the following example (in C90, i.e. before the introduction of stdint.h), only one
type is correctly defined from the definition of a standard type of the language.
typedef unsigned short uint16_t; /* definition of type uint16_t */
7.2.1 References
[Cert] Rec. PRE03-C Prefer typedefs to defines for encoding non-pointer types.
On the one hand, integer promotion is performed on integer values whose type is smaller than the
int type and when these integer values are subjected to an operation (binary operators, unary
operators, shifts, etc.). These integer values are then automatically and systematically converted
to int or unsigned int.
On the other hand, type balancing corresponds to the usual conversion to a common type when
operands are of different types. Finally, the last implicit conversion corresponds to the assignment
of a value in a different type.
Information
Details of integer promotion can be found in sections 6.2.1.1. and 6.3.1.1. of stan-
dards [AnsiC90] and [AnsiC99] respectively. For type balancing, the relevant sections
are 6.2.1.5 and 6.3.1.8 of standards [AnsiC90] and [AnsiC99] respectively.
The developer must make explicit the conversions implicit in the code to avoid any errors. The
classic case that is often a source of errors is an implicit conversion between signed and unsigned
types.
RULE
RULE — Explicit conversions between signed and unsigned types
81 Prohibit implicit type conversions. Use explicit conversions, particularly between
signed and unsigned types.
Bad example
signed int v1 = -1;
unsigned int v2 = 1;
if (v1 < v2)
{
/* v1 converted to unsigned int and value -1 becomes UINT_MAX , therefore the if
condition is always false */
}
Good example
signed int v1 = -1;
unsigned int v2 = 1;
if (v1 < (signed int)v2)
{
/* v2 is explicitly converted to a signed integer - the condition is true */
}
Again for the same reasons, no implicit conversion should be made between an integer type and
a floating type or from an integer type to a smaller integer type.
Bad example
In the following lines, conversions are implicit.
uint32_t u32;
int32_t s32;
uint16_t u16;
double dbl;
uint8_t idx;
s32 = 42;
u32 = s32; /* implicit conversion */
u16 = u32 + 2 * s32; /* implicit conversion to a smaller type */
dbl = u32 / u16; /* the result is 0 ( integer division ) */
s32 = dbl; /* implicit conversion float -> integer */
/* the following loop is infinite : idx being unsigned , idx >= 0 is always true
since an unsigned value cannot be negative */
for(idx = 27; idx >= 0; idx --) {
...
}
s32 = 42;
u32 = (uint32_t)s32;
u16 = (uint16_t)(( int32_t)u32 + 2 * s32);
dbl = (double)u16 / (double)u32;
Good example
int32_t s32;
uint16_t u16;
double dbl;
uint8_t idx;
s32 = 42;
u32 = (uint32_t)s32;
u16 = (uint16_t)(( int32_t)u32 + 2 * s32);
dbl = (double)u16 / (double)u32;
Information
-Wconversion and -Wsign-conversion warnings provided by GCC and CLANG can
help to detect such implicit conversions.
7.3.1 References
[Misra2012] Rule 10.1 Operands shall not be of an inappropriate essential type.
[Misra2012] Rule 10.3 The value of an expression should not be assigned to an object with a nar-
rower essential type or of a different essential type category.
[Misra2012] Rule 10.4 Both operands of an operator in which the usual arithmetic conversions are
performed shall have the same essential type category.
[Misra2012] Rule 10.5 The value of an expression should not be cast to an inappropriate essential
type.
[Misra2012] Rule 10.6 The value of a composite expression shall not be assigned to an object with
wider essential type.
In addition, type conversion from/to structured variables makes proofreading the code more com-
plex.
RECO
RECOMMENDATION — Do not use pointer type conversion on types struc-
82
tured differently
Bad example
In the following code, a structure type conversion will result in an overflow.
# define TAB_SIZE 16U
typedef struct {
int32_t magic;
} s_a;
typedef struct {
p->magic = 0xBAADCAFE;
p->s = 0xDEAD; /* overflow outside of the structure s_a */
p->x[0] = 4; /* and risk of overwritten data ( buffer overflow ) */
}
Good example
In the following code, the structure type conversion is no longer performed.
# define TAB_SIZE 16U
typedef struct {
int32_t magic;
} s_a;
typedef struct {
s_a h;
int16_t s;
uint8_t x[TAB_SIZE ];
} s_b;
7.4.1 References
[Misra2012] Rule 11.2 Conversions shall not be performed between a pointer to an incomplete
type of any other type.
[Misra2012] Rule 11.3 A cast shall not be performed between a pointer to object type and a pointer
to a different object type.
[Misra2012] Rule 11.8 A cast shall not remove a const or volatile qualification from the type pointed
to by a pointer.
[Cert] Rule EXP36-C Do not cast pointer into more strictly aligned pointer types.
[Cwe] CWE-704 Incorrect type conversion or cast.
[IsoSecu] Converting pointer values to more strictly aligned pointer types [alignconv].
The nuances between arrays and pointers are numerous and we can only draw the reader’s atten-
tion to this point and urge them to tread carefully.
The first line involves declaring N int type pointers in memory, i.e. an array of N int type pointers.
The second line declares a pointer to an array of N int type elements in memory.
The standard clarifies this point by explaining that any array type expression is converted to a
pointer type expression pointing to the first element of the array and is not an lvalue except when
Expression
An expression is not an object in memory but a piece of source code such as a+b or
&a, for example.
Lvalue
An lvalue (locator value) is an expression with a type, even incomplete, but different
from void which is associated with an address in memory.
An array is not a modifiable lvalue, which means that it cannot be assigned, incremented or modi-
fied in general.
int tab[N];
tab = 0; // error
tab --; // error
When the array expression is converted to a pointer type expression, this expression then produces
a simple value and is no longer an lvalue.
Warning
For an array tab, the tab and &tab[0] notations represent the address of the first
element of the array created in memory. The &tab notation, on the other hand, will
vary. When an array is declared statically, the array address cannot change and there
is no pointer creation as such on the array: the tab notation is similar to a label
managed by the compiler containing the address of the array.
n Therefore, if the array is declared statically (case of tab1 in the previous example),
&tab always represents the address of the first element of the array, i.e. the address
of the array.
n On the other hand, if the array is declared dynamically, the tab notation rep-
resents the pointer containing the address of the array created in memory and
therefore, &tab represents the address of the pointer to the array (case of tab2 in
the example).
The C standard allows access to the iþelement of a tab array to be represented in a variety of ways,
which can be a source of errors or confusion.
Warning
For a tab array, access to the iþelement can be written:
*(tab+i); /* usual notation 1 */
tab[i]; /* usual notation 2 */
*(i+tab); /* interchangeable array and index ! */
i[tab]; /* interchangeable array and index ! */
These notations are all recognised by the standard and are therefore correct, but this
can quickly reduce understanding of the code. Note that, even with demanding com-
pilation options, neither GCC nor CLANG will issue alerts on these types of notations,
In order to avoid any ambiguity and misunderstanding of the code and therefore potential errors,
notations tolerated by the standard and intended to invert the index and the name of an array will
not be used.
RULE
RULE — Access to the elements of an array will always be by designating
83
as the first attribute the array and as the second attribute the index of the
element concerned
Access to the iþelement of an array will always be written with the name of the array
first followed by the index of the element to be reached.
Furthermore, again for reasons of transparency, the typical notation of the arrays using square
brackets [] will be preferred.
RECO
RECOMMENDATION — Access to elements in an array should be using
84
square brackets
In the case of an array type variable, the dedicated notation (via square brackets)
must be used to avoid any ambiguity.
Bad example
for (i = 0; i< size_tab; i++) {
*(i+tab) = i; /* the square brackets are not used and the index is in the first
position */
...
}
Good example
for (i = 0; i < size_tab; i++) {
tab[i] = i;
...
}
8.1.1 References
[Cert] ARR00-C Understand how arrays work.
Information
The -Wvla option provides alerts on the use of VLAs in the code.
RULE
RULE — Do not use VLAs
85
8.2.1 References
[Misra2012] Rule 18.8 Variable-length array types shall not be used.
[Cert] ARR32-C Ensure size arguments for VLA are in a valid range.
[Cert] MEM05-C Avoid large stack allocations.
[Cwe] CWE-758 Reliance on undefined, unspecified, or Implementation-defined behavior.
[IsoSecu] Tainted, potentially mutilated, or out-of-domain integer values are used in a restricted
sink [taintsink].
RECO
RECOMMENDATION — Do not use an implicit size for arrays
86 In order to ensure that array accesses are valid, their size must be made explicit.
Bad example
In the example below, the size of the array is implicit on initialisation.
int32_t tab [] = { 1, 2, 3 }; /* array 3 elements , implicit size */
Good example
This time, the size of the arrays is explicitly specified.
int32_t tab [3] = { 1, 2, 3 }; /* array of 3 elements , explicit size , with
initialisation */
int32_t tab2 [2] = { 2, 3 }; /* array of 2 éléments , explicit size , with
initialisation */
RULE
RULE — Use unsigned integers for array sizes
87
RULE
RULE — Do not access an array element without checking the validity of
88
the used index
The validity of the used array index must be checked systematically: an array index
is valid if it is greater than or equal to zero and strictly less than the declared size of
the array. In the case of a character array, the end of string character ’\0’ must be
taken into account.
Bad example
i++;
tab[i] = i; /* no overflow check */
Good example
for (i = 0; i < size_tab; i++){ /* size_tab is the number of elements in the array
*/
tab[i] = i;
...
}
8.4.1 References
[Cert] Rec. ARR02-C Explicitly specify array bounds, even if implicitly defined by an initializer.
[Cert] Rule ARR30-C Do not form or use out-of-bounds pointers or array subscripts.
RULE
RULE — A NULL pointer must not be dereferenced
89 Before dereferencing a pointer, the developer must ensure that it is not NULL.
Bad example
In the following function, the pointer passed as a parameter is used without being
checked.
void function(const unsigned char *input)
{
size_t size = strlen(input); /* the pointer may be NULL */
...
}
Good example
Handling of the error relating to the NULL pointer has been added.
void function(const unsigned char *input)
{
if (NULL == input)
{
/* handling of the NULL pointer case */
}
else
{
size_t size = strlen(input);
/* ... */
}
}
Dangling pointer
A dangling pointer is a pointer that contains the memory address of an element that
has been freed.
In case of bugs and incorrect use of the deallocated pointer, the memory may be corrupted. Once
freed, the memory may (or may not) be reused by the system. The result of the use of the memory
area (via the pointer) is then undefined and not necessarily visible, and may cause security prob-
lems (use-after-free). By assigning the pointer to NULL after deallocation, you can specify that the
pointer no longer points to a valid memory area. And in case of an accidental use of the pointer,
no memory area will be corrupted since the pointer no longer points to any valid memory area.
RULE
RULE — A pointer must be assigned to NULL after deallocation
90 A pointer must be systematically assigned to NULL following the deallocation of the
memory it points to.
Bad example
In the code below, the pointer is not set to NULL following its deallocation.
list_t *p_list = NULL;
p_list = create_list ();
...
if(p_list != NULL) {
free_list(p_list);
}
/* setting of p_list to NULL is missing */
Good example
In the following example, the pointer is correctly set to NULL following the dealloca-
tion of the area being pointed to.
list_t *p_list = NULL;
p_list = create_list ();
...
if(p_list != NULL) {
8.6.1 References
[Cert] Rule MEM30-C Do not access freed memory.
[Cert] Rec MEM01-C Store a new value in pointers immediately after free().
[Misra2012] Rule 18.6. The address of an object with automatic storage shall not be copied to
another object that persists after the first object has ceased to exist.
[Cwe] CWE-415. Double free.
[Cwe] CWE-416. Use after free.
[Cwe] CWE-672 Operation on a resource after expiration or release.
[IsoSecu] Accessing freed memory [accfree].
[IsoSecu] Freeing memory multiple times [dbfree].
Alias
Two aliases are two variables or access paths to the same memory area.
Warning
The restrict qualifier is a declaration of the developer’s intention to associate a
single pointer with a memory area, not an actual fact. In practice, nothing prevents
the code from reaching the same area via a different pointer.
The behaviour becomes undefined if objects pointed to by restrict pointers have common mem-
ory addresses. In addition, it is necessary to check for the absence of common memory addresses
on each function call with restrict type parameters, but also during the execution of said func-
tions.
Warning
Several functions in the standard library have restrict type parameters since C99
(memcpy, strcat, strcpy, etc.).
It is very easy to introduce undefined behaviour through the use of restrict, as it must be ensured
that none of the pointers concerned share a memory area, while taking into account function calls
RULE
RULE — Do not use the restrict pointer qualifier
91 The restrict qualifier must not be used directly by the developer. Only indirect use,
i.e. via the standard library function call, is tolerated, but the developer must ensure
that no undefined behaviour will result from the use of such functions.
Information
The -Wrestrict GCC option makes it possible to alert to the misuse of restrict
pointers.
Bad example
In the following example, restrict type pointers share memory areas and therefore
cause undefined behaviour.
uint16_t * restrict ptdeb;
uint16_t * restrict ptfin;
uint16_t tab [12];
unsigned char * pt1;
unsigned char * pt2;
unsigned char c_str [] = "blabla";
...
ptdeb = &tab [0];
ptfin = &tab [11];
ptdeb = ptfin; /* undefined behaviour */
...
pt1 = pt2 + 2;
memcpy(pt2 , pt1 , 3); /* undefined behaviour - restrict type memcpy parameters */
Good example
In the following example, the restrict qualifiers have been deleted and there is no
longer any undefined behaviour.
uint16_t * ptdeb; /* deletion of restrict qualifier */
uint16_t * ptfin; /* deletion of restrict qualifier */
uint16_t tab [12];
unsigned char * pt1;
unsigned char * pt2;
unsigned char c_str [] = "blabla";
...
ptdeb = &tab [0];
ptfin = &tab [11];
ptdeb = ptfin; /* ok */
...
pt1 = pt2 + 2;
memmove(pt2 , pt1 , 3); /* change of function */
8.7.1 References
[Misra2012] Rule 8.14 The restrict type shall not be used.
[IsoSecu] Passing pointers into the same objects as arguments to different restrict-qualified param-
eters [restrict].
RECO
RECOMMENDATION — The number of levels of pointer indirection should
92
be limited to two
The number of levels of indirection for a pointer should not exceed two.
Bad example
The following code shows excessive levels of indirection.
void function(int8_t *** arr_pt) /* 3 levels */
{
int8_t ***pt;
...
}
Good example
In the following example, temporary pointers are introduced to facilitate access to
the data and limit the number of nesting levels.
typedef int8_t *int8ptr_t;
void function(int8ptr_t ** arr_pt) /* reduction to two levels */
{
int8_t *pt_temp; /* temporary pointer */
int8ptr_t **pt;
...
}
8.8.1 References
[Misra2012] Rule 18.5 Declarations should contain no more than two levels of pointer nesting.
Bad example
In the following example, the access should be rewritten with the indirection opera-
tor.
(* list.p_head).pNext = NULL;
Good example
In the following code, the indirection operator is correctly used.
list.p_head ->pNext = NULL;
Pointer arithmetic
Pointer arithmetic is the use of pointer values as integer values in an elementary
arithmetic operation (subtraction and addition).
Pointer arithmetic is very often used in the case of a pointer to an array element to navigate be-
tween the different elements of the array. Apart from this case, arithmetic on memory addresses
is very risky.
RULE
RULE — Only incrementing or decrementing array pointers is authorised
94 Incrementing or decrementing pointers should only be used on pointers represent-
ing an array or an element of an array.
Arithmetic on void* type pointers is therefore prohibited. No memory size is in fact associated
with the void* type, which causes undefined behaviour, in addition to the violation of the previous
rule.
RULE
RULE — No arithmetic on void* pointers is authorised
95 The use of any arithmetic on void* type pointers must be prohibited.
Even in the case of pointer arithmetic on elements in an array, a special care must be taken to
ensure that the arithmetic will not cause dereferencing outside of the array.
As a result, subtractions or comparisons between pointers will only be relevant for pointers on the
same array.
RULE
RULE — Subtraction and comparison between pointers in the same array
97
only
Only subtractions and comparisons of pointers on the same array are authorised.
RECO
RECOMMENDATION — A fixed address should not be assigned directly to
98
a pointer
Bad example
# include <stddef.h>
# include <stdint.h>
void function(int8_t * ptr_param)
{
int8_t tab1 [10];
int8_t tab2 [100];
uint8_t nb_elem = pt2 - pt1; /* the two pointers are not on the same array and
the type is not adapted */
...
}
Good example
# include <stddef.h>
# include <stdint.h>
void function(int8_t * ptr_param)
{
int8_t tab1 [10];
int8_t tab2 [100];
ptrdiff_t nb_elem = pt2 - tab2; /* both pointers are on the same array and
dedicated type used ( taken from stddef .h) */
...
}
8.10.1 References
[Misra2012] Rule 18.1 A pointer resulting from arithmetic on a pointer operand shall address an
element of the same array as that pointer operand.
[Misra2012] Rule 18.2 Substraction between pointer shall only be applied to pointers that address
elements of the same array.
[Misra2012] Rule 18.3 The relational operators shall not be applied to objects of pointer type ex-
ception where they point into a same object.
[Misra2012] Rule 18.4 The +, -, += and -= operators shall not be applied to an expression of pointer
type.
[Cert] Rule ARR36-C Do not substract or compare two pointers that do not refer to the same array.
[Cert] Rule ARR37-C Do not add or subtract an integer to a pointer to a non-array object.
[Cert] Rule ARR39-C Do not add or substract a scaled integer to a pointer.
[Cert] Rec. EXP08-C Ensure pointer arithmetic is used correctly.
[IsoSecu] Subtracting or comparing two pointers that do not refer to the same array [ptrobj].
[IsoSecu] Forming or using out-of-bounds pointers or array subscripts [invptr].
[Cwe] CWE-469 Use of pointer substraction to determine size.
[Cwe] CWE-468 Incorrect pointer scaling.
[Cwe] CWE-466 Return of pointer value outside of expected range.
[Cwe] CWE-587 Assignment of a fixed address to a pointer.
[AnsiC99] Sections 6.2.5, 6.3.2.3, 6.5.2.1, 6.5.6.
[AnsiC90] Sections 6.1.2.5, 6.2.2.3, 6.3.6, 6.3.8.
RULE
RULE — A structure must be used to group data representing the same
99
entity
Linked data must be grouped within a structure.
Information
This rule is not linked to an immediate security risk, but is a common-sense rule to
be applied to all developments.
Bad example
In the following example, the lack of structure results in function prototypes that are
difficult to understand.
void rectangle (float x0 , float y0 , float x1 , float y1 , float x2 ,
float y2 , float x3 , float y3);
void pyramide(float* coords); /* coords is an array */
Good example
The following example correctly uses independent structures to represent different
geometric shapes.
typedef struct point_s {
float x;
float y;
} Point_t;
RULE
RULE — Do not calculate the size of a structure as the sum of the size of
100
its fields
Because of the padding, the size of a structure should not be assumed to be the sum
of the size of its fields.
Bad example
# define SIZE_TABL 100
...
typedef struct{
int tabl[SIZE_TABL ];
size_t size;
} my_struct;
...
size_t sizestruct= sizeof(my_struct.tabl)+sizeof(my_struct.size);
/* assumes that the size of the structure is the sum of the size of the elements */
...
Good example
# define SIZE_TABL 100
...
typedef struct {
int tabl[SIZE_TABL ];
size_t size;
} my_struct;
...
size_t sizestruct = sizeof(my_struct); /* good size */
...
Warning
The use of non-standard attributes such as packed is not considered in this guide.
9.3 bit-field
In C, you can specify the size (in bits) of elements of a structure or union, in particular to use
memory more efficiently. Precautions must be taken when using bit-field. On the one hand, an
int type bit-field will not necessarily be signed. In fact, an int variable is indeed signed by default
except in the case of bit-field, where the sign becomes dependent on the compiler implementation.
RULE
RULE — All bit-fields must be explicitly declared as unsigned
101
RULE
RULE — Do not make assumptions about the internal representation of
102
structures with bit-fields
Bad example
typedef struct structure {
int ok: 1; /* bit -field of size 1 */
int value: 7; /* bit -field for which the sign depends on the compiler used */
} struct_bitfield ;
..
struct_bitfield s;
int *pt_s;
pt_s =( int *) &s;
s.ok=1;
..
if(s.ok ==1) /* if compiled with gcc for example , by default the bit - fields are
signed , therefore being of size 1, s.ok equals 0 or -1 ! */
{
pt_s ++; /* ? */
*pt_s =100; /* ? */
}
Good example
typedef struct structure {
unsigned int ok: 1; /* unsigned bit -field */
unsigned int value: 7; /* unsigned bit - field */
} struct_bitfield ;
..
9.3.1 References
[Cert] Rule EXP11-C Do not make assumptions regarding the layout of structures with bit-fields.
[Cert] Rec. EXP12-C Do not make assumptions about the type of a plain int bit-field when used in
an expression.
[Misra2012] Dir. 1.1. Any implementation-defined behaviour on which the output of the program
depends shall be documented and understood.
Furthermore, this means accepting arrays of undefined size, which contradicts the rule in sec-
tion 8.3. FAMs are therefore prohibited.
RULE
RULE — Do not use FAMs
103
9.4.1 References
[Misra2012] Rule 18.7 Flexible array member shall not be used.
[Cert] MEM33-C Allocate and copy structures containing a flexible array member dynamically.
[Cert] Rule DCL38-C Use the correct syntax when declaring a flexible array member.
The use of unions must be strictly limited to cases where the type is verified by other means and
only if it is necessary (for network frame parsing for example), and this must be justified with a
comment in the code.
9.5.1 References
[Misra2012] Rule 19.2 The union keyword should not be used.
For signed integer operations, it must be ensured that there is no overflow of the size of the asso-
ciated type and for unsigned integer operations, that there will be no value wrap.
RULE
RULE — Remove all possible value overflows for signed integers
105
RECO
RECOMMENDATION — Detect all possible value wraps for unsigned inte-
gers
106
Bad example
In the following function, no overflow is checked.
# include <stdint.h>
void f(uint8_t i, int8_t j)
{
uint8_t ibis = i+ 2;
int8_t j_bis = j +3;
/* ... */
}
Good example
In the following function, overflows are checked.
# include <stdint.h>
RULE
RULE — Detect and remove any potential division by zero
107 This check must be systematic for any division or computation of remainder of a
division.
Bad example
In the following function, no check on a possible division by zero is performed.
# include <stdint.h>
void func(int8_t i, int8_t j)
{
int8_t result;
result = i / j;
...
}
Good example
In the following function, there is a check on a possible division by zero.
# include <stdint.h>
void func(int8_t i, int8_t j)
{
int8_t result;
if (0 == j)
{
/* error */
}
else
{
result = i / j;
}
...
}
10.1.1 References
[Cert] Rule INT30-C Ensure that unsigned integer operation do not wrap.
[Cert] Rule INT31-C Ensure that integer conversions do not result in lost or misinterpreted data.
[Cert] Rule INT32-C Ensure that operations on signed integers do not result in overflow.
[Cert] Rule INT33-C Ensure that division and remainder operations do not result in divide-by-zero
errors.
[Cert] Rec. INT08-C Verify that all integer values are in range.
A complex expression will need to be simplified to aid understanding. If the complexity is relevant
(optimisation, etc.), a comment should explain and accompany the expression. A fairly common
example is to use a n-bit left shift for a multiplication by 2n (or a right shift for a division). Thus,
the following expression:
a << b;
can be used to perform the operation a ∗ 2b . Such expressions do not help with understanding the
code. In addition, these shifts must comply with precise rules taking into account the number of
bit shifts requested and the size of the type concerned (cf. section 10.7). It is recommended to use
bit shifts only when the purpose is to handle the bits of a register, for example.
RECO
RECOMMENDATION — Arithmetic operations should be written in a way
108
that assists with readability
Arithmetic operations that are as explicit (natural) as possible and follow the logic
of the program must be used.
Bad example
In the following example, the arithmetic operations are not readable. Understanding
of the operations is not immediate.
/* In the following calculation , we want to calculate a² + 4ac + b² */
uint64_t res;
uint32_t a, b, c;
res = a * a + ((a * c) << 2) + b * b; /* an explanation would be welcome */
/* a<<b is equalivaent to a* 2^b but here the bit shift is used for a
multiplication */
...
/* mask computation */
uint32_t bitfield = 0xCAFEBABE;
uint32_t n, bitmask;
n = 4;
bitmask = 1;
for(n = n; n > 0; n--) {
bitmask = 2 * bitmask;
} /* on the contrary here , the bit shift would have been more logical */
Good example
The following code performs calculations using simple arithmetic operations.
/* computation of a² + 4ac + b² */
uint64_t res;
uint32_t a;
uint32_t b;
uint32_t c;
res = (a * a) + (4 * (a * c)) + (b * b); /* plus clair */
...
/* mask computation */
uint32_t bitfield = 0xCAFEBABE;
uint32_t n = 4;
uint32_t bitmask
bitmask = 2 << n; /* bits handling */
bitfield = bitfield & (~ bitmask);
10.2.1 References
[Cert] Rec. INT14-C Avoid performing bitwise and arithmetic operations on the same data.
The systematic use of parentheses in the calculations makes it possible to clearly show and choose
the priority of the operations and the order in which the calculation is performed.
Information
The C language operators and their priorities are presented in appendix D.
RULE
RULE — Explanation of the order of evaluation of calculations through the
109
use of parentheses
To avoid any ambiguity in an expression, its subexpressions must be surrounded by
parentheses to make the order of evaluation of a calculation more explicit.
10.3.1 References
Another classic error is the combined if(a==b==c) equality test whose objective is, a priori, to
verify that the three variables are equal. In practice, as in the previous case, this test does not
behave as the developer expects. Indeed, this conditional will only be true if the three variables
are 1 or if c is 0 and a and b are different.
Boolean expression
The C language does not have a true boolean type in C90. The boolean type was
introduced with C99. It has an associated library (stdbool.h). However, we will
use the term boolean expression for expressions in the C language, even before C99,
where the result of the evaluation corresponds to a truth value, as is typically the
case for comparison expressions. A boolean expression corresponds to the false truth
value for an evaluation returning the value 0. Any other value returned by a boolean
expression (whether it is 1 or a negative, positive, integer or non-integer value) cor-
responds to the true truth value.
Boolean expressions containing at least two relational operators are prohibited without parenthe-
ses and must be broken down either into nested conditionals or into several relational expressions.
RECO
RECOMMENDATION — Avoid expressions of comparison or multiple equal-
110
ity
Bad example
# define N 100
...
if (0 <= x <= N) {
/* statement 1 ALWAYS executed */
} else {
/* statement 2 NEVER executed */
}
...
if (30 < x < 40) {
printf("pb"); /* ALWAYS executed */
}
...
Good example
# define N 100
...
if ((0 <= x) && (x <= N)) { /* case 1 : breakdown into 2 relational expressions */
/* statement 1 */
} else {
/* statement 2 */
}
...
if (30 < x) { /* case 2 : breakdown into 2 nested conditional statements */
if (x <40) {
printf("pb");
...
}}
10.4.1 References
[Misra2012] Rule 10.1 Req. Operands shall not be of an inappropriate essential type.
[Misra2012] Rule 12.1 The precedence of operators within expressions should be made explicit.
[Cert] EXP00-C Rec. Use parentheses for precedence of operation.
[Cert] EXP13-C Rec. Treat relational and equality operators as if they were nonassociative.
[Cwe] CWE-783 Operator Precedence Logic Error.
Bad example
In the following example, it is necessary to know the associativity between the oper-
ators and the priority between them in order to understand the order of evaluation.
if (x > 0 && y * z > length) {
...
}
u = z > 100 && 100 == x || x + y < z;
Good example
In the following code, the use of parentheses makes it possible to explicitly know the
order of evaluation.
if ((x > 0) && ((y * z) > length)) {
...
}
u = (z > 100) && ((100 == x) || ((x + y) < z));
10.5.1 References
[Cert] EXP00-C Rec. Use parentheses for precedence of operation.
[Misra2012] Rule 12.1 The precedence of operators within expressions should be made explicit.
[Cwe] CWE-783 Operator Precedence Logic Error.
RULE
RULE — Implicit comparison with 0 prohibited
113 All boolean expressions must use comparison operators. No implicit test with a value
equal to 0 or different from 0 must be performed.
Bad example
The comparisons implicit in the following code should be removed in favour of ex-
plicit comparisons.
# define MAX 10
uint8 z;
..
while (x) {
...
}
if (x < y) {
...
} else {
...
}
if (!z) { /* implicit comparison with 0 and z should be of the bool type */
...
}
if (ptr) {
...
}
for (x = MAX; x; x--) {
...
}
Good example
In the following example, no implicit comparison is performed and the dedicated
header file is used.
# include <stdbool.h>
10.6.1 References
RECO
RECOMMENDATION — Bitwise operators must be used with unsigned
115
operands only
Furthermore, some bitwise operators such as &, | or ^ can easily be mistakenly used — especially
the first two — instead of logical operators such as &&, || and !. To avoid this confusion, it is
important to check that the bitwise operators used in boolean expressions are actually the desired
operators. Bitwise operators have no reason to be applied to any operand of type boolean or
similar.
RULE
RULE — No bitwise operator on an operand of type boolean or similar
116
Bad example
if ((var >= 0) & (var < 120)) { /* operator confusion */
...
if ((val && FLAG) != 0) /* operator confusion ? */
...
}
Good example
if ((var >= 0) && (var < 120)) { /* correction */
...
if ((val & FLAG) != 0) /* correct use here of the bitwise operator
in a boolean expression */
...
}
10.7.1 References
[Cert] Rec. INT13-C Use Bitwise operators only on unsigned operands.
[Cert] Rec. EXP14-C Beware of integer promotion when performing bitwise operations on integer
types smaller than int.
[Cert] Rule INT34-C Do not shift an expression by a negative number of bits or by greater or equal
the number of bits that exist in the operand.
GOOD GOOD PRACTICE — Do not use the value returned during an assignment
PRACTICE
117
Information
During the compilation phase with the right options (-Wall, etc.), a warning will
be emitted suggesting in particular to place the assignment in parentheses in the
boolean expression (-Wparentheses option).
RULE
RULE — Assignment prohibited in a boolean expression
118 An assignment must not be made in any boolean expression. An assignment must
be made in an independent statement.
In order to limit the risks of writing an assignment with the = operator instead of a comparison
with the == operator, when the comparison is made between a variable and a constant operand,
the constant operand should be written as the left operand of the == operator and the variable
as the right operand. The compiler raises a warning when trying to assign a value to a constant
operand.
Information
This good practice is debatable, and is therefore not enforced but merely advised.
Compiling this type of code with strict options (in particular with -Wall), as required
by section 5.2, is actually sufficient to detect the use of the assignment operator in-
stead of the comparison operator in a boolean expression.
Good example
In the following example, all assignments are made in independent statements and
the various problems are corrected.
x = y + z;
if (0 < x) {
...
}
if (VALUE == z) {
...
}
10.8.1 References
[Misra2012] Rule 13.4 The result of an assignment operator should not be used.
[Cert] Rule EXP45-C Do not perform assignments in selection statements.
[IsoSecu] No assignment in conditional expressions [boolasign].
[Cwe] CWE-480 Use of incorrect operator.
[Cwe] CWE-481 Assigning instead of comparing.
[Cwe] CWE-482 Comparing instead of assigning.
However, code containing multiple assignments is difficult to read and also difficult to maintain.
Break the multiple variable assignment statement down into as many assignment statements as
there are variables.
RULE
RULE — Multiple assignment of variables prohibited
120 Multiple assignment of variables is not authorised.
Good example
The following code contains one assignment per variable.
...
a = 1;
b = 1;
c = 1;
d = 1;
However, when several statements are present on the same line, the code is less readable. Debug-
ging is also more difficult, since it is not possible to check the execution of code one statement at
a time.
The presence of multiple statements per line of code also distorts the metric of the number of lines
of code.
RULE
RULE — Only one statement per line of code
121
Bad example
In the following example, the code is difficult to understand.
int32_t a; int64_t b;
a = 4; b = a / 6; printf("a = %d, b = %lld\n", a, b);
Good example
int32_t a;
int64_t b;
a = 4;
b = a / 6;
printf("a = %d, b = %lld\n", a, b);
The representation of floating-point numbers in a machine is a complex notion which is often not
well known or badly understood and moreover, it is dependent on the precision associated with
the type. These floating-point numbers are often a source of errors.
Not all actual values can be represented as floating-point numbers, and other “unnatural” phe-
nomena such as absorption 11 and cancellation 12 can occur with the use of floating-point numbers,
although these points will not be detailed in this guide. Further details are given in the standard
IEEE754 [float], which ensures reproducible inter-compiler and inter-architecture behaviour in the
presence of floating-point numbers.
In addition, the error associated with the use of these floating-point numbers can become greater
than the result of the calculation using them.
Finally, certain elementary properties of real arithmetic are no longer true when using floats: com-
mutativity, associativity, etc.
For all of these reasons, the use of floating-point numbers is strongly discouraged. Should the use
of floating-point numbers prove necessary for numerical processing for example, the developer will
have to ensure that the constant float values are representative and that they are correctly used in
accordance with the associated precision.
RECO
RECOMMENDATION — Limit the use of floating-point numbers to what is
123
strictly necessary
The use of floating-point numbers should be limited.
Float type loop counters are sources of error due to the limited representativeness of this type and
the associated complexity.
11. Phenomenon related to the precision of floats, such as LargeF loatV alue+F loatingEpsilon = LargeF loatV alue, i.e. a large
float value will “absorb” a small float value
12. Another phenomenon always related to the precision and representation of floating-points numbers, such that for two close
float values, F loatV alue1 − F loatV alue2 = 0, whereas formally, F loatV alue1 ̸= F loatV alue2
The handling of float values in Boolean expressions is also always very risky in connection with
problems of representation and precision of these values. The use of the logic operators != or
== on floats is incorrect in most cases. The results may depend on the level of optimisation, the
compiler itself and the platform used.
RULE
RULE — Do not use floating-point numbers for comparisons of equality or
125
inequality
Bad example
float y = 0.1; /* not representable in simple precision */
...
if (y == 0.1) /* comparison of float value AND 0.1 double value so promotion of y
*/
printf("equal\n");
else
if (y == 0.1f) /* 0.1f float value - condition checked here */
printf("equal2\n");
else printf("not equal\n");
...
for (float x = 0.1f; x <= 1.0f; x += 0.1f) { /* the loop will be carried out 9 or
10 times */
}
Good example
double y = 0.1; /* type correction */
...
for (uint count = 1; count <= 10; count ++) {
float x = count /10.0f; /* 10 passes exactly in the loop */
}
10.11.1 References
[Misra2012] Rule 14.1 A loop counter shall not have essentially floating type.
[Cert] Rule FLP30-C Do not use floating-point variables as loop counters.
[Cert] Rule FLP30-C Do not use object representations to compare floating-point values.
[Cert] Rec. FLP00-C Understand the limitations of floating-point numbers.
[Cert] Rec. FLP01-C Take care in rearranging floating-point expressions.
[Cert] Rec. FLP02-C Avoid using floating-point numbers when precise computation is needed.
[Cert] Rec. FLP03-C Detect and handle floating-point errors.
[Cert] Rec. FLP04-C Check floating-point inputs for exceptional values.
[Cert] Rec. FLP05-C Do not use denormalized numbers.
[Cwe] CWE-369 Divide by Zero.
[Cwe] CWE-681 Incorrect conversion between numerical types.
As these complex numbers are based on a floating representation, their use is strongly discouraged.
RECO
RECOMMENDATION — No use of complex numbers
126 Complex numbers introduced since C99 should not be used.
RULE
RULE — Systematic use of braces for conditionals and loops
127 Never omit braces to delimit a statement block. Braces must be written to delimit a
block of statements after loops (for, while, do) and conditionals (if, else).
Bad example
In the code below, a conditional is not delimited by braces.
if (x == 0) /* braces are required , even for a single statement */
printf("X = 0\n");
/* An indented statement under the printf may visually suggest that the statement
is within the if , but it is not. For a jump statement like "goto", this may
result in a significant portion of the code not being executed . */
if (x != 0) {
if (x < 0) {
...
while (x < 0) {
x++;
...
}
} else {
while (x > 0) {
x--;
...
}
}
}
/* example of ’Apples goto fail */
if (err = SSLHashSHA1.update (&hashCtx , &signedParams)) != 0)
goto fail;
goto fail;
/* other checks , but not reachable due to the duplicate goto fail
without braces */
fail:
/* cleaning and releasing of buffers */
return err;
11.1.1 References
[Misra2012] Rule 15.6 The body of an iteration-statement or a selection statement shall be a
compound-statement.
[Cert] Rec. EXP19-C Use braces for the body of an if, for, or while statement.
RULE
RULE — Systematic definition of a default case in switch
128 A switch-case must always contain a default case placed last.
The code in each case should be simple and contain few statements. If complex processing is to be
carried out in a case, then a function must be defined for this processing and this function must
be called from the case.
RECO
RECOMMENDATION — No nesting of control structure in a
130
switch-case
Even though C allows it, the nesting of control structures inside a switch should be
avoided.
Finally, it is forbidden to declare, initialise variables or introduce code statements within a switch
statement before the first label associated with the first case of the switch-case.
Bad example
In the following example, the final default statement is missing.
switch(var) {
int i = 0; /* not authorized */
case VAL1:
...
break;
case VAL2:
...
case VAL3:
...
break;
/* absence of default and break and no comment to explain the case on the value
VAL2 */
}
Good example
In the following example, a break statement is present for each case. There is also
a final default statement to ensure that processing is carried out if the value did not
match any of the cases.
int i = 0; /* displaced declaration and initialisation */
switch(var) {
case VAL1:
...
break;
case VAL2:
...
/* break voluntarily missing */
case VAL3:
...
break;
default :
11.2.1 References
[Misra2012] Rule 16.1 All switch statements shall be well-formed.
[Misra2012] Rule 16.2 A switch label shall only be used when the most closely-enclosing compound
statement is the body of the switch statement.
[Misra2012] Rule 16.3 An unconditional break statement shall terminate every switch-clause.
[Misra2012] Rule 16.4 Every switch statement shall have a default label.
[Misra2012] Rule 16.6 Every switch statement shall have at least 2 switch-clauses.
[Misra2012] Rule 16.7 A switch expression shall not have a essentially boolean type.
[Cert] Rule DCL41-C Do not declare variables inside a switch statement before the first case label.
[Cert] Rec. MSC01-C Strive for logical completeness.
[Cert] Rec. MSC17-C Finish every set of statements associated with a case label with a break state-
ment.
[Cwe] CWE-484: Omitted Break Statement in Switch.
[IsoSecu] Use of an implied default in a switch statement [swtchdflt].
Furthermore, the C language does not require each element (initialisation, stop condition and
increment/decrement) of the for statement to be completed. It is possible to leave the initialisation
empty, for example, or even to leave each element empty, resulting in an infinite loop. In the case
of an infinite loop, the form while (1) { ... } is to be preferred to the form for(;;) { ... }.
RULE
RULE — Correct construction of for loops
132 Each element of a for loop must be completed and contain exactly one statement.
Thus a for loop must contain an initialisation of its counter, a stop condition on its
counter, and a loop counter increment or decrement.
Bad example
In the following example, the comma is used to separate several initialisations and
modifications of variables in the first and third element of the for. In addition, for
loops should be replaced by while() { ... } or do { ... } while () loops.
Good example
The following code only initialises the loop counter in the first for element, and only
increments the counter in the third element. The for loops contain all its elements
(initialisation, stop condition, counter increment).
# define MAX_LOOP 10U
for (i = 0; i < arraySize; i++) {
...
dataArray[i] = some_function(i);
...
}
while (1) {
data = read ();
if (0 == data) {
break;
}
...
}
i = 0;
do {
max = some_function(i);
i++;
}
while (i < max);
...
a = 0;
for (i = 0; i < MAX_LOOP; i++) {
...
some_function(a);
...
a += 2;
}
11.3.1 References
[Misra2012] Rule 14.2 A for loop shall be well-formed.
[Misra2012] Rule 15.6 The body of an iteration-statement or a selection statement shall be a
compound-statement.
[Cert] Rec. EXP15-C Do not place a semicolon on the same line as an if, for, or while statement.
[Cert] Rec. EXP19-C Use braces for the body of an if, for, or while statement.
The loop counter should only be modified in the third part of the for loop.
It is common to modify within the body of the loop a flag or other variable that intervenes at the
conditional stop expression of the for loop. This scenario must then be replaced by the use of a
break allowing the exit from the loop.
RULE
RULE — Change to a counter of a for loop forbidden in the body of the
133
loop
The counter of a for loop must not be changed inside the body of the for loop.
Bad example
The example below shows a for loop with a modification of its counter in its body.
There is a risk of an infinite loop.
# define MAX_LOOP 10U
Good example
In the following code, no changes are made to the counter in the body of the for
loop.
# define MAX_LOOP 10U
11.4.1 References
[Misra2012] Rule 14.2
RULE
RULE — No use of backward goto
134 Prohibit within a function the use of goto statements referring to a label that is
placed before this goto statement.
Bad example
The following example contains a backward goto. This choice of implementation
corresponds to an assembler approach, and does not take advantage of the higher
level possibilities offered by the C language.
# define BUFFER_SIZE 100U
void foo() {
uint8_t s[BUFFER_SIZE ];
uint8_t x = 0;
my_loop:
s[x] = x;
x++;
if(x < BUFFER_SIZE)
{
goto my_loop; /* backward goto prohibited */
}
}
Good example
The following example uses a loop type control structure. There is no need for a
backward goto.
# define BUFFER_SIZE 100U
void foo() {
uint8_t s[BUFFER_SIZE ];
uint8_t x = 0;
for(x = 0; x < BUFFER_SIZE; x++) {
s[x] = x;
}
}
The forward goto should only be used for error management, and the number of labels should be
kept to a minimum.
RECO
RECOMMENDATION — Limited use of forward goto
135 The use of a forward goto is tolerated only in cases where it allows:
n the number of exit points from the function to be significantly limited;
Good example
The following code does not use a forward goto, but uses the control structures pro-
posed by the C language.
# define BUFFER_LEN (128U)
int32_t my_function(int32_t a) {
FILE* f = NULL;
uint8_t *buffer = NULL;
int32_t result = ERR_UNDEFINED;
f = fopen("/my/path", "r");
if(NULL == f) {
result = ERR_FOPEN;
} else {
buffer = (uint8_t *) malloc(BUFFER_LEN * sizeof(uint8_t));
if(NULL == buffer) {
result = ERR_MALLOC;
}
else
{
...
free(buffer);
buffer = NULL;
result = ERR_NOERROR;
}
fclose(f);
f = NULL;
}
return result;
}
12.2.1 References
[Misra2012] Rule 15.1 15.5.
Declaration/Prototype of function
The declaration of a function or its prototype is a statement which defines three el-
ements: the return type of the function, its name and the list of its arguments, fol-
lowed by a semicolon.
Definition of function
The definition of a function is the body of the function, i.e. the set of statements it
executes. A function definition also contains a prototype of the function.
C90 allows for the implicit declaration of functions, whether it be the absence of a return type or
the absence of a function declaration. C99 is stricter and imposes at least one type specifier.
The C language, in its successive versions, proposes different forms for the declaration of functions.
Combining these different forms of function declaration is not recommended since this risks re-
sulting in a much less precise analysis of the code, and leading to problems when editing links.
Information
Compilers raise a warning (-Wimplicit-function-declaration or -Wimplicit-int
or -Wreturn-type), but assume an implicit extern int type, i.e. by default, a function
not associated with a return type has an integer type.
RULE
RULE — Any (non-static) function defined must have a function declara-
136
tion/prototype
RULE
RULE — The prototype declaration of a function must be consistent with
137
its definition
The types of parameters used to define and declare a function must be the same.
Activating the compiler warnings is used to find out which functions are not correctly declared
(missing return type, inconsistency of types between declaration and definition).
Bad example
In the example below, the return type is missing for a declaration, a function with-
out parameters is not correctly declared and there is an inconsistency between the
declaration and the definition.
/* header .h */
foo(uint8_t a); /* the return type is missing */
uint32_t bar(uint16_t b);
void car(); /* void is missing in the parameter type to indicate that the
function does not take parameters */
/* file.c */
foo(uint8_t a) {
...
}
uint32_t bar(int32_t b) { /* after the declaration , b must be a uint16_t */
...
}
void car() {
...
}
Good example
The following example makes a correct declaration and definition of functions.
/* header .h */
void foo(uint8_t a);
uint32_t bar(uint16_t b);
void car(void);
/* file.c */
void foo(uint8_t a) {
...
}
uint32_t bar(uint16_t b) {
...
}
void car(void) {
...
}
13.1.1 References
[Misra2012] Rule 8.1 Types shall be explicitly specified
[Misra2012] Rule 8.2 Function types shall be in prototype form with named parameters
[Misra2012] Rule 8.3 All declarations of an object or function shall use the same names and type
qualifiers
[Misra2012] Rule 17.3 A function shall not be declared implicitly
RECO
RECOMMENDATION — Documentation of functions
139 All functions must be documented. This includes:
n a description of the function and the processing carried out;
n the documentation of each parameter, the direction of the parameter (input, out-
put, input and output) and any condition existing on it;
This also includes the conditions for proper use of the function to be specified in the prototype,
especially in the case of portable code (Linux, Windows).
RECO
RECOMMENDATION — Specify call conditions for each function
140
RULE
RULE — The validity of all the parameters of a function must systematically
141
be questioned
This includes:
n the validity of the addresses for pointer-type parameters must be checked (point-
ers must be non-null, properly aligned, etc.);
n the parameters must be checked to ensure that they belong to their domain.
This applies to the functions defined by the developer (cf. section 13.2) as well as to
the functions of the standard library.
Bad example
In the following example, the validity of the parameters is not checked.
double division(int32_t n, int32_t d) {
/* d != 0 not verified */
return (( double)n) / (( double)d);
}
Good example
Example 1:
The following code shows an example where the validity of the parameters is
checked:
double division(int32_t n, int32_t d) {
double res = 0.0;
if(0 == d) {
/* error handling */
}
else {
res = (( double)n) / (( double)d);
}
return res;
}
Example 2:
The following code shows a second example where the validity of the parameters is
checked:
uint8_t encrypt(uint8_t *output , int32_t *output_len ,
const uint8_t *input , const int32_t input_len ,
encrypted_ctx *ctx) {
uint8_t err = 0;
13.3.1 References
[Misra2012] The validity of values passed to library shall be checked.
[Misra2012] Dir 4.1 Run-time failures shall be minimized
[Cert] API00-C Functions should validate their parameters
[Cert] Rule ARR38-C Guarantee that library functions do not form invalid pointers.
[Cert] Rec. MEM10-C Define and use a pointer validation function.
[Cwe] CWE-20 Insufficient input validation
[Cwe] CWE-628 Function call with incorrectly specified arguments.
[Cwe] CWE-686 Function call with incorrect argument type.
[Cwe] CWE-687 Function call with incorrectly specified argument value.
[IsoSecu] Calling functions with incorrect arguments [argcomp].
RULE
RULE — Pointer-type function parameters which point to memory that is
142
not to be changed must be declared as const
Mark as const all pointer-type parameters of a function that point to a memory area
that is read-only in the body of the function. The const qualifier must be applied to
the pointed object.
Bad example
The following example should use const for its parameter.
uint32_t foo(uint32_t *val) {
/* val lue */
uint32_t ret = 0;
Good example
In the following example, const is correctly used for the pointer parameter. The
memory area pointed to is not in fact modified in the body of the function.
uint32_t foo(const uint32_t *val) {
uint32_t ret = 0;
if (NULL != val){
if(TEST_VALUE > *val) {
ret = (*val) * 2;
} else {
ret = (*val);
}
}
return ret;
}
13.4.1 References
[Cert] Rec. DECL00-C Const-qualify immutable objects.
[Cert] Rec DECL13-C Declare function parameters that are pointers to values not changed by the
function as const.
[Cert] Rule EXP40-C Do not modify constants objects.
[Misra2012] Rule 8.13 A pointer should point to a const-qualified type whenever possible.
[Cwe] CWE-20 Improper Input Validation.
[Cwe] CWE-369 Divide by Zero.
Information
An inline function can be accessed by multiple files by being declared in a header
file.
RULE
RULE — inline functions must be declared as static
143 To avoid undefined behaviour, an inline function is systematically static.
RULE
RULE — Do not redefine functions or macros from the standard library or
144
another library
Identifiers, macros, or function names that are part of the standard library or another
library used must not be redefined.
Bad example
In the following example, confusion will occur due to the use of a function name
that already exists in the standard library.
/* do not reuse the name of the standard library */
void* malloc (size_t taille);
Good example
The following example defines a name that does not clash with the name of a func-
tion in the standard library.
/* renaming of the function */
void* mymalloc (size_t taille);
13.6.1 References
[Misra2012] Rule 5.8 Identifiers that define objects or functions with external linkage shall be
unique.
[Misra2012] Rule 5.9 Identifiers that define objects or functions with internal linkage shall be
unique.
The calling function must test the value returned by the function to ensure its validity against the
interface documentation (returned value within the value range or returned value corresponding
to a success or error code).
RULE
RULE — The return value of a function must always be tested
145 When a function returns a value, the returned value must systematically be tested.
Bad example
In the following code, the value returned by the function is not tested and no pro-
cessing is performed if an error has occurred.
struct stat o_stat_buffer;
stat("somefile.txt", &o_stat_buffer);
/* success of the stat function not tested */
...
Good example
In the following code, the value returned by the function is correctly tested.
struct stat o_stat_buffer;
uint8_t i_result = 0;
i_result = stat("somefile.txt", &o_stat_buffer);
if (0 != i_result) {
/* erreur */
return 0;
}
...
13.7.1 References
[Cert] Rec. EXP12-C Do not ignore values returned by functions.
[Misra2012] Dir. 4.7: If a function returns error information, then that error information shall be
tested.
[Misra2012] Rule 17.7 The value returned by a function having non-void return type shall be used.
[Cwe] CWE-252 Unchecked Return Value.
[Cwe] CWE-253 Incorrect Check of Function Return Value.
[Cwe] CWE-754 Improper check for unusual or exceptional conditions.
Bad example
In the following example, there are paths that do not explicitly return a value.
uint32_t encr_data(const uint8_t *p_data , uint32_t ui32_data_len ,
uint8_t ** pp_encrypted_data , uint32_t * ui32_encrypted_data_len )
{
uint8_t *p_encrypted_data = NULL;
if (NULL != p_data
&& NULL != pp_encrypted_data
&& NULL != ui32_encrypted_data_len ) {
if (ui32_data_len > 0) {
p_encrypted_data = (uint8_t *) calloc(ui32_data_len , sizeof(uint8_t));
...
return 1;
}
}
/* implicit return */
}
Good example
In the following code, the function code always explicitly returns a value.
uint32_t encr_data(const uint8_t *p_data , uint32_t ui32_data_len ,
uint8_t ** pp_encrypted_data , uint32_t * ui32_encrypted_data_len )
{
uint32_t ui32_result_code = 0;
uint8_t *p_encrypted_data = NULL;
if (NULL == p_data
|| NULL == pp_encrypted_data
|| NULL == ui32_encrypted_data_len ) {
ui32_result_code = 0;
goto End;
}
if (0 == ui32_data_len) {
ui32_result_code = 0;
goto End;
}
p_encrypted_data = (uint8_t *) calloc(ui32_data_len , sizeof(uint8_t));
...
(* pp_encrypted_data ) = p_encrypted_data;
ui32_result_code = 1;
End:
return ui32_result_code;
}
13.8.1 References
[Misra2012] Rule 17.4 All exit paths from a function with non-void return type shall have an explicit
return statement with an expression.
[Cert] Rule MSC37-C Ensure that control never reaches the end of a non-void function.
RULE
RULE — Structures must be passed by reference to a function
147 Do not pass structure type parameters by copying when calling a function.
Bad example
In the following example, the parameter is passed by value and not by address.
# define STR_SIZE 20U
typedef struct
{
unsigned char surname[STR_SIZE ];
unsigned char firstname[STR_SIZE ];
} person_t;
Good example
The following code correctly passes a structure type parameter using a pointer.
# define STR_SIZE 20U
typedef struct
{
unsigned char surname[STR_SIZE ];
unsigned char firstname[STR_SIZE ];
} person_t;
void some_function () {
person_t person;
...
add_person (& person);
...
}
To suppress this ambiguity, it is preferable to use the form with [] for an array type parameter as
indicated in sub-section 8.1.
RECO
RECOMMENDATION — Passing of an array as a parameter for a function
148 There are several ways to pass an array as a parameter for a function. When passing
by pointer, it must be specified in the function documentation that the parameter
corresponds to an array and also use the dedicated array notation.
Warning
For a multi-dimensional array, only the first dimension of the array can remain un-
defined when passing as a parameter, which therefore means defining the following
dimensions. For example, for a two-dimensional array, using tab[][] as a parameter
is a mistake, as a minimum the second dimension must be specified.
Bad example
The following example shows a prototype function with a pointer-type parameter.
It is an array passed as a parameter but this cannot be guessed from the function
signature. In this example, tab can:
n either be an integer passed by address
Good example
In this second example, the notation and comments ensure that it can be immedi-
ately determined that the parameter is indeed an array.
void func(int32_t tab[], uint32_t count); /* tab is an array of count elements */
The prototype of the function must be modified if the parameter is not useful.
However, in some cases, it may be justified not to use one (or more) parameters of a function:
n the function corresponds to a callback function whose prototype is imposed;
n for reasons of compatibility with existing code when upgrading a library. A previously used
parameter is no longer used;
In all these cases, a comment must then explicitly state why the parameter is ignored.
RECO
RECOMMENDATION — Mandatory use in a function of all its parameters
149 All the parameters present in the prototype of the function must be used in its im-
plementation.
Information
The -Wunused-parameter option is used to provide alerts in this scenario.
Bad example
In the following example, a parameter of the function is not used and should there-
fore be deleted.
uint32_t compute_data(uint32_t ui32A , uint32_t ui32B , uint32_t ui32C) {
uint32_t ui32_result = 0;
ui32_result = 2 * ui32A + 2 * ui32B;
return ui32_result;
}
13.11.1 References
[Misra2012] Rule 2.7 There should be no unused parameters in functions.
[Cert] Rule EXP37-C Call functions with the correct number and type of arguments.
Information
The -Wformat=2 option, required by subsection 5.3.1, allows the compiler to extend
its checks to arguments of variadic functions.
When NULL is passed to a classic function, NULL is converted to the correct type. This type conver-
sion does not work with the variadic functions since the “right type” is not known. In particular,
the standard allows NULL to be an integer constant or a pointer constant so on platforms where NULL
is also an integer constant, passing NULL for variadic functions can lead to undefined behaviour.
RULE
RULE — Do not call variadic functions with NULL as an argument
150
Bad example
...
unsigned char *string = NULL;
printf("%s %d\n", string , 1); /* undefined behaviour */
...
13.12.1 References
[Misra2012] The features of <stdarg.h> shall not be used.
[Cert] Rec. DCL10-C Maintain the contract between the writer and the caller of variadic functions.
[Cert] Rec. DCL11-C Understand the type issues associated with variadic functions.
[Cert] Rule EXP47-C Do not call va_arg with an argument of the incorrect type.
[Cert] Rule MSC39-C Do not call va_arg on a va_list that has an indeterminate value.
[Cwe] CWE-628 Function call with incorrectly specified arguments.
[IsoSecu] Calling functions with incorrect arguments [argcomp].
RULE
RULE — Use of the comma prohibited for statement sequences
151 The comma is not authorised when sequencing code statements.
The comma must be replaced by a semicolon for statement sequences. This means that:
n braces become necessary;
n the parameters of for loops must be reorganised.
Bad example
The following example makes use of the comma in expressions. It is not possible to
know the result of these statements when reading the code.
int32_t i = (j = 2, 1);
y = x ? (a++, a + 4) : c;
z = 3 * b + 2, 7 * c + 42;
a = (b = 2, c = 3, d = 4);
for(i = 0, j = SZ_MAX; i < SZ_MAX; i++, j--) {
...
}
Good example
In the following code, the comma is only used for the declaration of variables.
int32_t i, j;
i=1;
j=2;
if (0 != x) {
a++;
y = a + 4;
} else {
y = c;
}
z = 3 * b + 2;
j = SZ_MAX;
14.1.1 References
[Misra2012] Rule 12.3: The comma operator shall not be used.
RECO
RECOMMENDATION — The prefix operators ++ and -- should not be used
152 Pre-increment and pre-decrement operators will not be used.
RECO
RECOMMENDATION — No combined use of postfix operators with other
153
operators
Post-increment and post-decrement operators should not be mixed with other oper-
ators.
Lastly, and again for readability reasons, it is recommended not to use combined assignment oper-
ators (>>=, &=, *=, etc.).
RECO
RECOMMENDATION — Avoid the use of combined assignment operators
154
Bad example
The code below uses postfix operators mixed with other operators. The behaviour
of this code is not specified. It depends on the compiler used.
13. The choice of postfix operators can be discussed since the two operators used in isolation are equivalent.
...
uint32_t x;
uint8_t b[TAB_SIZE] = { 0 };
uint16_t i = 0;
x = foo(i++, i); /* not specified : problem with the order of evaluation of the
parameters */
...
Good example
In the following example, postfix operators are used in isolated statements.
# define TAB_SIZE 25U
...
uint32_t x;
uint8_t b[TAB_SIZE] = { 0 };
uint16_t i = 0;
i++; /* N.B. replacing this statement with ++i; would not change the behaviour of
the program in any way */
x = foo(i, i);
...
14.2.1 References
[Misra2012] Rule 13.3 A full expression containing an increment or decrement operator should
have no other potential side effects other than that caused by the increment or decrement operator.
[Cert] Rule EXP30-C Do not depend on the order of evaluation for side effects.
However, when the ternary operator is used with a complex conditional expression, or if several
ternary operators are nested, understanding the code and its maintenance becomes difficult.
RULE
RULE — No nested use of the ternary operator ?:
155 The nesting of ternary operators ?: is prohibited.
RULE
RULE — Correct construction of the expressions with the ternary opera-
156
tor ?:
The expressions resulting from the ternary operator ?: must be exactly of the same
type to avoid any type conversion.
Bad example
In the following example, the nested ternary expression makes it difficult to under-
stand the code, and the expressions of the two branches are not of the same type.
y = (x <42) ? 1042 : (t> 0) ? -1042 : 0.0;
/* integer and float , therefore implicit cast */
Good example
The following example uses several if-else conditionals in order to handle the as-
signment of a value to the variable y which depends on several conditions.
if(x < 42) {
y = 1042;
} else {
if(t > 0) {
y = -1042;
} else {
y = 0;
}
}
14.3.1 References
RULE
RULE — Dynamically allocate sufficient memory space for the allocated
157
object
For a ptr pointer, it is preferable to use ptr=malloc(sizeof(*ptr)); wherever pos-
sible.
RULE
RULE — Free dynamically-allocated memory as soon as possible
158 Any dynamically-allocated memory space must be freed up when it is no longer
needed.
For objects storing sensitive data, the memory areas must be reset before being freed.
RULE
RULE — Sensitive memory areas must be reset before being freed
159
Warning
It is crucial to ensure that this memory reset code is not optimised and is retained on
compilation. Most compilers consider this reset as dead code since the associated vari-
ables are not used afterwards. Generally speaking, the levels of optimisation should
not be pushed at compilation but sometimes, even at a low level of optimisation, one
must unfortunately recode one’s own memset to avoid this kind of inconvenience.
It is also important to note that memory release is only authorised for dynamically-allocated ob-
jects.
Finally, realloc must not be used to modify memory space allocated dynamically. This function
can in fact change the memory space allocated to an object by increasing or decreasing its size, but
can also free the memory of the object passed as parameter. Because of the risks associated with
memory handling or the potential double memory release corresponding to undefined behaviour,
use of this function should be avoided.
RULE
RULE — Do not change the dynamic allocation via realloc
161
Warning
If this fails, the realloc function returns NULL, but the initial memory location re-
mains intact and is therefore still accessible.
Bad example
# include <stdlib.h>
void fonc(size_t len){
long *p;
p = (long *) malloc(len *sizeof(int)); /* incorrect type */
...
p = (long *) realloc(p,0); /* release of p via realloc */
...
free(p); /* double release of p */
}
Good example
# include <stdlib.h>
void fonc(size_t len){
long *p;
p = (long *) malloc(len *sizeof(long)); /* corrected type */
...
free(p); /* realloc deleted and replaced by free */
}
15.1.1 References
[Cert] Rec. MEM00-C Allocate and free memory in the same module, at the same level of abstrac-
tion.
[Cert] Rule MEM31-C Free dynamically allocated memory when no longer needed.
[Cert] Rule MEM34-C Only free memory allocated dynamically.
[Cert] Rule MEM35-C Allocate sufficient memory for an object.
[Cert] Rule MEM36-C Do not modify the alignment of objects by calling realloc.
[Cert] Rec. MEM03-C Clear sensitive information stored in reusable resources.
It is preferable to use the object type and not the identifier of a variable as a parameter of the
sizeof operator. Using the identifier has the advantage of being “resistant” to the associated type
change, but it is then necessary to ensure that the use of the sizeof operator is correct.
In order to avoid problems related to the alignment of the members of a structure, it is common
to use:
n either the pre-compilation directive pack;
Warning
The pack directive is not standard.
RULE
RULE — Correct use of the sizeof operator
162 An expression contained in a sizeof must not:
n contain the operator “=”, since the expression will not be evaluated;
Warning
The sizeof operator does not return the size of the object, but the size used in mem-
ory.
Bad example
The following example shows incorrect use of the sizeof operator.
uint8_t tab[LEN];
typedef struct s_example {
uint32_t ui_field1;
uint8_t ui_field2;
} t_example;
int32_t i, isize;
t_exemple test;
t_exemple* ptr;
i = 5;
ptr = NULL;
isize = sizeof(i = 1234); /* the expression i= 1234 will not be processed */
/* i has the value 5, and not 1234. isize equals 4 */
isize = sizeof(t_example); /* the value returned by sizeof is 8 for 32- bit
alignment */
isize = sizeof (*ptr); /* the expression sizeof (* ptr) returns the size of the
structure t_example */
void crawl_tab(uint8_t tab [])
{
for (size_t i = 0; i < sizeof(tab) / sizeof(tab [0]); i++) /* tab is therefore a
pointer type parameter ! */
...
}
Tolerated example
The following example makes good use of the alignment directive and does not use
an expression as a parameter of the call to the sizeof operator.
# pragma pack(push , 1) /* 1 byte alignment - NOT STANDARD - */
uint8_t tab[LEN];
typedef struct s_example
{
uint32_t ui_field1;
uint8_t ui_field2;
} t_exemple;
# pragma pack(pop) /* return default alignment */
int32_t i;
size_t isize;
15.2.1 References
[Cert] Rec. EXP09-C Use sizeof to determine the size of a type of a variable.
[Cert] Rule EXP44-C Do not rely on side effects in operand to sizeof, _Alignof, or _Generic.
[Cert] Rec. ARR01-C Do not apply the sizeof operator to a pointer when taking the size of an array.
[Misra2012] Rule 13.6 The operand of the sizeof operator shall not contain any expression which
has potential side effects.
[IsoSecu] Taking the size of a pointer to determine the size of the pointed-to type [sizeofptr].
[Cwe] CWE-131 Incorrect Calculation of Buffer Size.
[Cwe] CWE-467 Use of sizeof() on a pointer type.
[Cwe] CWE-805 Buffer access with incorrect length value.
Usually, if allocation fails, the memory allocation function returns a NULL pointer. It is therefore
necessary to check that the pointer returned by the allocation function is not NULL.
If the allocation function behaves differently on an allocation error, refer to the documentation of
the function for how to handle the error appropriately.
RULE
RULE — Mandatory verification of the success of a memory allocation
163 The success of a memory allocation must always be checked.
This rule is a special case of section 13.7 but with special attention paid to the handling of memory
allocation errors.
Good example
In the following code, the success of the memory allocation is checked before the
pointer is used.
point_t *p_point = NULL;
if (NULL != p_point) {
p_point ->x = 0.0f;
p_point ->y = 0.0f;
} else {
/* error handling */
}
It is therefore necessary to associate memory areas with their use: data representing different
values is stored in separate memory spaces. If a shared memory area is recycled, make sure that it
is erased before being reused.
All memory areas that contain sensitive data must be explicitly deleted once the program no longer
needs to access this data.
Warning
Clearing buffers so that data does not remain on the stack via a memset, for example,
may be considered unnecessary by the compiler and the associated calls can therefore
be deleted in order to optimise the code. The developer should be aware of this risk
and consult the documentation of the compiler used in order to ensure that the calls
in question are properly stored.
RULE
RULE — Sensitive data must be isolated
164 Check the correct use of a memory area storing sensitive data, i.e. minimise memory
exposure, minimise copying and delete the area(s) that contained the sensitive data
as soon as possible.
14. There are many illustrations of this: Meltdown, Spectre, ZombieLoad, etc.
# include <stdlib.h>
/* buffer contains 4 bytes of the initialisation vector and the last 12 bytes of
the key */
memcpy(buffer , init , min(init_size , WORK_SIZE));
printIV(buffer);
...
/* no secure deletion */
}
Good example
The following example shows a partition between the key and the initialisation vec-
tor.
# define KEY_SIZE 16U
# define IV_SIZE 4U
# include <stdlib.h>
15.4.1 References
RULE
RULE — Initialise and view the value of errno before and after any exe-
165
cution of a standard library function that changes its value
Bad example
# include <stdlib.h>
void try1 (const unsigned char * len)
{
unsigned long res;
res = strtoul(len ,NULL ,5); /* conversion character string to unsigned long */
/* the function strtoul writes in errno */
if (res == ULONG_MAX)
{
/* problem management */
}
...
}
Good example
# include <stdlib.h>
# include <errno.h>
void try1 (const unsigned char * len)
{
unsigned long res;
errno = 0; /* init errno */
res = strtoul(len ,NULL ,5); /* conversion character string to unsigned long */
/* strtoul written in errno */
if (res == ULONG_MAX && errno != 0) /* errno reading */
{
/* problem management */
}
...
}
RULE
RULE — All errors returned by standard library functions must be handled
166 Any function return must be read in order to set up the appropriate processing fol-
lowing the execution of the function.
This rule is a specific case of section 13.7 but with special attention paid to the error management
of standard library functions.
Bad example
# include <stdio.h>
# include <stdlib.h>
int main(void)
{
FILE *fp = fopen("myfile.txt", "w"); /* returned value not read */
fputs("hello\n", fp);
...
}
Good example
# include <stdio.h>
# include <stdlib.h>
int main(void)
{
FILE *fp = fopen("myfile.txt", "w");
if (fp != NULL) {
fputs("hello\n", fp);
...
}
}
A documentation template for error codes must be defined. This should contain, for each return
code, the associated error and, in the event that several error codes may occur at the same time,
the priority between these codes must be specified for error management.
RULE
RULE — Error code documentation
167 All error codes returned by a function must be documented. If several error codes
can be returned at the same time by the function, the documentation must define
the priority for handling these codes.
Error codes must contain information. Without structuring, the information indicated by the re-
turn code is often insufficient. The structuring of return codes via masks is one possibility. The
return codes must also be structured in such a way that it can be determined whether the value
comes from a normal execution of the function, or on the contrary whether an external element
has interfered (buffer overflow, etc.).
RECO
RECOMMENDATION — Structuring of return codes
168 The return codes must be structured in such a way that information on the progress
of the function called can be obtained easily:
n error;
n error type;
n alarm;
n alarm type;
n ok;
16.3.1 References
[Cert] Rec. ERR00-C Adopt and implement a consistent and comprehensive error-handling policy.
n on Linux, the shell authorises a value between 0 and 255 (even if some codes are reserved for
signals; the value is accessible via the variable $?).
The use of a return code between 0 and 127 protects against the risks of modification (by type
conversion) or misinterpretation of the return code of a program:
n values between 0 and 127 can be coded over 7 bits;
n they have the same coding whether the integer type is signed or unsigned.
RULE
RULE — Return code of a C program according to the result of its execution
169 The return code of a C program must have a meaning in order to indicate that the
program has run correctly or that an error has occurred:
n the value of the return code must be between 0 and 127;
n the value 0 indicates that the program was executed without errors;
n the value 2 is generally used in Unix to indicate an error in the arguments passed
as parameters for the program.
The meaning of the program’s return codes must be indicated in its documentation.
Bad example
The following code presents a portability problem between Windows and Linux. The
value -1 is in fact converted to 255 on Linux with the bash shell.
int main(int argc , char* argv []) {
if (argc != 2) {
/* incorrect number of arguments */
return -1; /* the return code will not be interpreted correctly on Linux */
}
...
return 0;
Good example
In the following example, the return codes do not pose a portability problem.
# define RESULT_OK (0U)
# define ARG_ERROR (2U)
RECO
RECOMMENDATION — Give preference to error returns via return codes in
170
the main function
A C program must have a minimum main() function. Error returns are made by a
dedicated (and therefore documented) code return of this function.
RULE
RULE — Do not use the abort() or _Exit() functions
171
The exit() function results in a normal termination of the program and is not dependent on
the implementation. This exit from the program can be used, but excessively frequent use of this
function in the program can make it difficult to understand.
RECO
RECOMMENDATION — Limit calls to exit()
172 Calls to the exit() function must be commented and not overused. The developer
should replace them wherever possible with an error code return in the main func-
tion.
RULE
RULE — Do not use the setjmp() and longjump() functions
173
Bad example
# include <stdlib.h>
# include <stdio.h>
int read_file(void)
{
FILE *f = fopen("C:\\ myfile.txt", "w");
if (NULL == f)
{
/* problem when opening file */
_Exit (12); /* not authorized */
}
fprintf(f, "%s", "blablabla");
...
abort (); /* not authorized */
...
return 0;
}
int main(void)
{
int val = read_file ();
...
return 1;
}
Good example
# include <stdlib.h>
# include <stdio.h>
int read_file(void)
{
FILE *f = fopen("C:\\ myfile.txt", "w");
if (NULL == f)
{
/* problem when opening file */
return 12; /* error code documented for this problem */
}
fprintf(f, "%s", "blablabla");
...
return 10; /* other documented error code */
...
return 0; /* no problem */
}
int main(void)
{
int val = read_file ();
if (val == 0)
{ /* no problem in the function */
...
return 0;
}
else
{ /* error handling according to the error code */
...
return 1;
16.5.1 References
[Cert] Rule SIG30-C Call only asynchronous functions with signal handlers.
[Cert] ERR00-C Adopt and implement a consistent and comprehensive error-handling policy.
[Cert] ERR04-C Choose an appropriate termination strategy.
[Cert] ERR06-C Understand termination behavior of assert() and abort().
[IsoSecu] Calling functions in the C Standard Library other than abort, _Exit and signal from
within a signal handler [asyncsig].
[IsoSecu] Calling signal from interruptible signal handlers [sigcall].
[Cwe] CWE-479 Signal Handler Use of a Non-reentrant Function.
n setjmp.h;
n stdarg.h.
Therefore, these header files must not be used as they violate several of the above rules.
The library <stdarg.h>, for example, introduced in the C90 standard, declares a type and defines 3
macros: va_start, va_arg, va_end. A new macro (va_copy) is introduced with C99. The purpose
of this library is to allow the definition of functions with a variable number and type of arguments.
In addition, the use of these features can, in many cases, lead to undefined behaviour.
Inconsistent typing in the call of a variadic function can lead to an unexpected termination of the
function or even undefined behaviour.
RULE
RULE — Do not use the setjmp.h and stdarg.h standard libraries
174
17.1.1 References
[Misra2012] Rule 17.1 The features of <stdarg.h> shall not be used.
[Cert] Rec. DCL10-C Maintain the contract between the writer and caller of variadic functions.
[Cert] Rec. DCL11-C Understand the type issues associated with variadic functions.
[Cert] Rule MSC39-C Do not call va_arg() on a va_list that has an indeterminate value.
[Misra2012] 21.4 The standard header file <setjmp.h> shall not be used.
[Cert] MSC22-C Use the setjmp(), longjmp() facility securely.
[Cert] ERR04-C Choose an appropriate termination strategy.
[Cert] ERR05-C Application independent code should provide error detection without dictating
error handling.
RECO
RECOMMENDATION — Limit the use of standard libraries handling floating-
175
point numbers
The standard libraries float.h, complex.h, fenv.h and math.h should only be used
if absolutely necessary as in the case of digital processing.
17.2.1 References
[Misra2012] 21.11 The standard header file <tgmath.h> shall not be used.
[Misra2012] 21.12 The standard header file <fenv.h> shall not be used.
[Cert] FLP32-C Prevent or detect domain and range errors in math functions.
[Cert] FLP03-C Detect and handle floating point errors.
[Cwe] CWE-682 Incorrect calculation.
RULE
RULE — Do not use the functions atoi(), atol(), atof()
176
and atoll() from the library stdlib.h
Equivalent functions strto*() have to be used.
The rand() function of the standard library for pseudo-random number generation does not give
any guarantee as to the quality of the generated randomness.
RULE
RULE — Do not use the rand() function of the standard library
177
17.3.1 References
[Misra2012] The atof, atoi, atol and atoll functions shall not be used.
[Cert] ERR07-C Prefer functions that support error checking over equivalent functions that don’t.
[Cert] Rec MSC25-C Do not use insecure or weak cryptographic algorithms.
Warning
We refer to “less dangerous” versions because such functions for instance add a limit
to the size of an input parameter, but they can still result in undefined or unspecified
behaviours.
Information
In later versions of the C language (especially for C11), new and genuinely more
secure versions are proposed such as strcpy_s().
String handling functions of the type strxx will be replaced by the equivalent functions strnxx
when it is possible to limit the number of characters to be handled.
RULE
RULE — Use the “more secure” versions for standard library functions
178 When different versions of functions from the standard library exist, the “more se-
cure” version must be used.
Likewise, all obsolete or outdated functions must not be used. The best known example is that
of the gets() function, deprecated in the third technical patch of C99 [AnsiC99] and which was
removed from subsequent standards.
RULE
RULE — Do not use obsolete library functions or those which become ob-
179
solete in subsequent standards
RULE
RULE — Do not use library functions that handle buffers without taking the
180
buffer size as an argument
RECO
RECOMMENDATION — Indentation of long expressions
182 When a statement or expression is spread over several lines, indentation is essential
in order to facilitate understanding of the code. compréhension du code.
Bad example
The code in the following example should be re-indented in order to make it easier
to understand.
if(( OPT_1 == opt)
|| ((c >= a) && (0 xdeafbeef == b)
&& (NULL == p_point)))
{
/* processing */
}
if(0 != a_function_name_really_to_extend_to_be_as_explicit_as_possible (
A_CONSTANT_ALWAYS_WITH_A_VERY_EXPLICIT_NAME ,
A_SECOND_CONSTANT_ALWAYS_WITH_A_NAME_TO_EXTEND , 5, 50))
{
/* processing */
}
Unreachable code
Code is considered unreachable if there is no input that allows this point of the pro-
gram to be reached (statements in an always false conditional, statements located
after a return statement, etc.).
Dead code
“Dead code” is understood to mean code for which the execution has no effect (no
modification of variables, no impact on the control flow, etc.).
Furthermore, from a security point of view, dead or unreachable code can be used during a bypass
of the execution stream. This unreachable code may be debugging code, disabling security checks.
RULE
RULE — Identify and remove any dead code
183
RULE
RULE — The code must have no unreachable code other than defensive
184
code and interface code
There must never be any unreachable code, except for defensive code or interface
code, and in both cases it must be specified as a comment.
When running the tests, a dynamic analysis can be performed to identify memory leaks. Code
coverage should also be measured in order to identify parts of the software that have not been
tested.
RECO
RECOMMENDATION — Tool-based evaluation of the source code to limit
185
the risk of execution errors
The source code of the software should be analysed using at least one code analysis
tool. The results produced by the analysis tool should be studied by the developer
and corrections must be made in relation to the problems discovered.
18.4.1 References
[Misra2012] Rule 1.3 There shall be no occurrence of undefined or critical unspecified behaviour.
[Misra2012] Dir. 4.1: Run-time failures shall be minimized.
Cyclomatic complexity
Cyclomatic complexity is a metric that measures the structural complexity of a com-
puter program (module, function). It corresponds to the number of existing paths.
In the event of significant cyclomatic complexity, the code should be reorganised in order to sim-
plify it. This can be done, for example, by writing additional functions.
RECO
RECOMMENDATION — Limitation of cyclomatic complexity
186 The cyclomatic complexity of a function should be limited as far as possible.
RECO
RECOMMENDATION — Limitation of the length and complexity of a func-
187
tion
A function should ideally be associated with a single process and should therefore
correspond to a reasonable number of lines of code.
A developer may inadvertently use C++ keywords (for example: class, new, private, public,
delete, etc.) within a C code, whether to name a function, variable or something else. How-
ever, this hinders proofreading of code, and risks confusing the analysis tools. Moreover, this can
hamper maintenance and can cause compilation problems if the compiler also includes C++. A
search for these keywords in the sources of a C program can be easily automated. When one of the
keywords is found, the name of the variable, type or function must be changed.
RULE
RULE — Do not use C++ keywords
188 No C++ keywords must be used in the source code of a C program.
typedef struct
{
float x;
float y;
} point_t;
point_t *new();
Good example
In the following example, no C++ keywords are used.
/* point .h */
typedef struct
{
float x;
float y;
} point_t;
point_t *new_point ();
void delete_point(point_t *p);
In C99, the notation of comments on a line is extended with the following format:
// comments on a single line
The character sequences /* and // are prohibited in all comments, and the line splicing character
\ is prohibited in a comment introduced by // because it leads to undefined behaviour.
RULE
RULE — Prohibited character sequences in comments
189 The /* and // sequences are prohibited in all comments. And a comment on a line
introduced by // must not contain a line splicing character \.
19.1.1 References
[Misra2012] Rule 3.1 The characters sequences /* and // shall not be used within a comment.
[Misra2012] Rule 3.2 Line-splicing shall not be used in // comments.
If the toolchain does not support automatic insertion of canaries, such a mechanism must be
implemented by the developer himself. This can be achieved by passing an additional argument
to each critical function and verifying its value at the beginning and at the end of this function, as
illustrated in the code sample hereinbelow.
When this is not feasible, it is still possible to undertake a thorough analysis of the source code in
order to, for example, guarantee that no local arrays are used, to avoid any control flow hijacking
due to a stack buffer overflow.
Warning
Preference should be given to the “automatic” use of canaries by means of the
toolchain. Indeed, developing a canary mechanism remains a complicated task, of-
ten prone to programming errors or even vulnerabilities.
Good example
The keyword volatile is used to prevent possible optimisations of the compiler for
access to the values of the canary and canaryRef variables. In fact, it is necessary to
systematically go and read the canary and canaryRef values in memory.
typedef volatile uint32_t fid_t;
#ifdef ACTIVATE_CANARIES
static inline void verifcanari(fid_t canari , fid_t canariRef) {
uint8_t res = !!( canari != canariRef);
if (0 != res)
{
/* context - specific processing */
}
}
#else /* ifdef ACTIVATE_CANARIES */
static inline void verifcanari(fid_t canari , fid_t canariRef) { }
#endif /* ifdef ACTIVATE_CANARIES */
void foo(fid_t canari) {
/* checking of the canary parameter at the beginning of the function */
verifcanari(canari , FID_FOO);
n assertions designed to check the integrity of the software during execution: these are intended
to ensure that the software runs normally and to detect a hardware failure or an attempt to
modify it externally (e.g. a fault attack).
A software integrity assertion should not be written using the macro assert(). Indeed, this macro
is deleted from the code generated on compilation in release mode. Furthermore, these assertions
It may be that a code checking the integrity of a software program is detected as code unreachable
by the compiler or a static analysis tool (in fact, the code can check a set of conditions that cannot
occur during normal program execution). The purpose of this code must be clearly documented,
and it must be ensured that compiler optimisations do not result in the deletion of this code in the
generated binary.
RULE
RULE — No development assertion on a code in production
191 Development assertions must not be present in production.
RECO
RECOMMENDATION — Management of integrity assertions should include
192
emergency data deletion
Integrity assertions should appear in production. If an integrity assertion is triggered,
the processing code should result in the emergency deletion of sensitive data.
19.4 Last line of a non-empty file must end with a line break
The absence of a line break at the end of a non-empty file leads to undefined behaviour according
to the C90 and C99 standards.
Warning
The vast majority of publishers, particularly in a Linux environment, automatically
and invisibly add this line break when closing files.
RULE
RULE — All non-empty files must end with a line break and the preprocessor
193
directives and comments must be closed
A non-empty file must not end in the middle of a comment or preprocessor directive.
Information
Option -ansi is equivalent to option -std=c90, which is itself equivalent to -std=c89
and -std=iso9899:1990.
Information
Option -std=iso9899:199409 corresponds to ISO C90 as modified in amendment 1
in 1995.
Information
Option -std=iso9899:1999 is equivalent to -std=c99.
n -Wbad-function-cast
n -Wcast-align
n -Wfloat-equal
n -Wnull-dereference
n -Wshadow (warns whenever a local variable or type declaration reuses an identifier that is already
bound to another variable, parameter or type)
n -Wstack-protector (warns about functions that are not instrumented with a stack canary)
n -Wstrict-prototypes
n -Wmissing-prototypes
n -Wundef
n -Wvla
The following options are specific to GCC:
n -Wduplicated-branches
n -Wduplicated-cond
n -Wformat-signedness
n -Wjump-misses-init
n -Wnested-externs (warns on declarations that use the extern storage class specifier within a
function)
n -Wold-style-definition
n -Wshift-negative-value
n -Wshift-overflow=2
n -Wstrict-overflow=3 (warns about a number of cases where the compiler optimizes based on
the assumption that signed overflow does not occur)
n -Wsuggest-attribute=format (warns for cases where adding a format GCC attribute may be
beneficial)
n -Wsuggest-attribute=malloc (warns for cases where adding a malloc GCC attribute may be
beneficial)
n -Wswitch-default (warns whenever a switch statement does not have a default case)
n -Wwrite-strings (adds type qualifier const to constant strings so that copying the address of
one into a pointer to a non-const-qualified type produces a warning; this helps the developer
to find at compile-time code that tries to write into a string constant — provided that the const
keyword has indeed been used in declarations and prototypes, otherwise this warning becomes
unhelpfully very noisy)
n -Wcast-function-type
n -Wcomma
n -Wcovered-switch-default
n -Wduplicate-enum
n -Wloop-analysis
n -Wformat-non-iso
n -Wformat-pedantic
n -Wformat-type-confusion
n -Wfour-char-constants
n -Wimplicit-fallthrough
n -Wpointer-arith
n -Wpragmas
n -Wreserved-identifier
n -Wshift-sign-overflow
n -Wsigned-enum-bitfield
n -Wstatic-in-inline
n -Wtautological-constant-in-range-compare
n -Wthread-safety
n -Wunreachable-code
n -Wunreachable-code-aggressive
n -Wused-but-marked-unused
n -Wvariadic-macros
n -Wzero-as-null-pointer-constant
Information
With CLANG, option -Wall automatically enables option -Wmost, which itself enables
many additional warnings. Therefore, the options that correspond to the latter are
not listed above.
Using -Weverything may be interesting to discover new warnings supported by the compiler or
in case of highest level of requirement on a given code base. It should not be used systematically
though, since it might for instance cause trouble for project builds following a tool update.
Information
The following points are an example of coding conventions. Some choices are arbi-
trary and open for discussion. This example of conventions can be used or taken as
a foundation, if no development convention has been defined for the project to be
produced. Different tools or advanced editors are able to automatically implement
some of these coding conventions.
Where agreements have been defined in the context of the implementation of a project, the docu-
ment clearly specifying these conventions must accompany the project in question.
A file should not exceed 4000 lines (including documentation and comments).
A space character is systematically left between a keyword and the opening parenthesis that follows
it.
The opening brace of a block is placed on a new line. The block closing brace is also placed on a
new line.
The semicolon indicating the end of a statement is stuck to the last operand of the statement.
For a function call with many parameters, if it is necessary to place the parameters on several lines,
these parameters are indented to be positioned at the opening parenthesis of the function call.
Good example
...
uint32_t processing_function (linked_list_t *p_param1 , uint32_t ui32_param2 ,
const unsigned char *s_param3)
{
uint32_t ui32_result = 0;
element_t *pp_out_param4 = NULL;
if (1 == ui32_result)
{
...
}
End:
return ui32_result;
}
If the stdbool.h header is present, it must be included in order to benefit from the boolean type it
defines. In its absence, the bool type must be defined as presented on the following code (header
file from GCC version 4.8.2). The _Bool type is defined for the compilers compatible with standard
C99 and subsequent standards.
/* Copyright (C) 1998 -2013 Free Software Foundation , Inc. */
# ifndef _STDBOOL_H
# define _STDBOOL_H
# ifndef __cplusplus
#else // __cplusplus
# endif // __cplusplus
# endif // stdbool .h
E.4 Naming
E.4.1 Language for implementation
The language used for naming libraries, header files, source files, macros, types, variables and func-
tions must be English. This use of English avoids mixing words in French with the keywords of the
C language which are in English within the code. The entire source code produced is thus more
coherent.
The language used for documentation and comments should be English from the beginning of
development and for all documentation and comments.
The following example shows the organisation of directories for a library containing utility func-
tions:
The following list gives examples of header file and source file names: utils_linked_list.h,
utils_linked_list.c, utils_mutex.h, utils_mutex.c, utils_thread.h, utils_thread.c …
Good example
# define LOG_DEBUG(sMessage) write_log_message(sMessage)
The name of a macro, defined to avoid the multiple inclusion of a header file, uses the name of the
header file in upper case. The full stop character is replaced by an underscore character.
Good example
# define UTILS_LINKED_LIST_H
When defining a type for an enumeration or structure, the name following the keyword enum or
struct must have the suffix _tag. The name of the type after the closing brace defining the type
must be the same name, with _tag replaced by _t.
Good example
status_t utils_create_linked_list (linked_list_t ** pp_list);
status_t utils_delete_linked_list (linked_list_t *pp_list);
The following table shows the prefixes for the variable names according to type, as well as an
example for each type of variable:
E.5 Documentation
E.5.1 Format of tags for documentation
The source code documentation must be produced using the Doxygen tool’s tag system. Doxygen
tags must all begin with the @ character. The Doxygen tool also allows the backslash character.
However, in order to have uniformity for the documentation of the source code, the at-sign prefix
for Doxygen commands is imposed.
The following points outline the minimum documentation that must be present in a header file.
n the company (and if necessary the author) and copyright associated with the file;
n the Doxygen @file tag. The @file tag can optionally be followed by the file name. In the
absence of the file name, the file name is automatically deduced from the file in which the
@file tag is located.
It is essential to use the @file tag in the header files and the source files. In its absence the
documentation on functions, global variables, type definitions and enumerations present in the
file is not included in the Doxygen documentation produced.
If the file is part of a library, the command @addtogroup <label> [title] must be used. This
serves to group the documentation of all the functions of a library within a module in the docu-
mentation produced. The label is the name of the group to be used in all files belonging to the
library. The title is optional. It is used to name the group in the documentation.
The @addtogroup command must be supplemented by the @{ and @} tag pair in order to delimit
the elements of the file belonging to the group.
n the presentation of each parameter, specifying whether it is an input, output or both input and
output parameter;
n the value returned by the function. In the case of an error code, the success case(s) must be
indicated, along with the different error codes that can be returned and their priority;
Good example
The following lines show the minimum documentation for a header file.
# ifndef UTILS_LINKED_LIST_H
# define UTILS_LINKED_LIST_H
/*!
* @file linked_list .h
* @author DEV 1
*
* @brief Linked List
*
* Function declarations for the manipulation of linked list.
*
* @addtogroup utils Library Utils
* @{
*/
/*!
* @brief Enumeration of status codes
*
* Status codes to indicate the success or the failure of functions
*/
typedef enum status_tag {
STATUS_SUCCESS = 0, //!< success
/*!
* @brief Element of the linked list
*/
typedef struct linked_list_element_tag
{
struct linked_list_element_tag * pNext; //!< next element
struct linked_list_element_tag * pPrevious; //!< previous element
void* pData; //!< data of the element
} linked_list_element_t ;
/*!
* @brief Double linked list
*
* Structure to define a double linked list. The type data of the list is void.
*/
typedef struct linked_list_tag
{
linked_list_element_t *pHead; //!< first element
linked_list_element_t *pTail; //!< last element
} linked_list_t;
/*!
* @brief New linked list
*
* Creation of a new linked list by allocating the memory for the structure and by
initializing the list.
* The new list is empty.
*
* @param [out] ppList is the new list
* @return # STATUS_SUCCESS the creation and the initialization are done with
success
* @return # STATUS_INVALID_PARAM if ppList is NULL or
* if (* ppList ) != NULL
* @return # STATUS_MEMORY_ERROR fail of the memory allocation
* @pre ppList != NULL and (* ppList ) == NULL
* @note the created list has to be deleted
* by calling # utils_delete_linked_list
*/
status_t utils_create_linked_list (linked_list_t ** ppList);
/*!
* @brief Deletion of the list
*
* All the elements of the list are deleted and the used memory is freed .
* @warning The memory used by the data in the list is not freed ..
*
* @param [in , out] ppList the list to delete .
* @return # STATUS_SUCCESS if the deletion of the list is a success
* @return # STATUS_INVALID_PARAM if ppList is NULL
* or if (* ppList ) is NULL
* @pre ppList != NULL and (* ppList ) != NULL
* @post (* ppList ) == NULL
*/
status_t utils_delete_linked_list (linked_list_t ** ppList);
...
/*! @} */
# endif // UTILS_LINKED_LIST_H
168
préprocesseur, 11 typedef, 46
[Misra2012] MISRA-C:2012 Guidelines for the use of the C language in critical systems.
Guidelines, https://www.misra.org.uk/MISRAHome/MISRAC2012.