Code Structure¶
A code structure test analyzes the structure of a translation unit’s source code and enforces the predefined rules. Each translation unit requires its own set of rules. If the analyzed code violates at least one rule, the test is failed, and feedback on what rules have been violated is given. Otherwise, feedback that the code structure test has been passed successfully is given.
There are three different types of rules:
General rules: These rules can be defined on every level.
Global rules: These rules can only be defined at a global level.
Function rules: These rules can only be defined at the function level.
Note
If a rule is defined globally, each function is expected to abide by it, but rules on the function level supersede global definitions within their bodies.
In the following sections, the rules, their meaning, and default values are explained in detail. The remainder of this section lists the different rules for each level.
General rules:
input: allow/disallow/expect
stdininput.output: allow/disallow/expect
stdoutoutput.insecure: allow/disallow/expect
stderroutput.recursion: allow/disallow/expect recursion.
disallowed_keywords: List of disallowed keywords.
expected_keywords: List of expected keywords.
allowed_function_calls: List of allowed function calls.
disallowed_function_calls: List of disallowed function calls.
expected_function_calls: List of expected function calls.
expected_variable_declarations: List of expected variable definitions.
Rules exclusive to the global definition:
required_functions: List of function prototypes defining required functions.
disallowed_includes: List of C-library names that are not allowed.
global_variables: allow/disallow global variable declarations.
compile_args: compiler arguments for clang AST-generation
functions: Key that indicates the start of the list function rule segments.
Additional rules for function level are:
function: function name
A key mechanic of code structure tests is that all keyword and function usages and whether recursive functions are used are propagated to the calling function.
General Rules¶
These rules can be used on both the global and function level. If a rule is defined on the global level, functions must abide by it as well, but functions can supersede rules defined on the global level either partially or fully. The following sections describe all general rules.
input¶
This rule indicates whether input from stdin is allowed, disallowed or expected.
This is achieved by allowing/disallowing or expecting the usage of at least one of the following functions:
Input Functions (click to expand)
fgetcfgetsfgetwcfreadfscanffscanf_sfwscanffwscanf_sgetcgetc_unlockedgetchargetchar_unlockedgetdelimgetlinegetsgets_sgetwgetwcgetwchargetwlinepreadreadreadvscanfscanf_ssscanfsscanf_sswscanfswscanf_svfscanfvfscanf_svfwscanfvfwscanf_svscanfvscanf_svsscanfvsscanf_svswscanfvswscanf_svwscanfvwscanf_swscanfwscanf_s
If defined globally, this rule can be superseded on function level either partially, by defining allowed_function_calls, or expected_function_calls, or as a whole by redefining input.
Note
Please note that expected is not yet implemented! Currently, expected behaves just like allowed.
Possible values:
true: usage of at least one of the functions is expected (this is not implemented yet! currentlytruebehaves just likenull)null: usage of the functions is allowed but not mandatory.false: usage of the functions is disallowed
Example:
input: false
Default value:
If it is not present on the function level, the global settings remain unchanged.
If the keyword input is omitted on the global level, input is allowed.
output¶
This rule indicates whether output on stdout is allowed, disallowed or expected.
This is achieved by allowing/disallowing or expecting the usage of at least one of the following functions:
Output Functions (click to expand)
ferrorfprintffprintf_sfpurgefputcfputsfputwcfputwsfputws_lfreadfreopenfropenfwprintffwriteperrorprintfprintf_sputcputcharputsputwputwcputwcharpwritevasprintfvfprintfvfprintf_svfwprintfvfwprintf_svprintfvprintf_svwprintfvwprintf_swprintfwritewritev
If defined globally, this rule can be superseded on function level either partially, by defining allowed_function_calls or expected_function_calls, or as a whole by redefining output.
Note
Please note that expected is not yet implemented! Currently, expected behaves just like allowed.
Possible values:
true: usage of at least one of the functions is expected (this is not implemented yet! currentlytruebehaves just likenull)null: usage of the functions is allowed but not mandatory.false: usage of the functions is disallowed
Example:
output: null
Default value:
If it is not present on the function level, the global settings remain unchanged.
If the keyword output is omitted on the global level output is allowed.
insecure¶
This rule indicates whether the usage of insecure functions is allowed, disallowed or expected.
Insecure functions are functions that start or kill processes or threads as well as the exec-function family.
This is achieved by allowing/disallowing or expecting the usage of at least one of the following functions:
Insecure Functions (click to expand)
forksystemkillkillpgexeclexecleexeclpexecvexecvpexecvPexecveexecvpefexecvevforkclonetkilltgkillexecveatpthread_createthrd_create
If defined globally, this rule can be superseded on function level either partially, by defining allowed_function_calls or expected_function_calls, or as a whole by redefining insecure.
Note
Please note that expected is not yet implemented! Currently, expected behaves just like allowed.
Possible values:
true: usage of at least one of the functions is expected (this is not implemented yet! currentlytruebehaves just likenull)null: usage of the functions is allowed but not mandatory.false: usage of the functions is disallowed
Example:
insecure: false
Default value:
If it is not present on the function level, the global settings remain unchanged.
If the keyword insecure is omitted on the global level, insecure functions are is allowed.
recursion¶
This rule indicates whether recursion is allowed, disallowed, or expected.
If defined globally, this rule can be superseded on function level by redefining recursion.
Possible values:
true: recursion is expected. If used globally, this means that every function has to be recursive. If used on the function level, the current function has to be recursive.null: recursion is is allowed but not mandatory.false: recursion is disallowed.
Example:
recursion: true
Default value:
If it is not present on the function level, the global settings remain unchanged.
If the keyword recursion is omitted on the global level, recursion is allowed.
disallowed_keywords¶
The usage of all keywords listed here is disallowed. If defined globally, this rule can be superseded on the function level by adding globally disallowed keywords to expected_keywords.
Possible values:
A list of c-keywords that is not allowed
Example:
disallowed_keywords:
- for
- while
- do
Default value:
If it is not present on the function level, the global settings remain unchanged.
If disallowed_keywords is omitted on the global level, all keywords are allowed.
expected_keywords¶
The usage of all keywords listed here is expected. If defined globally, this rule can be superseded on the function level by adding globally expected keywords to disallowed_keywords.
Possible values:
A list of c-keywords that is expected
Example:
expected_keywords:
- if
- unsigned
Default value:
If it is not present on the function level, the global settings remain unchanged.
If expected_keywords is omitted on the global level, all keywords are allowed.
allowed_function_calls¶
The usage of all function calls of external functions listed here is allowed. If defined globally, this rule can be superseded on the function level by adding globally allowed function calls to disallowed_function_calls or expected_function_calls.
Possible values:
A list of c-library function calls that is allowed.
Example:
allowed_function_calls:
- printf
- fprintf
Default value:
If it is not present on the function level, the global settings remain unchanged.
If allowed_function_calls is omitted on the global level, all function calls are allowed.
disallowed_function_calls¶
The usage of all function calls of external functions listed here is disallowed. If defined globally, this rule can be superseded on the function level by adding globally disallowed function calls to allowed_function_calls or expected_function_calls.
Possible values:
A list of C-library function calls that are not allowed.
disallowed_function_calls:
- pow
Default value:
If it is not present on the function level, the global settings remain unchanged.
If disallowed_function_calls is omitted on the global level, all function calls are allowed.
expected_function_calls¶
The usage of all function calls of external functions listed here is expected. If defined globally, this rule can be superseded on the function level by adding globally expected function calls to allowed_function_calls or disallowed_function_calls.
Possible values:
A list of C-library function calls that are expected.
expected_function_calls:
- qsort
Default value:
If it is not present on the function level, the global settings remain unchanged.
If expected_function_calls is omitted on the global level, all function calls are allowed.
expected_variable_declarations¶
The variables defined in this list are expected to be defined with the same type in the analyzed code. If defined globally, this means that each function body must contain this variable definition. If defined on function level, only the function body in question must contain the variable definitions. This rule can not be superseded on the function level.
Note
We plan on adding function parameters to the analyzed code for variable declarations. This is not yet implemented.
Possible values:
The list of variable declarations that are expected.
Example:
expected_variable_declarations:
- 'long int n;'
- 'long long value;'
Default value:
If expected_variable_declarations is omitted there are no expected variable declarations.
Global Rules¶
These rules can only be used on the global level, and it is not possible to influence them on the function level. The following sections describe all global rules.
required_functions¶
The function prototypes defined in this list must be present in the analyzed code.
Possible values:
The list of c function prototypes that must be present
Example 1:
required_functions:
- "void foo(int);"
- "int bar(void);"
Example 2:
required_functions:
- "#include \"types.h\" \n void print_person(person_t *person);"
- "#include <stdlib.h> \nvoid my_sort(char array[], size_t length);"
Note
As shown in Example 2, non standard types and types defined with typedef can be loaded via includes. Attention: The types.h file must reside in the same location as the test configuration fie and the Tests.py file.
Alternatively the same required functions can be written as:
required_functions:
- |
#include "types.h"
void print_person(person_t *person);
- |
#include <stdlib.h>
void my_sort(char array[], size_t length);"
Default value:
If required_functions is omitted, there are no functions whose implementation is required.
disallowed_includes¶
The usage of C-library includes listed here is not allowed in the analyzed code.
Possible values:
The list of c-library includes that are disallowed
Example:
disallowed_includes:
- 'stdio.h'
Default value:
If disallowed_includes is omitted, all c-library includes are allowed.
global_variables¶
Indicates whether global variables can be used in the analyzed translation unit or not.
If global_variables is true, the usage of global variables is allowed.
If global_variables is false, the usage of global variables is not allowed.
Example:
global_variables: false
Default value:
If global_variables is omitted, the usage of global variables is not allowed.
compile_args¶
This keyword can be used to define an array containing additional command line arguments that should be passed to clang when it is parsing the syntax tree. For example they can be used to specify include paths or warnings.
Example:
compile_args:
- "-I/path/to/include"
- "-Wall"
functions¶
This key indicates the start of the function rules list. It is required if rules for at least one function are defined. Otherwise, it is disallowed.
Function Rules¶
These rules can only be used on function level. The following sections describe all function rules.
Note
If function f1 calls function f2 then function f2 must also abide to all rules for function f1 because all checks are propagated backward along the call graph.
function¶
This key indicates the name of the function to which the following rules are applied. This key can only be used in the functions list.
Possible values:
The name of the function
Example:
functions:
- function: print_something
output: true
- function: main
output: true
Default value:
The function key can not be omitted if rules for a given function are to be added.
Example code structure test definitions:¶
In this section, a few examples for code structure test definitions are given.
Note
For security reasons, insecure: false is included in each of the following test cases. Omit insecure: false at your own risk.
Example 1 (incorrect): Output via printf is only allowed in function print_hello_world but print_hello_world is called in the function main
1version: "0.0"
2translation_unit: hello_world.c
3tests:
4 - type: structural
5 name: Structural
6 insecure: false
7 output: false
8 required_functions:
9 - "void print_hello_world(void);"
10 functions:
11 - function: print_hello_world
12 expected_function_calls:
13 - printf
14 - function: main
15 expected_function_calls:
16 - print_hello_world
Lets suppose the function print_hello_world calls the printf function. The printf-call triggers an infringement of the global rule output: false because the printf call is propagated backward along the call-graph from print_hello_world to main.
Example 1 (correct): Output is only allowed in functions print_hello_world and main
1version: "0.0"
2translation_unit: hello_world.c
3tests:
4 - type: structural
5 name: Structural
6 insecure: false
7 output: false
8 required_functions:
9 - "void print_hello_world(void);"
10 functions:
11 - function: print_hello_world
12 expected_function_calls:
13 - printf
14 - function: main
15 expected_function_calls:
16 - print_hello_world
17 - printf
Lets suppose the function print_hello_world calls the printf function. In this case the printf-call does not trigger an infringement of the global rule output: false because the rule has been overridden for all functions along the call-graph, in this specific case print_hello_world and main.