Skip to content

Commit af84f24

Browse files
committed
changes for making query more robust
1 parent ee9625d commit af84f24

File tree

3 files changed

+94
-7
lines changed

3 files changed

+94
-7
lines changed

cpp/cert/src/rules/CON53-CPP/DeadlockByLockingInPredefinedOrder.ql

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import cpp
1616
import codingstandards.cpp.cert
1717
import codingstandards.cpp.Concurrency
18+
import semmle.code.cpp.controlflow.Dominance
1819

1920
/**
2021
* Gets a pair of locks guarding a `LockProtectedControlFlowNode` in an order
@@ -27,7 +28,8 @@ predicate getAnOrderedLockPair(
2728
lock1 = node.coveredByLock() and
2829
lock2 = node.coveredByLock() and
2930
not lock1 = lock2 and
30-
lock1.getFile() = lock2.getFile() and
31+
lock1.getEnclosingFunction() = lock2.getEnclosingFunction() and
32+
node.(Expr).getEnclosingFunction() = lock1.getEnclosingFunction() and
3133
exists(Location l1Loc, Location l2Loc |
3234
l1Loc = lock1.getLocation() and
3335
l2Loc = lock2.getLocation()
@@ -53,15 +55,30 @@ predicate getAnOrderedLockPair(
5355
* same time that are invoked from a thread.
5456
*/
5557

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+
)
66+
}
67+
68+
predicate isSerializedLock(LockingOperation lock) { not isUnSerializedLock(lock) }
69+
5670
from LockProtectedControlFlowNode node, Function f, FunctionCall lock1, FunctionCall lock2
5771
where
5872
not isExcluded(node, ConcurrencyPackage::deadlockByLockingInPredefinedOrderQuery()) and
5973
// we can get into trouble when we get into a situation where there may be two
6074
// locks in the same threaded function active at the same time.
61-
// simple ordering
62-
// lock1 = node.coveredByLock() and
63-
// lock2 = node.coveredByLock() and
75+
// simple ordering is applied here for presentation purposes.
6476
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
6582
// To reduce the noise (and increase usefulness) we alert the user at the
6683
// level of the function, which is the unit the synchronization should be
6784
// performed.

cpp/cert/test/rules/CON53-CPP/test.cpp

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <mutex>
2+
#include <stdlib.h>
23
#include <thread>
34

45
class B {
@@ -54,9 +55,64 @@ int d3(B *from, B *to, int amount) { // NON_COMPLIANT
5455
return 0;
5556
}
5657

58+
int getA() { return 0; }
59+
int getARand() { return rand(); }
60+
61+
int d4(B *from, B *to, int amount) { // COMPLIANT
62+
std::mutex *one;
63+
std::mutex *two;
64+
65+
int a = getARand();
66+
67+
// here a may take on multiple different
68+
// values and thus different values may flow
69+
// into the locks
70+
if (a == 9) {
71+
one = &from->mu;
72+
two = &to->mu;
73+
} else {
74+
one = &to->mu;
75+
two = &from->mu;
76+
}
77+
78+
std::lock_guard<std::mutex> from_lock(*one);
79+
std::lock_guard<std::mutex> to_lock(*two);
80+
81+
from->set(from->get() - amount);
82+
to->set(to->get() + amount);
83+
84+
return 0;
85+
}
86+
87+
int d5(B *from, B *to, int amount) { // NON_COMPLIANT
88+
std::mutex *one;
89+
std::mutex *two;
90+
91+
int a = getARand();
92+
93+
// here a may take on multiple different
94+
// values and thus different values may flow
95+
// into the locks
96+
if (a == 9) {
97+
one = &from->mu;
98+
two = &to->mu;
99+
} else {
100+
one = &to->mu;
101+
two = &from->mu;
102+
}
103+
104+
std::lock_guard<std::mutex> from_lock(from->mu);
105+
std::lock_guard<std::mutex> to_lock(to->mu);
106+
107+
from->set(from->get() - amount);
108+
to->set(to->get() + amount);
109+
110+
return 0;
111+
}
112+
57113
void f(B *ba1, B *ba2) {
58114
std::thread thr1(d1, ba1, ba2, 100);
59-
std::thread thr2(d2, ba2, ba1, 100);
115+
std::thread thr2(d1, ba2, ba1, 100);
60116
thr1.join();
61117
thr2.join();
62118
}
@@ -73,4 +129,18 @@ void f3(B *ba1, B *ba2) {
73129
std::thread thr2(d3, ba1, ba2, 100);
74130
thr1.join();
75131
thr2.join();
76-
}
132+
}
133+
134+
void f4(B *ba1, B *ba2) {
135+
std::thread thr1(d4, ba1, ba2, 100);
136+
std::thread thr2(d4, ba1, ba2, 100);
137+
thr1.join();
138+
thr2.join();
139+
}
140+
141+
void f5(B *ba1, B *ba2) {
142+
std::thread thr1(d5, ba1, ba2, 100);
143+
std::thread thr2(d5, ba1, ba2, 100);
144+
thr1.join();
145+
thr2.join();
146+
}

cpp/common/src/codingstandards/cpp/Concurrency.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ class RAIIStyleLock extends LockingOperation {
318318
*/
319319
override predicate isLock() {
320320
this instanceof ConstructorCall and
321-
lock = getArgument(0) and
321+
lock = getArgument(0).getAChild() and
322322
// defer_locks don't cause a lock
323323
not exists(Expr exp |
324324
exp = getArgument(1) and

0 commit comments

Comments
 (0)