-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathForeachCapture.ql
More file actions
129 lines (117 loc) · 3.83 KB
/
ForeachCapture.ql
File metadata and controls
129 lines (117 loc) · 3.83 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
129
/**
* @name Capturing a foreach variable
* @description Code that captures a 'foreach' variable and uses it outside the loop behaves differently in C# version 4 and C# version 5
* @kind problem
* @problem.severity recommendation
* @precision medium
* @id cs/captured-foreach-variable
* @tags portability
* maintainability
* language-features
* external/cwe/cwe-758
*/
import csharp
import semmle.code.csharp.dataflow.LibraryTypeDataFlow
import semmle.code.csharp.frameworks.system.Collections
import semmle.code.csharp.frameworks.system.collections.Generic
predicate lambdaCaptures(AnonymousFunctionExpr lambda, Variable v) {
exists(VariableAccess va |
va.getEnclosingCallable() = lambda |
va.getTarget() = v
)
}
predicate lambdaCapturesLoopVariable(AnonymousFunctionExpr lambda, ForeachStmt loop, Variable v) {
lambdaCaptures(lambda, v) and
inForeachStmtBody(loop, lambda) and
loop.getVariable() = v
}
predicate inForeachStmtBody(ForeachStmt loop, Element e) {
e = loop.getBody()
or
exists(Element mid |
inForeachStmtBody(loop, mid)
and
e = mid.getAChild()
)
}
class LambdaDataFlowConfiguration extends DataFlow::Configuration {
LambdaDataFlowConfiguration() {
this = "LambdaDataFlowConfiguration"
}
override predicate isSource(DataFlow::Node source) {
lambdaCapturesLoopVariable(source.asExpr(), _, _)
}
override predicate isSink(DataFlow::Node sink) {
exists(getAssignmentTarget(sink.asExpr()))
}
predicate capturesLoopVarAndIsStoredIn(AnonymousFunctionExpr lambda, Variable loopVar, Element storage) {
exists(DataFlow::Node sink |
this.hasFlow(DataFlow::exprNode(lambda), sink) |
storage = getAssignmentTarget(sink.asExpr())
)
and
exists(ForeachStmt loop |
lambdaCapturesLoopVariable(lambda, loop, loopVar) |
not declaredInsideLoop(loop, storage)
)
}
}
Element getAssignmentTarget(Expr e) {
exists(Assignment a |
a.getRValue() = e |
result = a.getLValue().(PropertyAccess).getTarget() or
result = a.getLValue().(FieldAccess).getTarget() or
result = a.getLValue().(LocalVariableAccess).getTarget() or
result = a.getLValue().(EventAccess).getTarget()
)
or
exists(AddEventExpr aee |
e = aee.getRValue() and
result = aee.getLValue().getTarget()
)
or
result = getCollectionAssignmentTarget(e)
}
Element getCollectionAssignmentTarget(Expr e) {
// Store into collection via method
exists(MethodCall mc, Method m, IEnumerableFlow ief, CallableFlowSourceArg source, CallableFlowSinkQualifier sink, int i |
mc.getQualifier() = result.(Variable).getAnAccess()
and
ief = mc.getQualifier().getType().getSourceDeclaration()
and
m = mc.getTarget().getSourceDeclaration()
and
ief.callableFlow(source, sink, m, _)
and
source.getCallable() = m
and
source.getArgumentIndex() = i
and
e = mc.getArgument(i)
)
or
// Array initializer
e = result.(ArrayCreation).getInitializer().getAnElement()
or
// Collection initializer
e = result.(ObjectCreation).getInitializer().(CollectionInitializer).getElementInitializer(_).getAnArgument()
or
// Store values using indexer
exists(IndexerAccess ia, AssignExpr ae |
ia.getQualifier() = result.(Variable).getAnAccess()
and
ia = ae.getLValue()
and
e = ae.getRValue()
)
}
// Variable v is declared inside the loop body
predicate declaredInsideLoop(ForeachStmt loop, LocalVariable v) {
exists(LocalVariableDeclStmt decl |
decl.getVariableDeclExpr(_).getVariable() = v |
inForeachStmtBody(loop, decl)
)
}
from LambdaDataFlowConfiguration c, AnonymousFunctionExpr lambda, Variable loopVar, Element storage
where c.capturesLoopVarAndIsStoredIn(lambda, loopVar, storage)
select lambda, "Function which may be stored in $@ captures variable $@", storage, storage.toString(), loopVar, loopVar.getName()