Parallelism-UnsafeVarAccess¶
Do not access global variables or static members outside critical region
Required inputs: IR
Detect shared variables, which are accessed from concurrently running tasks or interrupt service routines without being explicitly sequenced via critical sections.
An issue is reported for a shared variable, that is potentially accessed in a way that is unsafe, meaning that it is concurrently read and written by different tasks/ISRs. Such an access is often called data race and may result in subtle misbehavior of the program. Example:
| Non-compliant | Compliant |
|---|---|
struct S global = ...;
void task1(void)
{
struct S local1 = ...;
global = local;
}
void task2(void)
{
struct S local2 = global;
...
}
|
struct S global = ...;
void task1(void)
{
struct S local1 = ...;
DisableAllInterrupts();
global = local;
EnableAllInterrupts();
}
void task2(void)
{
DisableAllInterrupts();
struct S local2 = global;
EnableAllInterrupts();
...
}
|
task1 reads global, storing the
result in local1. This may occur concurrently with the access in
task2 resulting in an undefined result in local2 (assuming the
functions task1 and task2 are concurrent tasks). In the
compliant example, proper mutual exclusion is achieved by the use of
DisableAllInterrupts.
The following model for tasks and interrupt service routines is supported:
- A single-core execution model is supported.
- Tasks and ISRs are defined by the following properties. Refer to option
partitions of this rule to configure them:
Priority Pre-defined static priority of the task/ISR. Higher values indicate higher priority. Entries Set of one or more functions serving as the start functions of the task or ISR. All functions transitively called from these belong to the task/ISR. Guarded Property that indicates that this task/ISR will always run exclusively on the processor. It cannot be interrupted or preempted by another task or ISR. - Synchronization and serialization is possible via a pair of disable/enable
primitives. In the compliant example,
DisableAllInterrupts()andEnableAllInterrupts()was used, but these primitives can be freely configured. Afterdisablewas called, no preemption and no interrupt may occur, untilenableis called by the current task/ISR. Please refer to the options enter_critical_functions, enter_critical_macros, exit_critical_functions, and exit_critical_macros to configure the primitives. - To omit critical sections in highest-priority tasks/ISRs, please enable option
strict_priorities. For example, in the compliant
example, the critical section in
task2could be removed iftask2had higher priority thantask1. - Access to certain global variables can be excluded from the data race check, if the access to those global variables is considered atomic. Refer to option treat_types_as_atomic to configure data types of global variables that are accessed atomically. Also consider options allow_volatile_sig_atomic_t and allow_c11_atomics.
- By default, only pairs of reading and writing access occurring concurrently are considered to cause data race issues, refer to option show_identical_access to also report writing/writing and even reading/reading pairs.
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
multiple_lock_add |
Lock is acquired while it is already locked. |
None |
False |
non_recursive_lock_add |
Non-recursive lock is acquired while it is already locked. |
None |
False |
removed_nonexisting_lock |
Lock is released, although it is not currently locked. |
None |
False |
unbalanced_locks_path |
Different control flow paths have different sets of locks. |
None |
False |
unbalanced_locks_routine |
Routine may return with different lock set than it is entered with ({in_set} vs {out_set}). |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
access_kinds¶
access_kinds : set[bauhaus.ir.LIR_Class_Name] = {'Reading_Operand_Interface', 'Writing_Operand_Interface'}
Reading_Operand_Interface,
Writing_Operand_Interface, Address_Operand_Interface).
allow_c11_atomics¶
allow_c11_atomics : bool = True
allow_volatile_sig_atomic_t¶
allow_volatile_sig_atomic_t : bool = False
volatile sig_atomic_t.
debug_output¶
debug_output : bool = False
enter_critical_functions¶
enter_critical_functions
Set of function names to enter a critical region.Type: set[bauhaus.analysis.config.QualifiedName]
Default:
{'EnterCriticalSection', 'mtx_lock', 'pthread_mutex_lock', 'std::_Mutex_base::lock', 'std::mutex::lock'}
enter_critical_macros¶
enter_critical_macros : set[bauhaus.analysis.config.MacroName] = set()
excluded_routines¶
excluded_routines : set[bauhaus.analysis.config.QualifiedName] = set()
excluded_subgraphs¶
excluded_subgraphs : set[bauhaus.analysis.config.QualifiedName] = set()
exit_critical_functions¶
exit_critical_functions
Set of function names to exit a critical region.Type: set[bauhaus.analysis.config.QualifiedName]
Default:
{'ExitCriticalSection', 'mtx_unlock', 'pthread_mutex_unlock', 'std::_Mutex_base::unlock', 'std::mutex::unlock'}
exit_critical_macros¶
exit_critical_macros : set[bauhaus.analysis.config.MacroName] = set()
inspect_pointers¶
inspect_pointers : bool = False
nested_critical_regions¶
nested_critical_regions : bool = True
output_safe_accesses¶
output_safe_accesses : bool = False
partitions¶
partitions : dict[str, dict[str, typing.Any]] = {}
entries: list of entry functions or this task/isrfunctions_passed_to: name of thread creation function. Any function designated by a pointer passed to that function will be considered an entry function.vectors: list of global variable names with function pointers to entry functions or this task/ISRguarded: boolean property. Set toTrueif this task is nonpreemptive and cannot be interrupted by interrupt handlers. Set toFalseor omit otherwise (default).
__interrupts__ will automatically contain
all interrupt handlers recorded as Additional_Entries in IR (see
compiler toolchain's advanced.main_entries configuration) in addition
to any entries specified in its dict.
report_cfg_based_critical_region_issues¶
report_cfg_based_critical_region_issues : bool = False
show_identical_access¶
show_identical_access : bool = False
show_object_number¶
show_object_number : bool = False
strict_priorities¶
strict_priorities : bool = False
treat_types_as_atomic¶
treat_types_as_atomic : set[typing.Pattern[str] | typing.Tuple[typing.Optional[int], typing.Optional[int], typing.Optional[typing.Pattern[str]]]] = set()