Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions c/common/test/includes/standard-library/stdatomic.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define ATOMIC_VAR_INIT(value) (value)
#define atomic_init __c11_atomic_init
#define atomic_is_lock_free(obj) __c11_atomic_is_lock_free(sizeof(*(obj)))
typedef _Atomic(int) atomic_int;

Expand Down
76 changes: 76 additions & 0 deletions c/misra/src/rules/RULE-21-26/TimedlockOnInappropriateMutexType.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* @id c/misra/timedlock-on-inappropriate-mutex-type
* @name RULE-21-26: The Standard Library function mtx_timedlock() shall only be invoked on mutexes of type mtx_timed
* @description The Standard Library function mtx_timedlock() shall only be invoked on mutex objects
* of appropriate mutex type.
* @kind path-problem
* @precision high
* @problem.severity error
* @tags external/misra/id/rule-21-26
* correctness
* concurrency
* external/misra/c/2012/amendment4
* external/misra/obligation/required
*/

import cpp
import codingstandards.c.misra
import semmle.code.cpp.dataflow.new.DataFlow

class MutexTimed extends EnumConstant {
MutexTimed() { hasName("mtx_timed") }
}

class MutexInitCall extends FunctionCall {
Expr mutexExpr;
Expr mutexTypeExpr;

MutexInitCall() {
getTarget().hasName("mtx_init") and
mutexExpr = getArgument(0) and
mutexTypeExpr = getArgument(1)
}

predicate isTimedMutexType() {
exists(EnumConstantAccess baseTypeAccess |
(
baseTypeAccess = mutexTypeExpr
or
baseTypeAccess = mutexTypeExpr.(BinaryBitwiseOperation).getAnOperand()
) and
baseTypeAccess.getTarget() instanceof MutexTimed
)
or
mutexTypeExpr.getValue().toInt() = any(MutexTimed m).getValue().toInt()
}

Expr getMutexExpr() { result = mutexExpr }
}

module MutexTimedlockFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
exists(MutexInitCall init |
node.asDefiningArgument() = init.getMutexExpr() and not init.isTimedMutexType()
)
}

predicate isSink(DataFlow::Node node) {
exists(FunctionCall fc |
fc.getTarget().hasName("mtx_timedlock") and
node.asIndirectExpr() = fc.getArgument(0)
)
}
}

module Flow = DataFlow::Global<MutexTimedlockFlowConfig>;

import Flow::PathGraph

from Flow::PathNode source, Flow::PathNode sink
where
not isExcluded(sink.getNode().asExpr(),
Concurrency7Package::timedlockOnInappropriateMutexTypeQuery()) and
Flow::flowPath(source, sink)
select sink.getNode(), source, sink,
"Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'.", source.getNode(),
"initialized"
78 changes: 78 additions & 0 deletions c/misra/src/rules/RULE-9-7/UninitializedAtomicObject.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @id c/misra/uninitialized-atomic-object
* @name RULE-9-7: Atomic objects shall be appropriately initialized before being accessed
* @description Atomic objects that do not have static storage duration shall be initialized with a
* value or by using 'atomic_init()'.
* @kind problem
* @precision high
* @problem.severity warning
* @tags external/misra/id/rule-9-7
* concurrency
* external/misra/c/2012/amendment4
* external/misra/obligation/mandatory
*/

import cpp
import codingstandards.c.misra
import codingstandards.cpp.StdFunctionOrMacro
import semmle.code.cpp.controlflow.Dominance

class ThreadSpawningFunction extends Function {
ThreadSpawningFunction() {
this.hasName("pthread_create")
or
this.hasName("thrd_create")
or
exists(FunctionCall fc |
fc.getTarget() instanceof ThreadSpawningFunction and
fc.getEnclosingFunction() = this
)
}
}

private string atomicInit() { result = "atomic_init" }

class AtomicInitAddressOfExpr extends AddressOfExpr {
AtomicInitAddressOfExpr() {
exists(StdFunctionOrMacro<C11FunctionWrapperMacro, atomicInit/0>::Call c |
this = c.getArgument(0)
)
}
}

ControlFlowNode getARequiredInitializationPoint(LocalScopeVariable v) {
result = v.getParentScope().(BlockStmt).getFollowingStmt()
or
exists(DeclStmt decl |
decl.getADeclaration() = v and
result =
any(FunctionCall fc |
fc.getTarget() instanceof ThreadSpawningFunction and
fc.getEnclosingBlock().getEnclosingBlock*() = v.getParentScope() and
fc.getAPredecessor*() = decl
)
)
}

from VariableDeclarationEntry decl, Variable v
where
not isExcluded(decl, Concurrency7Package::uninitializedAtomicObjectQuery()) and
v = decl.getVariable() and
v.getUnderlyingType().hasSpecifier("atomic") and
not v.isTopLevel() and
not exists(v.getInitializer()) and
exists(ControlFlowNode missingInitPoint |
missingInitPoint = getARequiredInitializationPoint(v) and
// Check for `atomic_init(&v)`
not exists(AtomicInitAddressOfExpr initialization |
initialization.getOperand().(VariableAccess).getTarget() = v and
dominates(initialization, missingInitPoint)
) and
// Check for `unknown_func(&v)` which may call `atomic_init` on `v`.
not exists(FunctionCall fc |
fc.getAnArgument().(AddressOfExpr).getOperand().(VariableAccess).getTarget() = v and
dominates(fc, missingInitPoint)
)
)
select decl,
"Atomic object '" + v.getName() + "' has no initializer or corresponding use of 'atomic_init()'."
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
edges
| test.c:10:24:10:24 | *m | test.c:10:43:10:43 | *m | provenance | |
| test.c:13:12:13:14 | mtx_init output argument | test.c:14:17:14:19 | *& ... | provenance | |
| test.c:13:12:13:14 | mtx_init output argument | test.c:15:14:15:16 | *& ... | provenance | |
| test.c:15:14:15:16 | *& ... | test.c:10:24:10:24 | *m | provenance | |
| test.c:17:12:17:14 | mtx_init output argument | test.c:18:17:18:19 | *& ... | provenance | |
| test.c:17:12:17:14 | mtx_init output argument | test.c:19:14:19:16 | *& ... | provenance | |
| test.c:19:14:19:16 | *& ... | test.c:10:24:10:24 | *m | provenance | |
| test.c:30:12:30:14 | mtx_init output argument | test.c:31:17:31:19 | *& ... | provenance | |
| test.c:30:12:30:14 | mtx_init output argument | test.c:32:14:32:16 | *& ... | provenance | |
| test.c:32:14:32:16 | *& ... | test.c:10:24:10:24 | *m | provenance | |
| test.c:42:12:42:16 | mtx_init output argument | test.c:42:13:42:14 | *l3 [post update] [m] | provenance | |
| test.c:42:13:42:14 | *l3 [post update] [m] | test.c:43:18:43:19 | *l3 [m] | provenance | |
| test.c:42:13:42:14 | *l3 [post update] [m] | test.c:44:15:44:16 | *l3 [m] | provenance | |
| test.c:43:18:43:19 | *l3 [m] | test.c:43:17:43:21 | *& ... | provenance | |
| test.c:44:14:44:18 | *& ... | test.c:10:24:10:24 | *m | provenance | |
| test.c:44:15:44:16 | *l3 [m] | test.c:44:14:44:18 | *& ... | provenance | |
nodes
| test.c:10:24:10:24 | *m | semmle.label | *m |
| test.c:10:43:10:43 | *m | semmle.label | *m |
| test.c:13:12:13:14 | mtx_init output argument | semmle.label | mtx_init output argument |
| test.c:14:17:14:19 | *& ... | semmle.label | *& ... |
| test.c:15:14:15:16 | *& ... | semmle.label | *& ... |
| test.c:17:12:17:14 | mtx_init output argument | semmle.label | mtx_init output argument |
| test.c:18:17:18:19 | *& ... | semmle.label | *& ... |
| test.c:19:14:19:16 | *& ... | semmle.label | *& ... |
| test.c:30:12:30:14 | mtx_init output argument | semmle.label | mtx_init output argument |
| test.c:31:17:31:19 | *& ... | semmle.label | *& ... |
| test.c:32:14:32:16 | *& ... | semmle.label | *& ... |
| test.c:42:12:42:16 | mtx_init output argument | semmle.label | mtx_init output argument |
| test.c:42:13:42:14 | *l3 [post update] [m] | semmle.label | *l3 [post update] [m] |
| test.c:43:17:43:21 | *& ... | semmle.label | *& ... |
| test.c:43:18:43:19 | *l3 [m] | semmle.label | *l3 [m] |
| test.c:44:14:44:18 | *& ... | semmle.label | *& ... |
| test.c:44:15:44:16 | *l3 [m] | semmle.label | *l3 [m] |
subpaths
#select
| test.c:10:43:10:43 | *m | test.c:13:12:13:14 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:13:12:13:14 | mtx_init output argument | initialized |
| test.c:10:43:10:43 | *m | test.c:17:12:17:14 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:17:12:17:14 | mtx_init output argument | initialized |
| test.c:10:43:10:43 | *m | test.c:30:12:30:14 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:30:12:30:14 | mtx_init output argument | initialized |
| test.c:10:43:10:43 | *m | test.c:42:12:42:16 | mtx_init output argument | test.c:10:43:10:43 | *m | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:42:12:42:16 | mtx_init output argument | initialized |
| test.c:14:17:14:19 | *& ... | test.c:13:12:13:14 | mtx_init output argument | test.c:14:17:14:19 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:13:12:13:14 | mtx_init output argument | initialized |
| test.c:18:17:18:19 | *& ... | test.c:17:12:17:14 | mtx_init output argument | test.c:18:17:18:19 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:17:12:17:14 | mtx_init output argument | initialized |
| test.c:31:17:31:19 | *& ... | test.c:30:12:30:14 | mtx_init output argument | test.c:31:17:31:19 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:30:12:30:14 | mtx_init output argument | initialized |
| test.c:43:17:43:21 | *& ... | test.c:42:12:42:16 | mtx_init output argument | test.c:43:17:43:21 | *& ... | Call to mtx_timedlock with mutex which is $@ without flag 'mtx_timed'. | test.c:42:12:42:16 | mtx_init output argument | initialized |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-21-26/TimedlockOnInappropriateMutexType.ql
45 changes: 45 additions & 0 deletions c/misra/test/rules/RULE-21-26/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "threads.h"

mtx_t g1;
mtx_t g2;
mtx_t g3;
mtx_t g4;

struct timespec ts = {0, 0};

void doTimeLock(mtx_t *m) { mtx_timedlock(m, &ts); }

void main(void) {
mtx_init(&g1, mtx_plain);
mtx_timedlock(&g1, &ts); // NON-COMPLIANT
doTimeLock(&g1); // NON-COMPLIANT

mtx_init(&g2, mtx_plain | mtx_recursive);
mtx_timedlock(&g2, &ts); // NON-COMPLIANT
doTimeLock(&g2); // NON-COMPLIANT

mtx_init(&g3, mtx_timed);
mtx_timedlock(&g3, &ts); // COMPLIANT
doTimeLock(&g3); // COMPLIANT

mtx_init(&g4, mtx_timed | mtx_recursive);
mtx_timedlock(&g4, &ts); // COMPLIANT
doTimeLock(&g4); // COMPLIANT

mtx_t l1;
mtx_init(&l1, mtx_plain);
mtx_timedlock(&l1, &ts); // NON-COMPLIANT
doTimeLock(&l1); // NON-COMPLIANT

mtx_t l2;
mtx_init(&l2, mtx_timed);
mtx_timedlock(&l2, &ts); // COMPLIANT
doTimeLock(&l2); // COMPLIANT

struct s {
mtx_t m;
} l3;
mtx_init(&l3.m, mtx_plain);
mtx_timedlock(&l3.m, &ts); // NON-COMPLIANT
doTimeLock(&l3.m); // NON-COMPLIANT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
| test.c:24:15:24:16 | definition of l3 | Atomic object 'l3' has no initializer or corresponding use of 'atomic_init()'. |
| test.c:27:15:27:16 | definition of l4 | Atomic object 'l4' has no initializer or corresponding use of 'atomic_init()'. |
| test.c:31:15:31:16 | definition of l5 | Atomic object 'l5' has no initializer or corresponding use of 'atomic_init()'. |
| test.c:41:15:41:16 | definition of l7 | Atomic object 'l7' has no initializer or corresponding use of 'atomic_init()'. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-9-7/UninitializedAtomicObject.ql
46 changes: 46 additions & 0 deletions c/misra/test/rules/RULE-9-7/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "stdatomic.h"
#include "threads.h"

_Atomic int g1; // COMPLIANT
_Atomic int g2 = 0; // COMPLIANT

void f_thread(void *x);

void f_starts_thread() {
thrd_t t;
thrd_create(&t, f_thread, 0);
}

void f_may_initialize_argument(void *p1) {}

void main() {
_Atomic int l1 = 1; // COMPLIANT
f_starts_thread();

_Atomic int l2; // COMPLIANT
atomic_init(&l2, 0);
f_starts_thread();

_Atomic int l3; // NON-COMPLIANT
f_starts_thread();

_Atomic int l4; // NON-COMPLIANT
f_starts_thread();
atomic_init(&l4, 0);

_Atomic int l5; // NON-COMPLIANT
if (g1 == 0) {
atomic_init(&l5, 0);
}
f_starts_thread();

_Atomic int l6; // COMPLIANT
f_may_initialize_argument(&l6);
f_starts_thread();

_Atomic int l7; // NON_COMPLIANT
if (g1 == 0) {
f_may_initialize_argument(&l7);
}
f_starts_thread();
}
Loading
Loading