CWE Rule 413
Description
Rule Description
The software does not lock or does not correctly lock a resource when the software must have exclusive access to the resource.
Polyspace Implementation
The rule checker checks for these issues:
Data race
Data race through standard library function call
Shared data access within signal handler
Examples
Data race
This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).
This issue occurs when:
Multiple tasks perform unprotected operations on a shared variable.
At least one task performs a write operation.
At least one operation is nonatomic. To detect data race on both atomic and nonatomic operations, use the options
-detect-atomic-data-race
. See Extend Data Race Checkers to Atomic Operations.
If you activate this checker without specifying the multitasking options first, you see a warning in the log:
Warning: Checker 'Data Race' is activated but no protection have been defined
To specify these options, on the Configuration pane, select Multitasking. For more information, see Configuring Polyspace Multitasking Analysis Manually.
Data race can result in unpredictable values of the shared variable because you do not control the order of the operations in different tasks.
Data races between two write operations are more serious than data races between a write and read operation. Two write operations can interfere with each other and result in indeterminate values. To identify write-write conflicts, use the filters on the Detail column of the Results List pane. For these conflicts, the Detail column shows the additional line:
Variable value may be altered by write-write concurrent access.
To fix this defect, protect the operations on the shared variable using critical sections, temporal exclusion or another means. See Protections for Shared Variables in Multitasking Code.
To identify existing protections that you can reuse, see the table and graphs associated with the result. The table shows each pair of conflicting calls. The Access Protections column shows existing protections on the calls. To see the function call sequence leading to the conflicts, click the icon. For an example, see below.
Extend this checker to check for data races in operations that Bug Finder might not detect by default. For instance:
You might be using multithreading functions that are not supported by Polyspace. Extend this checker by mapping the functions of your multithreading functions to their known POSIX® equivalent. See Extend Concurrency Defect Checkers to Unsupported Multithreading Environments.
Polyspace assumes that certain operations are atomic and excludes them from data race checks. See Define Atomic Operations in Multitasking Code. These assumptions might not apply to your environment. To extend the data race checkers to include these operations, use the option
-detect-atomic-data-race
. See Extend Data Race Checkers to Atomic Operations.
int var; //Noncompliant void begin_critical_section(void); void end_critical_section(void); void increment(void) { var++; } void task1(void) { increment(); } void task2(void) { increment(); } void task3(void) { begin_critical_section(); increment(); end_critical_section(); }
In this example, to emulate multitasking behavior, specify the following options:
Option | Specification | |
---|---|---|
Configure multitasking manually | ||
Tasks (-entry-points) |
| |
Critical section details (-critical-section-begin -critical-section-end) | Starting routine | Ending routine |
begin_critical_section | end_critical_section |
On the command-line, you can use the following:
polyspace-bug-finder -entry-points task1,task2,task3 -critical-section-begin begin_critical_section:cs1 -critical-section-end end_critical_section:cs1
In this example, the tasks task1
, task2
,
and task3
call the function increment
. increment
contains
the operation var++
that can involve multiple machine
instructions including:
Reading
var
.Writing an increased value to
var
.
These machine instructions, when executed from task1
and task2
,
can occur concurrently in an unpredictable sequence. For example,
reading var
from task1
can occur
either before or after writing to var
from task2
.
Therefore the value of var
can be unpredictable.
Though task3
calls increment
inside
a critical section, other tasks do not use the same critical section.
The operations in the critical section of task3
are
not mutually exclusive with operations in other tasks.
Therefore, the three tasks are operating on a shared variable without common protection. In your result details, you see each pair of conflicting function calls.
If you click the icon, you see the function call sequence starting
from the entry point to the read or write operation. You also see that the operation
starting from task3
is in a critical section. The Access
Protections entry shows the lock and unlock function that begin and end
the critical section. In this example, you see the functions
begin_critical_section
and
end_critical_section
.
One possible correction is to place the operation in critical section. You can implement the critical section in multiple ways. For instance:
You can place
var++
in a critical section. Whentask1
enters its critical section, the other tasks cannot enter their critical sections untiltask1
leaves its critical section. The operationvar++
from the three tasks cannot interfere with each other.To implement the critical section, in the function
increment
, place the operationvar++
between calls tobegin_critical_section
andend_critical_section
.int var; void begin_critical_section(void); void end_critical_section(void); void increment(void) { begin_critical_section(); var++; end_critical_section(); } void task1(void) { increment(); } void task2(void) { increment(); } void task3(void) { increment(); }
You can place the call to
increment
in the same critical section in the three tasks. Whentask1
enters its critical section, the other tasks cannot enter their critical sections untiltask1
leaves its critical section. The calls toincrement
from the three tasks cannot interfere with each other.To implement the critical section, in each of the three tasks, call
increment
between calls tobegin_critical_section
andend_critical_section
.int var; void begin_critical_section(void); void end_critical_section(void); void increment(void) { var++; } void task1(void) { begin_critical_section(); increment(); end_critical_section(); } void task2(void) { begin_critical_section(); increment(); end_critical_section(); } void task3(void) { begin_critical_section(); increment(); end_critical_section(); }
Another possible correction is to make the tasks, task1
, task2
and task3
,
temporally exclusive. Temporally exclusive tasks cannot execute concurrently.
On the Configuration pane, specify the following additional options:
Option | Value |
---|---|
Temporally exclusive tasks (-temporal-exclusions-file) |
|
On the command-line, you can use the following:
polyspace-bug-finder -temporal-exclusions-file "C:\exclusions_file.txt"
C:\exclusions_file.txt
has the following line:
task1 task2 task3
#include <pthread.h> pthread_mutex_t count_mutex; long long count; //Noncompliant void* increment_count(void* args) { count = count + 1; return NULL; } void* set_count(void *args) { long long c; c = count; return NULL; } int main(void) { pthread_t thread_increment; pthread_t thread_get; pthread_create(&thread_increment, NULL, increment_count, NULL); pthread_create(&thread_get, NULL, set_count, NULL); pthread_join(thread_get, NULL); pthread_join(thread_increment, NULL); return 1; }
In this example, Bug Finder detects
the creation of separate threads with pthread_create
. The Data
race defect is raised because the operation count = count +
1
in the thread with id thread_increment
conflicts with the
operation c = count
in the thread with id thread_get
.
The variable count
is accessed in multiple threads without a common
protection.
The two conflicting operations are
nonatomic. The operation c = count
is nonatomic on 32-bit targets. See
Define Atomic Operations in Multitasking Code.
pthread_mutex_lock
and
pthread_mutex_unlock
PairTo prevent concurrent access on the variable count
,
protect operations on count
with a critical
section. Use the functions pthread_mutex_lock
and
pthread_mutex_unlock
to implement the
critical section.
#include <pthread.h> pthread_mutex_t count_mutex; long long count; void* increment_count(void* args) { pthread_mutex_lock(&count_mutex); count = count + 1; pthread_mutex_unlock(&count_mutex); return NULL; } void* set_count(void *args) { long long c; pthread_mutex_lock(&count_mutex); c = count; pthread_mutex_unlock(&count_mutex); return NULL; } int main(void) { pthread_t thread_increment; pthread_t thread_get; pthread_create(&thread_increment, NULL, increment_count, NULL); pthread_create(&thread_get, NULL, set_count, NULL); pthread_join(thread_get, NULL); pthread_join(thread_increment, NULL); return 1; }
Data race through standard library function call
This checker is deactivated in a default Polyspace as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).
This issue occurs when:
Multiple tasks call the same standard library function.
For instance, multiple tasks call the
strerror
function.The calls are not protected using a common protection.
For instance, the calls are not protected by the same critical section.
Functions flagged by this defect are not guaranteed to be reentrant. A function is reentrant if it can be interrupted and safely called again before its previous invocation completes execution. If a function is not reentrant, multiple tasks calling the function without protection can cause concurrency issues. For the list of functions that are flagged, see CON33-C: Avoid race conditions when using library functions.
To find this defect, you must specify the multitasking options before analysis. To specify these options, on the Configuration pane, select Multitasking. For more information, see Configuring Polyspace Multitasking Analysis Manually.
The functions flagged by this defect are nonreentrant because their implementations can use global or static variables. When multiple tasks call the function without protection, the function call from one task can interfere with the call from another task. The two invocations of the function can concurrently access the global or static variables and cause unpredictable results.
The calls can also cause more serious security vulnerabilities, such as abnormal termination, denial-of-service attack, and data integrity violations.
To fix this defect, do one of the following:
Use a reentrant version of the standard library function if it exists.
For instance, instead of
strerror()
, usestrerror_r()
orstrerror_s()
. For alternatives to functions flagged by this defect, see the documentation for CON33-C.Protect the function calls using common critical sections or temporal exclusion.
See
Critical section details (-critical-section-begin -critical-section-end)
andTemporally exclusive tasks (-temporal-exclusions-file)
.To identify existing protections that you can reuse, see the table and graphs associated with the result. The table shows each pair of conflicting calls. The Access Protections column shows existing protections on the calls. To see the function call sequence leading to the conflicts, click the icon. For an example, see below.
#include <errno.h> #include <stdio.h> #include <string.h> void begin_critical_section(void); void end_critical_section(void); FILE *getFilePointer(void); void func(FILE *fp) { fpos_t pos; errno = 0; if (0 != fgetpos(fp, &pos)) { char *errmsg = strerror(errno); //Noncompliant printf("Could not get the file position: %s\n", errmsg); } } void task1(void) { FILE* fptr1 = getFilePointer(); func(fptr1); } void task2(void) { FILE* fptr2 = getFilePointer(); func(fptr2); } void task3(void) { FILE* fptr3 = getFilePointer(); begin_critical_section(); func(fptr3); end_critical_section(); }
In this example, to emulate multitasking behavior, specify the following options:
Option | Specification | |
---|---|---|
Configure multitasking manually | ||
Tasks (-entry-points) |
| |
Critical section details (-critical-section-begin -critical-section-end) | Starting routine | Ending routine |
begin_critical_section | end_critical_section |
On the command-line, you can use the following:
polyspace-bug-finder -entry-points task1,task2,task3 -critical-section-begin begin_critical_section:cs1 -critical-section-end end_critical_section:cs1
In this example, the tasks, task1
, task2
and task3
,
call the function func
. func
calls
the nonreentrant standard library function, strerror
.
Though task3
calls func
inside
a critical section, other tasks do not use the same critical section.
Operations in the critical section of task3
are
not mutually exclusive with operations in other tasks.
These three tasks are calling a nonreentrant standard library function without common protection. In your result details, you see each pair of conflicting function calls.
If you click the icon, you see the function call sequence starting from the
entry point to the standard library function call. You also see that the call starting from
task3
is in a critical section. The Access Protections
entry shows the lock and unlock function that begin and end the critical section. In this
example, you see the functions begin_critical_section
and
end_critical_section
.
One possible correction is to use a reentrant version of the
standard library function strerror
. You can use
the POSIX version strerror_r
which has the
same functionality but also guarantees thread-safety.
#include <errno.h> #include <stdio.h> #include <string.h> void begin_critical_section(void); void end_critical_section(void); FILE *getFilePointer(void); enum { BUFFERSIZE = 64 }; void func(FILE *fp) { fpos_t pos; errno = 0; if (0 != fgetpos(fp, &pos)) { char errmsg[BUFFERSIZE]; if (strerror_r(errno, errmsg, BUFFERSIZE) != 0) { /* Handle error */ } printf("Could not get the file position: %s\n", errmsg); } } void task1(void) { FILE* fptr1 = getFilePointer(); func(fptr1); } void task2(void) { FILE* fptr2 = getFilePointer(); func(fptr2); } void task3(void) { FILE* fptr3 = getFilePointer(); begin_critical_section(); func(fptr3); end_critical_section(); }
One possible correction is to place the call to strerror
in
critical section. You can implement the critical section in multiple
ways.
For instance, you can place the call to the intermediate function func
in
the same critical section in the three tasks. When task1
enters
its critical section, the other tasks cannot enter their critical
sections until task1
leaves its critical section.
The calls to func
and therefore the calls to strerror
from
the three tasks cannot interfere with each other.
To implement the critical section, in each of the three tasks,
call func
between calls to begin_critical_section
and end_critical_section
.
#include <errno.h> #include <stdio.h> #include <string.h> void begin_critical_section(void); void end_critical_section(void); FILE *getFilePointer(void); void func(FILE *fp) { fpos_t pos; errno = 0; if (0 != fgetpos(fp, &pos)) { char *errmsg = strerror(errno); printf("Could not get the file position: %s\n", errmsg); } } void task1(void) { FILE* fptr1 = getFilePointer(); begin_critical_section(); func(fptr1); end_critical_section(); } void task2(void) { FILE* fptr2 = getFilePointer(); begin_critical_section(); func(fptr2); end_critical_section(); } void task3(void) { FILE* fptr3 = getFilePointer(); begin_critical_section(); func(fptr3); end_critical_section(); }
Another possible correction is to make the tasks, task1
, task2
and task3
,
temporally exclusive. Temporally exclusive tasks cannot execute concurrently.
On the Configuration pane, specify the following additional options:
Option | Value |
---|---|
Temporally exclusive tasks (-temporal-exclusions-file) |
|
On the command-line, you can use the following:
polyspace-bug-finder -temporal-exclusions-file "C:\exclusions_file.txt"
C:\exclusions_file.txt
has the following line:
task1 task2 task3
Shared data access within signal handler
This issue occurs when you access or modify a shared object inside a signal handler.
When you define a signal handler function to access or modify a shared object, the handler accesses or modifies the shared object when it receives a signal. If another function is already accessing the shared object, that function causes a race condition and can leave the data in an inconsistent state.
To access or modify shared objects inside a signal handler, check that the objects are
lock-free atomic, or, if they are integers, declare them as volatile
sig_atomic_t
.
#include <signal.h> #include <stdlib.h> #include <string.h> /* declare global variable. */ int e_flag; void sig_handler(int signum) { /* Signal handler accesses variable that is not of type volatile sig_atomic_t. */ e_flag = signum; //Noncompliant } int func(void) { if (signal(SIGINT, sig_handler) == SIG_ERR) { /* Handle error */ abort(); } /* Program code */ if (raise(SIGINT) != 0) { /* Handle error */ abort(); } /* More code */ return 0; }
In this example, sig_handler
accesses e_flag
, a
variable of type int
. A concurrent access by another function can leave
e_flag
in an inconsistent state.
volatile
sig_atomic_t
Before you access a shared variable from a signal handler, declare the variable with
type volatile sig_atomic_t
instead of int
. You can
safely access variables of this type asynchronously.
#include <signal.h> #include <stdlib.h> #include <string.h> /* Declare variable of type volatile sig_atomic_t. */ volatile sig_atomic_t e_flag; void sig_handler(int signum) { /* Use variable of proper type inside signal handler. */ e_flag = signum; } int func(void) { if (signal(SIGINT, sig_handler) == SIG_ERR) { /* Handle error */ abort(); } /* Program code */ if (raise(SIGINT) != 0) { /* Handle error */ abort(); } /* More code */ return 0; }
Check Information
Category: Resource Locking Problems |
Version History
Introduced in R2023a
See Also
External Websites
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)