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
stdin
input.output: allow/disallow/expect
stdout
output.insecure: allow/disallow/expect
stderr
output.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)
fgetc
fgets
fgetwc
fread
fscanf
fscanf_s
fwscanf
fwscanf_s
getc
getc_unlocked
getchar
getchar_unlocked
getdelim
getline
gets
gets_s
getw
getwc
getwchar
getwline
pread
read
readv
scanf
scanf_s
sscanf
sscanf_s
swscanf
swscanf_s
vfscanf
vfscanf_s
vfwscanf
vfwscanf_s
vscanf
vscanf_s
vsscanf
vsscanf_s
vswscanf
vswscanf_s
vwscanf
vwscanf_s
wscanf
wscanf_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! currentlytrue
behaves 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)
ferror
fprintf
fprintf_s
fpurge
fputc
fputs
fputwc
fputws
fputws_l
fread
freopen
fropen
fwprintf
fwrite
perror
printf
printf_s
putc
putchar
puts
putw
putwc
putwchar
pwrite
vasprintf
vfprintf
vfprintf_s
vfwprintf
vfwprintf_s
vprintf
vprintf_s
vwprintf
vwprintf_s
wprintf
write
writev
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! currentlytrue
behaves 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)
fork
system
kill
killpg
execl
execle
execlp
execv
execvp
execvP
execve
execvpe
fexecve
vfork
clone
tkill
tgkill
execveat
pthread_create
thrd_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! currentlytrue
behaves 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
.