|
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