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