forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTOCTOURace.ql
More file actions
128 lines (120 loc) · 4.52 KB
/
TOCTOURace.ql
File metadata and controls
128 lines (120 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* @name Time-of-check time-of-use race condition
* @description Using a resource after an unsynchronized state check can lead to a race condition,
* if the state may be changed between the check and use.
* @kind problem
* @problem.severity warning
* @security-severity 7.7
* @precision medium
* @id java/toctou-race-condition
* @tags security
* external/cwe/cwe-367
*/
import java
import semmle.code.java.Concurrency
import semmle.code.java.controlflow.Guards
/**
* Holds if `e1` and `e2` appear within a `synchronized` block on `monitor`.
*/
predicate commonSynchronization(Expr e1, Expr e2, Variable monitor) {
exists(SynchronizedStmt s |
locallySynchronizedOn(e1, s, monitor) and
locallySynchronizedOn(e2, s, monitor)
)
}
/**
* Holds if `m` is a call to a synchronized method on `receiver`.
*/
predicate synchCallOn(MethodCall m, Variable receiver) {
m.getCallee() instanceof SynchronizedCallable and
m.getQualifier() = receiver.getAnAccess()
}
/**
* A callable that might be used concurrently. This is a heuristic to avoid flagging
* non-concurrent usage of classes that try to be concurrency-safe (e.g. a lot of the Java
* collections).
*/
class PossiblyConcurrentCallable extends Callable {
PossiblyConcurrentCallable() {
this instanceof SynchronizedCallable
or
exists(SynchronizedStmt s | s.getEnclosingCallable() = this)
or
exists(FieldAccess f | f.getVariable().isVolatile() | f.getEnclosingCallable() = this)
or
exists(VarAccess v |
v.getVariable().getType().(RefType).hasQualifiedName("java.lang", "ThreadLocal")
|
v.getEnclosingCallable() = this
)
}
}
private VarAccess getANonInitializationAccess(Field f) {
result = f.getAnAccess() and
exists(Callable c | c = result.getEnclosingCallable() |
not (
c = f.getDeclaringType().getACallable() and
(c instanceof Constructor or c instanceof InitializerMethod)
)
)
}
/**
* Holds if all accesses to `v` (outside of initializers) are locked in the same way.
*/
predicate alwaysLocked(Field f) {
exists(Variable lock |
forex(VarAccess access | access = getANonInitializationAccess(f) |
locallySynchronizedOn(access, _, lock)
)
)
or
exists(RefType thisType |
forex(VarAccess access | access = getANonInitializationAccess(f) |
locallySynchronizedOnThis(access, thisType)
)
)
or
exists(RefType classType |
forex(VarAccess access | access = getANonInitializationAccess(f) |
locallySynchronizedOnClass(access, classType)
)
)
}
/**
* Holds if the value of `v` probably never escapes the local scope.
*/
predicate probablyNeverEscapes(LocalVariableDecl v) {
// Not passed into another function.
not exists(Call c | c.getAnArgument() = v.getAnAccess()) and
// Not assigned directly to another variable.
not exists(Assignment a | a.getSource() = v.getAnAccess()) and
// Not returned.
not exists(ReturnStmt r | r.getResult() = v.getAnAccess()) and
// All assignments are to new instances of a class.
forex(Expr e | e = v.getAnAssignedValue() | e instanceof ClassInstanceExpr)
}
// Loop conditions tend to be uninteresting, so are not included.
from IfStmt check, MethodCall call1, MethodCall call2, Variable r
where
check.getCondition().getAChildExpr*() = call1 and
// This can happen if there are loops, etc.
not call1 = call2 and
// The use is controlled by one of the branches of the condition, i.e. whether it
// is reached actually depends on that condition.
call1.getBasicBlock().(ConditionBlock).controls(call2.getBasicBlock(), _) and
// Two calls to synchronized methods on the same variable.
synchCallOn(call1, r) and
synchCallOn(call2, r) and
// Not jointly synchronized on that variable.
// (If the caller synchronizes on `r` then it takes the same monitor as the `synchronized` callees do.)
not commonSynchronization(call1, call2, r) and
// Only include cases that look like they may be intended for concurrent usage.
check.getEnclosingCallable() instanceof PossiblyConcurrentCallable and
// Ignore fields that look like they're consistently guarded with some other lock.
not alwaysLocked(r) and
// Ignore local variables whose value probably never escapes, as they can't be accessed concurrently.
not probablyNeverEscapes(r) and
// The synchronized methods on `Throwable` are not interesting.
not call1.getCallee().getDeclaringType() instanceof TypeThrowable
select call2, "This uses the state of $@ which $@. But these are not jointly synchronized.", r,
r.getName(), call1, "is checked at a previous call"