diff --git a/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.md b/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.md new file mode 100644 index 0000000000..0c3d8c3ade --- /dev/null +++ b/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.md @@ -0,0 +1,98 @@ +# CON39-C: Do not join or detach a thread that was previously joined or detached + +This query implements the CERT-C rule CON39-C: + +> Do not join or detach a thread that was previously joined or detached + + +## Description + +The C Standard, 7.26.5.6 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states that a thread shall not be joined once it was previously joined or detached. Similarly, subclause 7.26.5.3 states that a thread shall not be detached once it was previously joined or detached. Violating either of these subclauses results in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +## Noncompliant Code Example + +This noncompliant code example detaches a thread that is later joined. + +```cpp +#include +#include + +int thread_func(void *arg) { + /* Do work */ + thrd_detach(thrd_current()); + return 0; +} + +int main(void) { + thrd_t t; + + if (thrd_success != thrd_create(&t, thread_func, NULL)) { + /* Handle error */ + return 0; + } + + if (thrd_success != thrd_join(t, 0)) { + /* Handle error */ + return 0; + } + return 0; +} +``` + +## Compliant Solution + +This compliant solution does not detach the thread. Its resources are released upon successfully joining with the main thread: + +```cpp +#include +#include + +int thread_func(void *arg) { + /* Do work */ + return 0; +} + +int main(void) { + thrd_t t; + + if (thrd_success != thrd_create(&t, thread_func, NULL)) { + /* Handle error */ + return 0; + } + + if (thrd_success != thrd_join(t, 0)) { + /* Handle error */ + return 0; + } + return 0; +} +``` + +## Risk Assessment + +Joining or detaching a previously joined or detached thread is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
CON39-C Low Likely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 Supported, but no explicit checker
CodeSonar 7.1p0 CONCURRENCY.TNJ Thread is not Joinable
Helix QAC 2022.3 C1776
Parasoft C/C++test 2022.1 CERT_C-CON39-a Do not join or detach a thread that was previously joined or detached
Polyspace Bug Finder R2022b CERT C: Rule CON39-C Checks for join or detach of a joined or detached thread (rule fully covered)
+ + +## Related Vulnerabilities + +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+CON39-C). + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.26.5.3, "The thrd_detach Function" Subclause 7.26.5.6, "The thrd_join Function"
+ + +## Implementation notes + +This query considers problematic usages of join and detach irrespective of the execution of the program and other synchronization and interprocess communication mechanisms that may be used. + +## References + +* CERT-C: [CON39-C: Do not join or detach a thread that was previously joined or detached](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql b/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql new file mode 100644 index 0000000000..a8dead535d --- /dev/null +++ b/c/cert/src/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql @@ -0,0 +1,50 @@ +/** + * @id c/cert/thread-was-previously-joined-or-detached + * @name CON39-C: Do not join or detach a thread that was previously joined or detached + * @description Joining or detaching a previously joined or detached thread can lead to undefined + * program behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/con39-c + * correctness + * concurrency + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.cpp.Concurrency + +// OK +// 1) Thread calls detach parent DOES NOT call join +// 2) Parent calls join, thread does NOT call detach() +// NOT OK +// 1) Thread calls detach, parent calls join +// 2) Thread calls detach twice, parent does not call join +// 3) Parent calls join twice, thread does not call detach +from C11ThreadCreateCall tcc +where + not isExcluded(tcc, Concurrency5Package::threadWasPreviouslyJoinedOrDetachedQuery()) and + // Note: These cases can be simplified but they are presented like this for clarity + // case 1 - calls to `thrd_join` and `thrd_detach` within the parent or + // within the parent / child CFG. + exists(C11ThreadWait tw, C11ThreadDetach dt | + tw = getAThreadContextAwareSuccessor(tcc) and + dt = getAThreadContextAwareSuccessor(tcc) + ) + or + // case 2 - multiple calls to `thrd_detach` within the threaded CFG. + exists(C11ThreadDetach dt1, C11ThreadDetach dt2 | + dt1 = getAThreadContextAwareSuccessor(tcc) and + dt2 = getAThreadContextAwareSuccessor(tcc) and + not dt1 = dt2 + ) + or + // case 3 - multiple calls to `thrd_join` within the threaded CFG. + exists(C11ThreadWait tw1, C11ThreadWait tw2 | + tw1 = getAThreadContextAwareSuccessor(tcc) and + tw2 = getAThreadContextAwareSuccessor(tcc) and + not tw1 = tw2 + ) +select tcc, "Thread may call join or detach after the thread is joined or detached." diff --git a/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.md b/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.md new file mode 100644 index 0000000000..663f82d303 --- /dev/null +++ b/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.md @@ -0,0 +1,173 @@ +# CON40-C: Do not refer to an atomic variable twice in an expression + +This query implements the CERT-C rule CON40-C: + +> Do not refer to an atomic variable twice in an expression + + +## Description + +A consistent locking policy guarantees that multiple threads cannot simultaneously access or modify shared data. Atomic variables eliminate the need for locks by guaranteeing thread safety when certain operations are performed on them. The thread-safe operations on atomic variables are specified in the C Standard, subclauses 7.17.7 and 7.17.8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\]. While atomic operations can be combined, combined operations do not provide the thread safety provided by individual atomic operations. + +Every time an atomic variable appears on the left side of an assignment operator, including a compound assignment operator such as `*=`, an atomic write is performed on the variable. The use of the increment (++`)` or decrement `(--)` operators on an atomic variable constitutes an atomic read-and-write operation and is consequently thread-safe. Any reference of an atomic variable anywhere else in an expression indicates a distinct atomic read on the variable. + +If the same atomic variable appears twice in an expression, then two atomic reads, or an atomic read and an atomic write, are required. Such a pair of atomic operations is not thread-safe, as another thread can modify the atomic variable between the two operations. Consequently, an atomic variable must not be referenced twice in the same expression. + +## Noncompliant Code Example (atomic_bool) + +This noncompliant code example declares a shared `atomic_bool` `flag` variable and provides a `toggle_flag()` method that negates the current value of `flag`: + +```cpp +#include +#include + +static atomic_bool flag = ATOMIC_VAR_INIT(false); + +void init_flag(void) { + atomic_init(&flag, false); +} + +void toggle_flag(void) { + bool temp_flag = atomic_load(&flag); + temp_flag = !temp_flag; + atomic_store(&flag, temp_flag); +} + +bool get_flag(void) { + return atomic_load(&flag); +} + +``` +Execution of this code may result in unexpected behavior because the value of `flag` is read, negated, and written back. This occurs even though the read and write are both atomic. + +Consider, for example, two threads that call `toggle_flag()`. The expected effect of toggling `flag` twice is that it is restored to its original value. However, the scenario in the following table leaves `flag` in the incorrect state. + +`toggle_flag()` without Compare-and-Exchange + +
Time flag Thread Action
1 true t 1 Reads the current value of flag , which is true, into a cache
2 true t 2 Reads the current value of flag , which is still true, into a different cache
3 true t 1 Toggles the temporary variable in the cache to false
4 true t 2 Toggles the temporary variable in the different cache to false
5 false t 1 Writes the cache variable's value to flag
6 false t 2 Writes the different cache variable's value to flag
+As a result, the effect of the call by *t*2 is not reflected in `flag`; the program behaves as if `toggle_flag()` was called only once, not twice. + + +## Compliant Solution (atomic_compare_exchange_weak()) + +This compliant solution uses a compare-and-exchange to guarantee that the correct value is stored in `flag`. All updates are visible to other threads. The call to `atomic_compare_exchange_weak()` is in a loop in conformance with [CON41-C. Wrap functions that can fail spuriously in a loop](https://wiki.sei.cmu.edu/confluence/display/c/CON41-C.+Wrap+functions+that+can+fail+spuriously+in+a+loop). + +```cpp +#include +#include + +static atomic_bool flag = ATOMIC_VAR_INIT(false); + +void init_flag(void) { + atomic_init(&flag, false); +} + +void toggle_flag(void) { + bool old_flag = atomic_load(&flag); + bool new_flag; + do { + new_flag = !old_flag; + } while (!atomic_compare_exchange_weak(&flag, &old_flag, new_flag)); +} + +bool get_flag(void) { + return atomic_load(&flag); +} +``` +An alternative solution is to use the `atomic_flag` data type for managing Boolean values atomically. However, `atomic_flag` does not support a toggle operation. + +## Compliant Solution (Compound Assignment) + +This compliant solution uses the `^=` assignment operation to toggle `flag`. This operation is guaranteed to be atomic, according to the C Standard, 6.5.16.2, paragraph 3. This operation performs a bitwise-exclusive-or between its arguments, but for Boolean arguments, this is equivalent to negation. + +```cpp +#include +#include + +static atomic_bool flag = ATOMIC_VAR_INIT(false); + +void toggle_flag(void) { + flag ^= 1; +} + +bool get_flag(void) { + return flag; +} +``` +An alternative solution is to use a mutex to protect the atomic operation, but this solution loses the performance benefits of atomic variables. + +## Noncompliant Code Example + +This noncompliant code example takes an atomic global variable `n` and computes `n + (n - 1) + (n - 2) + ... + 1`, using the formula `n * (n + 1) / 2`: + +```cpp +#include + +atomic_int n = ATOMIC_VAR_INIT(0); + +int compute_sum(void) { + return n * (n + 1) / 2; +} +``` +The value of `n` may change between the two atomic reads of `n` in the expression, yielding an incorrect result. + +## Compliant Solution + +This compliant solution passes the atomic variable as a function argument, forcing the variable to be copied and guaranteeing a correct result. Note that the function's formal parameter need not be atomic, and the atomic variable can still be passed as an actual argument. + +```cpp +#include + +int compute_sum(int n) { + return n * (n + 1) / 2; +} + +``` + +## Risk Assessment + +When operations on atomic variables are assumed to be atomic, but are not atomic, surprising data races can occur, leading to corrupted data and invalid control flow. + +
Rule Severity Likelihood Remediation Cost Priority Level
CON40-C Medium Probable Medium P8 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 multiple-atomic-accesses Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-CON40
CodeSonar 7.1p0 CONCURRENCY.MAA Multiple Accesses of Atomic
Coverity 2017.07 EVALUATION_ORDER (partial) MISRA 2012 Rule 13.2 VOLATILE_ATOICITY (possible) Implemented
Helix QAC 2022.3 C1114, C1115, C1116 C++3171, C++4150
Klocwork 2022.3 CERT.CONC.ATOMIC_TWICE_EXPR
Parasoft C/C++test 2022.1 CERT_C-CON40-a Do not refer to an atomic variable twice in an expression
Polyspace Bug Finder R2022b CERT C: Rule CON40-C Checks for: Atomic variable accessed twice in an expressiontomic variable accessed twice in an expression, atomic load and store sequence not atomictomic load and store sequence not atomic. Rule fully covered.
PRQA QA-C 9.7 1114, 1115, 1116
RuleChecker 22.04 multiple-atomic-accesses Partially checked
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://www.securecoding.cert.org/confluence/display/seccode/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+CON40-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CWE 2.11 CWE-366 , Race Condition within a Thread 2017-07-07: CERT: Rule subset of CWE
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-366 and CON40-C** + +CON40-C = Subset( CON43-C) Intersection( CON32-C, CON40-C) = Ø + +CWE-366 = Union( CON40-C, list) where list = + +* C data races that do not involve an atomic variable used twice within an expression + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.5.16.2, "Compound Assignment" 7.17, "Atomics"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [CON40-C: Do not refer to an atomic variable twice in an expression](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql b/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql new file mode 100644 index 0000000000..d20663bd87 --- /dev/null +++ b/c/cert/src/rules/CON40-C/AtomicVariableTwiceInExpression.ql @@ -0,0 +1,47 @@ +/** + * @id c/cert/atomic-variable-twice-in-expression + * @name CON40-C: Do not refer to an atomic variable twice in an expression + * @description Atomic variables that are referred to twice in the same expression can produce + * unpredictable program behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/con40-c + * correctness + * concurrency + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +from MacroInvocation mi, Variable v, Locatable whereFound +where + not isExcluded(whereFound, Concurrency5Package::atomicVariableTwiceInExpressionQuery()) and + ( + // There isn't a way to safely use this construct in a way that is also + // possible the reliably detect so advise against using it. + ( + mi.getMacroName() = ["atomic_store", "atomic_store_explicit"] + or + // This construct is generally safe, but must be used in a loop. To lower + // the false positive rate we don't look at the conditions of the loop and + // instead assume if it is found in a looping construct that it is likely + // related to the safety property. + mi.getMacroName() = ["atomic_compare_exchange_weak", "atomic_compare_exchange_weak_explicit"] and + not exists(Loop l | mi.getAGeneratedElement().(Expr).getParent*() = l) + ) and + whereFound = mi + ) + or + mi.getMacroName() = "ATOMIC_VAR_INIT" and + exists(Expr av | + av = mi.getAGeneratedElement() and + av = v.getAnAssignedValue() and + exists(Assignment m | + not m instanceof AssignXorExpr and + m.getLValue().(VariableAccess).getTarget() = v and + whereFound = m + ) + ) +select mi, "Atomic variable possibly referred to twice in an $@.", whereFound, "expression" diff --git a/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.expected b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.expected new file mode 100644 index 0000000000..3b2022f342 --- /dev/null +++ b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.expected @@ -0,0 +1,8 @@ +| test.c:26:3:26:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:32:3:32:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:37:3:37:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:58:3:58:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:66:3:66:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:73:3:73:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:87:3:87:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | +| test.c:94:3:94:13 | call to thrd_create | Thread may call join or detach after the thread is joined or detached. | diff --git a/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.qlref b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.qlref new file mode 100644 index 0000000000..5daa5a5046 --- /dev/null +++ b/c/cert/test/rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.qlref @@ -0,0 +1 @@ +rules/CON39-C/ThreadWasPreviouslyJoinedOrDetached.ql \ No newline at end of file diff --git a/c/cert/test/rules/CON39-C/test.c b/c/cert/test/rules/CON39-C/test.c new file mode 100644 index 0000000000..32af0bd8b9 --- /dev/null +++ b/c/cert/test/rules/CON39-C/test.c @@ -0,0 +1,96 @@ +#include +#include + +void detach(thrd_t t) { thrd_detach(t); } + +int t1(void *p) { + thrd_detach(thrd_current()); + return 0; +} + +int t2(void *p) { return 0; } + +int t3(void *p) { + thrd_detach(thrd_current()); + thrd_detach(thrd_current()); + return 0; +} + +int t4(void *p) { + detach(thrd_current()); + return 0; +} + +void p1() { + thrd_t t; + thrd_create(&t, t1, NULL); // NON_COMPLIANT + thrd_join(t, 0); +} + +void p2() { + thrd_t t; + thrd_create(&t, t3, NULL); // NON_COMPLIANT - detach called multiple times. +} + +void p3(int a, int b) { + thrd_t t; + thrd_create(&t, t2, NULL); // NON_COMPLIANT - Join in parent 2x + thrd_join(t, 0); + + if (a > b) { + thrd_join(t, 0); + } +} + +void p4() { + thrd_t t; + thrd_create(&t, t1, NULL); // COMPLIANT +} + +void p5() { + thrd_t t; + thrd_create(&t, t2, NULL); // COMPLIANT + thrd_join(t, 0); +} + +void p6() { + thrd_t t; + thrd_create(&t, t2, NULL); // NON_COMPLIANT + + thrd_detach(t); + thrd_join(t, 0); +} + +void p7() { + thrd_t t; + thrd_create(&t, t2, NULL); // NON_COMPLIANT + thrd_join(t, 0); + thrd_detach(t); +} + +void p8() { + thrd_t t; + thrd_create(&t, t2, NULL); // NON_COMPLIANT + + thrd_detach(t); + thrd_detach(t); +} + +void p9() { + thrd_t t; + thrd_create(&t, t2, NULL); // COMPLIANT + detach(t); +} + +void p10() { + thrd_t t; + thrd_create(&t, t2, NULL); // NON_COMPLIANT + thrd_detach(t); + detach(t); +} + +void p11() { + thrd_t t; + thrd_create(&t, t4, NULL); // NON_COMPLIANT + thrd_detach(t); +} diff --git a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected new file mode 100644 index 0000000000..377d6cc818 --- /dev/null +++ b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.expected @@ -0,0 +1,6 @@ +| test.c:6:19:6:40 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:32:3:32:10 | ... += ... | expression | +| test.c:6:19:6:40 | ATOMIC_VAR_INIT(value) | Atomic variable possibly referred to twice in an $@. | test.c:33:3:33:13 | ... = ... | expression | +| test.c:10:3:10:23 | atomic_store(a,b) | Atomic variable possibly referred to twice in an $@. | test.c:10:3:10:23 | atomic_store(a,b) | expression | +| test.c:11:3:11:35 | atomic_store_explicit(a,b,c) | Atomic variable possibly referred to twice in an $@. | test.c:11:3:11:35 | atomic_store_explicit(a,b,c) | expression | +| test.c:24:3:24:48 | atomic_compare_exchange_weak(a,b,c) | Atomic variable possibly referred to twice in an $@. | test.c:24:3:24:48 | atomic_compare_exchange_weak(a,b,c) | expression | +| test.c:25:3:26:45 | atomic_compare_exchange_weak_explicit(a,b,c,d,e) | Atomic variable possibly referred to twice in an $@. | test.c:25:3:26:45 | atomic_compare_exchange_weak_explicit(a,b,c,d,e) | expression | diff --git a/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.qlref b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.qlref new file mode 100644 index 0000000000..6f7e6b8ea2 --- /dev/null +++ b/c/cert/test/rules/CON40-C/AtomicVariableTwiceInExpression.qlref @@ -0,0 +1 @@ +rules/CON40-C/AtomicVariableTwiceInExpression.ql \ No newline at end of file diff --git a/c/cert/test/rules/CON40-C/test.c b/c/cert/test/rules/CON40-C/test.c new file mode 100644 index 0000000000..a72a9d4809 --- /dev/null +++ b/c/cert/test/rules/CON40-C/test.c @@ -0,0 +1,34 @@ +#include +#include + +static bool fl1 = ATOMIC_VAR_INIT(false); +static bool fl2 = ATOMIC_VAR_INIT(false); +static bool fl3 = ATOMIC_VAR_INIT(false); +static bool fl4 = ATOMIC_VAR_INIT(false); + +void f1() { + atomic_store(&fl1, 0); // NON_COMPLIANT + atomic_store_explicit(&fl1, 0, 0); // NON_COMPLIANT +} + +void f2() { + do { + } while (!atomic_compare_exchange_weak(&fl2, &fl2, &fl2)); // COMPLIANT + + do { + } while (!atomic_compare_exchange_weak_explicit(&fl2, &fl2, &fl2, &fl2, + &fl2)); // COMPLIANT +} + +void f3() { + atomic_compare_exchange_weak(&fl2, &fl2, &fl2); // NON_COMPLIANT + atomic_compare_exchange_weak_explicit(&fl2, &fl2, &fl2, &fl2, + &fl2); // NON_COMPLIANT +} + +void f4() { fl3 ^= true; } + +void f5() { + fl3 += 1; // NON_COMPLIANT + fl3 = 1 + 1; // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/common/test/includes/standard-library/stdatomic.h b/c/common/test/includes/standard-library/stdatomic.h index 89f78d9025..f07aa2697a 100644 --- a/c/common/test/includes/standard-library/stdatomic.h +++ b/c/common/test/includes/standard-library/stdatomic.h @@ -1,2 +1,7 @@ #define atomic_compare_exchange_weak(a, b, c) 0 #define atomic_compare_exchange_weak_explicit(a, b, c, d, e) 0 +#define atomic_load(a) 0 +#define atomic_load_explicit(a, b) +#define atomic_store(a, b) 0 +#define atomic_store_explicit(a,b,c) 0 +#define ATOMIC_VAR_INIT(value) (value) \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/Concurrency.qll b/cpp/common/src/codingstandards/cpp/Concurrency.qll index 9994a79150..a793a3d317 100644 --- a/cpp/common/src/codingstandards/cpp/Concurrency.qll +++ b/cpp/common/src/codingstandards/cpp/Concurrency.qll @@ -483,6 +483,20 @@ class C11ThreadWait extends ThreadWait { C11ThreadWait() { getTarget().getName() = "thrd_join" } } +/** + * Models thread detach functions. + */ +abstract class ThreadDetach extends FunctionCall { } + +/** + * Models a call to `thrd_detach` in C11. + */ +class C11ThreadDetach extends ThreadWait { + VariableAccess var; + + C11ThreadDetach() { getTarget().getName() = "thrd_detach" } +} + abstract class MutexSource extends FunctionCall { } /** diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll new file mode 100644 index 0000000000..ebbb3dabe5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency5.qll @@ -0,0 +1,42 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Concurrency5Query = + TThreadWasPreviouslyJoinedOrDetachedQuery() or + TAtomicVariableTwiceInExpressionQuery() + +predicate isConcurrency5QueryMetadata(Query query, string queryId, string ruleId) { + query = + // `Query` instance for the `threadWasPreviouslyJoinedOrDetached` query + Concurrency5Package::threadWasPreviouslyJoinedOrDetachedQuery() and + queryId = + // `@id` for the `threadWasPreviouslyJoinedOrDetached` query + "c/cert/thread-was-previously-joined-or-detached" and + ruleId = "CON39-C" + or + query = + // `Query` instance for the `atomicVariableTwiceInExpression` query + Concurrency5Package::atomicVariableTwiceInExpressionQuery() and + queryId = + // `@id` for the `atomicVariableTwiceInExpression` query + "c/cert/atomic-variable-twice-in-expression" and + ruleId = "CON40-C" +} + +module Concurrency5Package { + Query threadWasPreviouslyJoinedOrDetachedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `threadWasPreviouslyJoinedOrDetached` query + TQueryC(TConcurrency5PackageQuery(TThreadWasPreviouslyJoinedOrDetachedQuery())) + } + + Query atomicVariableTwiceInExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `atomicVariableTwiceInExpression` query + TQueryC(TConcurrency5PackageQuery(TAtomicVariableTwiceInExpressionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index 638e68492e..4664c7df34 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -7,6 +7,7 @@ import Concurrency1 import Concurrency2 import Concurrency3 import Concurrency4 +import Concurrency5 import Contracts1 import Contracts3 import Declarations1 @@ -39,6 +40,7 @@ newtype TCQuery = TConcurrency2PackageQuery(Concurrency2Query q) or TConcurrency3PackageQuery(Concurrency3Query q) or TConcurrency4PackageQuery(Concurrency4Query q) or + TConcurrency5PackageQuery(Concurrency5Query q) or TContracts1PackageQuery(Contracts1Query q) or TContracts3PackageQuery(Contracts3Query q) or TDeclarations1PackageQuery(Declarations1Query q) or @@ -71,6 +73,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isConcurrency2QueryMetadata(query, queryId, ruleId) or isConcurrency3QueryMetadata(query, queryId, ruleId) or isConcurrency4QueryMetadata(query, queryId, ruleId) or + isConcurrency5QueryMetadata(query, queryId, ruleId) or isContracts1QueryMetadata(query, queryId, ruleId) or isContracts3QueryMetadata(query, queryId, ruleId) or isDeclarations1QueryMetadata(query, queryId, ruleId) or diff --git a/rule_packages/c/Concurrency5.json b/rule_packages/c/Concurrency5.json new file mode 100644 index 0000000000..67707201fd --- /dev/null +++ b/rule_packages/c/Concurrency5.json @@ -0,0 +1,47 @@ +{ + "CERT-C": { + "CON39-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Joining or detaching a previously joined or detached thread can lead to undefined program behavior.", + "kind": "problem", + "name": "Do not join or detach a thread that was previously joined or detached", + "precision": "high", + "severity": "error", + "short_name": "ThreadWasPreviouslyJoinedOrDetached", + "tags": [ + "correctness", + "concurrency" + ], + "implementation_scope": { + "description": "This query considers problematic usages of join and detach irrespective of the execution of the program and other synchronization and interprocess communication mechanisms that may be used." + } + } + ], + "title": "Do not join or detach a thread that was previously joined or detached" + }, + "CON40-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Atomic variables that are referred to twice in the same expression can produce unpredictable program behavior.", + "kind": "problem", + "name": "Do not refer to an atomic variable twice in an expression", + "precision": "very-high", + "severity": "error", + "short_name": "AtomicVariableTwiceInExpression", + "tags": [ + "correctness", + "concurrency" + ] + } + ], + "title": "Do not refer to an atomic variable twice in an expression" + } + } +} \ No newline at end of file