Skip to content

Commit ccc5d4d

Browse files
authored
Merge pull request #45 from jsinglet/jsinglet/concurrency3
Concurrency 3
2 parents 224670c + e8776e0 commit ccc5d4d

File tree

61 files changed

+2295
-265
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2295
-265
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# CON31-C: Do not destroy a mutex while it is locked
2+
3+
This query implements the CERT-C rule CON31-C:
4+
5+
> Do not destroy a mutex while it is locked
6+
7+
8+
## Description
9+
10+
Mutexes are used to protect shared data structures being concurrently accessed. If a mutex is destroyed while a thread is blocked waiting for that mutex, [critical sections](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-criticalsections) and shared data are no longer protected.
11+
12+
The C Standard, 7.26.4.1, paragraph 2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
13+
14+
> The `mtx_destroy` function releases any resources used by the mutex pointed to by `mtx`. No threads can be blocked waiting for the mutex pointed to by `mtx`.
15+
16+
17+
This statement implies that destroying a mutex while a thread is waiting on it is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
18+
19+
## Noncompliant Code Example
20+
21+
This noncompliant code example creates several threads that each invoke the `do_work()` function, passing a unique number as an ID. The `do_work()` function initializes the `lock` mutex if the argument is 0 and destroys the mutex if the argument is `max_threads - 1`. In all other cases, the `do_work()` function provides normal processing. Each thread, except the final cleanup thread, increments the atomic `completed` variable when it is finished.
22+
23+
Unfortunately, this code contains several race conditions, allowing the mutex to be destroyed before it is unlocked. Additionally, there is no guarantee that `lock` will be initialized before it is passed to `mtx_lock()`. Each of these behaviors is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
24+
25+
```cpp
26+
#include <stdatomic.h>
27+
#include <stddef.h>
28+
#include <threads.h>
29+
30+
mtx_t lock;
31+
/* Atomic so multiple threads can modify safely */
32+
atomic_int completed = ATOMIC_VAR_INIT(0);
33+
enum { max_threads = 5 };
34+
35+
int do_work(void *arg) {
36+
int *i = (int *)arg;
37+
38+
if (*i == 0) { /* Creation thread */
39+
if (thrd_success != mtx_init(&lock, mtx_plain)) {
40+
/* Handle error */
41+
}
42+
atomic_store(&completed, 1);
43+
} else if (*i < max_threads - 1) { /* Worker thread */
44+
if (thrd_success != mtx_lock(&lock)) {
45+
/* Handle error */
46+
}
47+
/* Access data protected by the lock */
48+
atomic_fetch_add(&completed, 1);
49+
if (thrd_success != mtx_unlock(&lock)) {
50+
/* Handle error */
51+
}
52+
} else { /* Destruction thread */
53+
mtx_destroy(&lock);
54+
}
55+
return 0;
56+
}
57+
58+
int main(void) {
59+
thrd_t threads[max_threads];
60+
61+
for (size_t i = 0; i < max_threads; i++) {
62+
if (thrd_success != thrd_create(&threads[i], do_work, &i)) {
63+
/* Handle error */
64+
}
65+
}
66+
for (size_t i = 0; i < max_threads; i++) {
67+
if (thrd_success != thrd_join(threads[i], 0)) {
68+
/* Handle error */
69+
}
70+
}
71+
return 0;
72+
}
73+
74+
```
75+
76+
## Compliant Solution
77+
78+
This compliant solution eliminates the race conditions by initializing the mutex in `main()` before creating the threads and by destroying the mutex in `main()` after joining the threads:
79+
80+
```cpp
81+
#include <stdatomic.h>
82+
#include <stddef.h>
83+
#include <threads.h>
84+
85+
mtx_t lock;
86+
/* Atomic so multiple threads can increment safely */
87+
atomic_int completed = ATOMIC_VAR_INIT(0);
88+
enum { max_threads = 5 };
89+
90+
int do_work(void *dummy) {
91+
if (thrd_success != mtx_lock(&lock)) {
92+
/* Handle error */
93+
}
94+
/* Access data protected by the lock */
95+
atomic_fetch_add(&completed, 1);
96+
if (thrd_success != mtx_unlock(&lock)) {
97+
/* Handle error */
98+
}
99+
100+
return 0;
101+
}
102+
103+
int main(void) {
104+
thrd_t threads[max_threads];
105+
106+
if (thrd_success != mtx_init(&lock, mtx_plain)) {
107+
/* Handle error */
108+
}
109+
for (size_t i = 0; i < max_threads; i++) {
110+
if (thrd_success != thrd_create(&threads[i], do_work, NULL)) {
111+
/* Handle error */
112+
}
113+
}
114+
for (size_t i = 0; i < max_threads; i++) {
115+
if (thrd_success != thrd_join(threads[i], 0)) {
116+
/* Handle error */
117+
}
118+
}
119+
120+
mtx_destroy(&lock);
121+
return 0;
122+
}
123+
124+
```
125+
126+
## Risk Assessment
127+
128+
Destroying a mutex while it is locked may result in invalid control flow and data corruption.
129+
130+
<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> CON31-C </td> <td> Medium </td> <td> Probable </td> <td> High </td> <td> <strong>P4</strong> </td> <td> <strong>L3</strong> </td> </tr> </tbody> </table>
131+
132+
133+
## Automated Detection
134+
135+
<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> </td> <td> Supported, but no explicit checker </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.0p0 </td> <td> <strong>CONCURRENCY.LOCALARG</strong> </td> <td> Local Variable Passed to Thread </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C4961, C4962</strong> </td> <td> </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>4961, 4962 </strong> </td> <td> </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-CON31-a</strong> <strong>CERT_C-CON31-b</strong> <strong>CERT_C-CON31-c</strong> </td> <td> Do not destroy another thread's mutex Do not use resources that have been freed Do not free resources using invalid pointers </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule CON31-C </a> </td> <td> Checks for destruction of locked mutex (rule fully covered) </td> </tr> </tbody> </table>
136+
137+
138+
## Related Vulnerabilities
139+
140+
Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+CON31-C).
141+
142+
## Related Guidelines
143+
144+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
145+
146+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-667 </a> , Improper Locking </td> <td> 2017-07-10: CERT: Rule subset of CWE </td> </tr> </tbody> </table>
147+
148+
149+
## CERT-CWE Mapping Notes
150+
151+
[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
152+
153+
**CWE-667 and CON31-C/POS48-C**
154+
155+
Intersection( CON31-C, POS48-C) = Ø
156+
157+
CWE-667 = Union, CON31-C, POS48-C, list) where list =
158+
159+
* Locking &amp; Unlocking issues besides unlocking another thread’s C mutex or pthread mutex.
160+
161+
## Bibliography
162+
163+
<table> <tbody> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> 7.26.4.1, "The <code>mtx_destroy</code> Function" </td> </tr> </tbody> </table>
164+
165+
166+
## Implementation notes
167+
168+
None
169+
170+
## References
171+
172+
* CERT-C: [CON31-C: Do not destroy a mutex while it is locked](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @id c/cert/do-not-allow-a-mutex-to-go-out-of-scope-while-locked
3+
* @name CON31-C: Do not destroy a mutex while it is locked
4+
* @description Allowing a mutex to go out of scope while it is locked removes protections around
5+
* shared resources.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/con31-c
10+
* correctness
11+
* concurrency
12+
* external/cert/obligation/rule
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.cert
17+
import codingstandards.cpp.rules.donotallowamutextogooutofscopewhilelocked.DoNotAllowAMutexToGoOutOfScopeWhileLocked
18+
19+
class DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery extends DoNotAllowAMutexToGoOutOfScopeWhileLockedSharedQuery {
20+
DoNotAllowAMutexToGoOutOfScopeWhileLockedQuery() {
21+
this = Concurrency3Package::doNotAllowAMutexToGoOutOfScopeWhileLockedQuery()
22+
}
23+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# CON31-C: Do not destroy a mutex while it is locked
2+
3+
This query implements the CERT-C rule CON31-C:
4+
5+
> Do not destroy a mutex while it is locked
6+
7+
8+
## Description
9+
10+
Mutexes are used to protect shared data structures being concurrently accessed. If a mutex is destroyed while a thread is blocked waiting for that mutex, [critical sections](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-criticalsections) and shared data are no longer protected.
11+
12+
The C Standard, 7.26.4.1, paragraph 2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
13+
14+
> The `mtx_destroy` function releases any resources used by the mutex pointed to by `mtx`. No threads can be blocked waiting for the mutex pointed to by `mtx`.
15+
16+
17+
This statement implies that destroying a mutex while a thread is waiting on it is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
18+
19+
## Noncompliant Code Example
20+
21+
This noncompliant code example creates several threads that each invoke the `do_work()` function, passing a unique number as an ID. The `do_work()` function initializes the `lock` mutex if the argument is 0 and destroys the mutex if the argument is `max_threads - 1`. In all other cases, the `do_work()` function provides normal processing. Each thread, except the final cleanup thread, increments the atomic `completed` variable when it is finished.
22+
23+
Unfortunately, this code contains several race conditions, allowing the mutex to be destroyed before it is unlocked. Additionally, there is no guarantee that `lock` will be initialized before it is passed to `mtx_lock()`. Each of these behaviors is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
24+
25+
```cpp
26+
#include <stdatomic.h>
27+
#include <stddef.h>
28+
#include <threads.h>
29+
30+
mtx_t lock;
31+
/* Atomic so multiple threads can modify safely */
32+
atomic_int completed = ATOMIC_VAR_INIT(0);
33+
enum { max_threads = 5 };
34+
35+
int do_work(void *arg) {
36+
int *i = (int *)arg;
37+
38+
if (*i == 0) { /* Creation thread */
39+
if (thrd_success != mtx_init(&lock, mtx_plain)) {
40+
/* Handle error */
41+
}
42+
atomic_store(&completed, 1);
43+
} else if (*i < max_threads - 1) { /* Worker thread */
44+
if (thrd_success != mtx_lock(&lock)) {
45+
/* Handle error */
46+
}
47+
/* Access data protected by the lock */
48+
atomic_fetch_add(&completed, 1);
49+
if (thrd_success != mtx_unlock(&lock)) {
50+
/* Handle error */
51+
}
52+
} else { /* Destruction thread */
53+
mtx_destroy(&lock);
54+
}
55+
return 0;
56+
}
57+
58+
int main(void) {
59+
thrd_t threads[max_threads];
60+
61+
for (size_t i = 0; i < max_threads; i++) {
62+
if (thrd_success != thrd_create(&threads[i], do_work, &i)) {
63+
/* Handle error */
64+
}
65+
}
66+
for (size_t i = 0; i < max_threads; i++) {
67+
if (thrd_success != thrd_join(threads[i], 0)) {
68+
/* Handle error */
69+
}
70+
}
71+
return 0;
72+
}
73+
74+
```
75+
76+
## Compliant Solution
77+
78+
This compliant solution eliminates the race conditions by initializing the mutex in `main()` before creating the threads and by destroying the mutex in `main()` after joining the threads:
79+
80+
```cpp
81+
#include <stdatomic.h>
82+
#include <stddef.h>
83+
#include <threads.h>
84+
85+
mtx_t lock;
86+
/* Atomic so multiple threads can increment safely */
87+
atomic_int completed = ATOMIC_VAR_INIT(0);
88+
enum { max_threads = 5 };
89+
90+
int do_work(void *dummy) {
91+
if (thrd_success != mtx_lock(&lock)) {
92+
/* Handle error */
93+
}
94+
/* Access data protected by the lock */
95+
atomic_fetch_add(&completed, 1);
96+
if (thrd_success != mtx_unlock(&lock)) {
97+
/* Handle error */
98+
}
99+
100+
return 0;
101+
}
102+
103+
int main(void) {
104+
thrd_t threads[max_threads];
105+
106+
if (thrd_success != mtx_init(&lock, mtx_plain)) {
107+
/* Handle error */
108+
}
109+
for (size_t i = 0; i < max_threads; i++) {
110+
if (thrd_success != thrd_create(&threads[i], do_work, NULL)) {
111+
/* Handle error */
112+
}
113+
}
114+
for (size_t i = 0; i < max_threads; i++) {
115+
if (thrd_success != thrd_join(threads[i], 0)) {
116+
/* Handle error */
117+
}
118+
}
119+
120+
mtx_destroy(&lock);
121+
return 0;
122+
}
123+
124+
```
125+
126+
## Risk Assessment
127+
128+
Destroying a mutex while it is locked may result in invalid control flow and data corruption.
129+
130+
<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> CON31-C </td> <td> Medium </td> <td> Probable </td> <td> High </td> <td> <strong>P4</strong> </td> <td> <strong>L3</strong> </td> </tr> </tbody> </table>
131+
132+
133+
## Automated Detection
134+
135+
<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> </td> <td> Supported, but no explicit checker </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.0p0 </td> <td> <strong>CONCURRENCY.LOCALARG</strong> </td> <td> Local Variable Passed to Thread </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C4961, C4962</strong> </td> <td> </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>4961, 4962 </strong> </td> <td> </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-CON31-a</strong> <strong>CERT_C-CON31-b</strong> <strong>CERT_C-CON31-c</strong> </td> <td> Do not destroy another thread's mutex Do not use resources that have been freed Do not free resources using invalid pointers </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule CON31-C </a> </td> <td> Checks for destruction of locked mutex (rule fully covered) </td> </tr> </tbody> </table>
136+
137+
138+
## Related Vulnerabilities
139+
140+
Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+CON31-C).
141+
142+
## Related Guidelines
143+
144+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
145+
146+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-667 </a> , Improper Locking </td> <td> 2017-07-10: CERT: Rule subset of CWE </td> </tr> </tbody> </table>
147+
148+
149+
## CERT-CWE Mapping Notes
150+
151+
[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
152+
153+
**CWE-667 and CON31-C/POS48-C**
154+
155+
Intersection( CON31-C, POS48-C) = Ø
156+
157+
CWE-667 = Union, CON31-C, POS48-C, list) where list =
158+
159+
* Locking &amp; Unlocking issues besides unlocking another thread’s C mutex or pthread mutex.
160+
161+
## Bibliography
162+
163+
<table> <tbody> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> 7.26.4.1, "The <code>mtx_destroy</code> Function" </td> </tr> </tbody> </table>
164+
165+
166+
## Implementation notes
167+
168+
None
169+
170+
## References
171+
172+
* CERT-C: [CON31-C: Do not destroy a mutex while it is locked](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @id c/cert/do-not-destroy-a-mutex-while-it-is-locked
3+
* @name CON31-C: Do not destroy a mutex while it is locked
4+
* @description Calling delete on a locked mutex removes protections around shared resources.
5+
* @kind problem
6+
* @precision high
7+
* @problem.severity error
8+
* @tags external/cert/id/con31-c
9+
* correctness
10+
* concurrency
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import codingstandards.cpp.rules.donotdestroyamutexwhileitislocked.DoNotDestroyAMutexWhileItIsLocked
17+
18+
class DoNotDestroyAMutexWhileItIsLockedQuery extends DoNotDestroyAMutexWhileItIsLockedSharedQuery {
19+
DoNotDestroyAMutexWhileItIsLockedQuery() {
20+
this = Concurrency3Package::doNotDestroyAMutexWhileItIsLockedQuery()
21+
}
22+
}

0 commit comments

Comments
 (0)