Skip to content

Commit 8741010

Browse files
committed
refactor shared rule
1 parent adfb4f0 commit 8741010

File tree

2 files changed

+99
-16
lines changed

2 files changed

+99
-16
lines changed

cpp/common/src/codingstandards/cpp/exclusions/c/Concurrency2.qll

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,10 @@ import RuleMetadata
44
import codingstandards.cpp.exclusions.RuleMetadata
55

66
newtype Concurrency2Query =
7-
TDoNotDestroyAMutexWhileItIsLockedQuery() or
87
TDeadlockByLockingInPredefinedOrderQuery() or
98
TWrapFunctionsThatCanSpuriouslyWakeUpInLoopQuery()
109

1110
predicate isConcurrency2QueryMetadata(Query query, string queryId, string ruleId) {
12-
query =
13-
// `Query` instance for the `doNotDestroyAMutexWhileItIsLocked` query
14-
Concurrency2Package::doNotDestroyAMutexWhileItIsLockedQuery() and
15-
queryId =
16-
// `@id` for the `doNotDestroyAMutexWhileItIsLocked` query
17-
"c/cert/do-not-destroy-a-mutex-while-it-is-locked" and
18-
ruleId = "CON31-C"
19-
or
2011
query =
2112
// `Query` instance for the `deadlockByLockingInPredefinedOrder` query
2213
Concurrency2Package::deadlockByLockingInPredefinedOrderQuery() and
@@ -35,13 +26,6 @@ predicate isConcurrency2QueryMetadata(Query query, string queryId, string ruleId
3526
}
3627

3728
module Concurrency2Package {
38-
Query doNotDestroyAMutexWhileItIsLockedQuery() {
39-
//autogenerate `Query` type
40-
result =
41-
// `Query` type for `doNotDestroyAMutexWhileItIsLocked` query
42-
TQueryC(TConcurrency2PackageQuery(TDoNotDestroyAMutexWhileItIsLockedQuery()))
43-
}
44-
4529
Query deadlockByLockingInPredefinedOrderQuery() {
4630
//autogenerate `Query` type
4731
result =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Provides a library which includes a `problems` predicate for locks that do not
3+
* lock in a predefined order.
4+
*/
5+
6+
import cpp
7+
import codingstandards.cpp.Customizations
8+
import codingstandards.cpp.Exclusions
9+
import codingstandards.cpp.Concurrency
10+
import semmle.code.cpp.controlflow.Dominance
11+
12+
abstract class PreventDeadlockByLockingInPredefinedOrderSharedQuery extends Query { }
13+
14+
Query getQuery() { result instanceof PreventDeadlockByLockingInPredefinedOrderSharedQuery }
15+
16+
/**
17+
* Gets a pair of locks guarding a `LockProtectedControlFlowNode` in an order
18+
* specified by the locking function's call site.
19+
*/
20+
pragma[inline]
21+
predicate getAnOrderedLockPair(
22+
FunctionCall lock1, FunctionCall lock2, LockProtectedControlFlowNode node
23+
) {
24+
lock1 = node.coveredByLock() and
25+
lock2 = node.coveredByLock() and
26+
not lock1 = lock2 and
27+
lock1.getEnclosingFunction() = lock2.getEnclosingFunction() and
28+
node.(Expr).getEnclosingFunction() = lock1.getEnclosingFunction() and
29+
exists(Location l1Loc, Location l2Loc |
30+
l1Loc = lock1.getLocation() and
31+
l2Loc = lock2.getLocation()
32+
|
33+
l1Loc.getEndLine() < l2Loc.getStartLine()
34+
or
35+
l1Loc.getStartLine() = l2Loc.getEndLine() and
36+
l1Loc.getEndColumn() < l2Loc.getStartColumn()
37+
)
38+
}
39+
40+
/*
41+
* There are two ways to safely avoid deadlock. One involves doing the locking
42+
* in a specific order that is guaranteed to be the same across all thread
43+
* invocations. This is especially hard to check and thus we adopt an
44+
* alternative viewpoint wherein we view a "safe" usage of multiple locks to be
45+
* one that uses the built in `std::lock` functionality which avoids this
46+
* problem.
47+
*
48+
* To properly deadlock, a thread must have at least two different locks (i.e.,
49+
* mutual exclusion) which are used in an order that causes a problem. Thus we
50+
* look for functions with CFNs wherein there may be two locks active at the
51+
* same time that are invoked from a thread.
52+
*/
53+
54+
predicate isUnSerializedLock(LockingOperation lock) {
55+
exists(VariableAccess va |
56+
va = lock.getArgument(0).getAChild().(VariableAccess) and
57+
not exists(Assignment assn |
58+
assn = va.getTarget().getAnAssignment() and
59+
not bbDominates(assn.getBasicBlock(), lock.getBasicBlock())
60+
)
61+
)
62+
}
63+
64+
predicate isSerializedLock(LockingOperation lock) { not isUnSerializedLock(lock) }
65+
66+
query predicate problems(
67+
Function f, string message, FunctionCall lock1, string lock1String, FunctionCall lock2,
68+
string lock2String
69+
) {
70+
exists(LockProtectedControlFlowNode node |
71+
not isExcluded(node, getQuery()) and
72+
// we can get into trouble when we get into a situation where there may be two
73+
// locks in the same threaded function active at the same time.
74+
// simple ordering is applied here for presentation purposes.
75+
getAnOrderedLockPair(lock1, lock2, node) and
76+
// it is difficult to determine if the ordering applied to the locks is "safe"
77+
// so here we simply look to see that there exists at least one other program
78+
// path that would yield different argument values to the lock functions
79+
// perhaps arising from some logic that applies an ordering to the locking.
80+
not (isSerializedLock(lock1) and isSerializedLock(lock2)) and
81+
// To reduce the noise (and increase usefulness) we alert the user at the
82+
// level of the function, which is the unit the synchronization should be
83+
// performed.
84+
f = node.getEnclosingStmt().getEnclosingFunction() and
85+
// Because `std::lock` isn't included in our definition of a 'lock'
86+
// it is not necessary to check to see if it is in fact what is protecting
87+
// these CNFs.
88+
// However, to reduce noise, we shall require that the function we are
89+
// reporting makes some sort of locking call since this is likely where the
90+
// user intends to perform the locking operations. Our implementation will
91+
// currently look into all of these nodes which is less helpful for the user
92+
// but useful for our analysis.
93+
any(LockingOperation l).getEnclosingFunction() = f and
94+
lock1String = "lock 1" and
95+
lock2String = "lock 2"
96+
) and
97+
message =
98+
"Threaded function may be called from a context that uses $@ and $@ which may lead to deadlocks."
99+
}

0 commit comments

Comments
 (0)