mirror of
https://github.com/github/codeql.git
synced 2026-07-13 07:25:38 +02:00
118 lines
3.7 KiB
Plaintext
118 lines
3.7 KiB
Plaintext
/**
|
|
* @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, LibraryTypeDataFlow ltdf, CallableFlowSource source,
|
|
CallableFlowSink sink
|
|
|
|
|
m = mc.getTarget().getUnboundDeclaration() and
|
|
ltdf.callableFlow(source, AccessPath::empty(), sink, AccessPath::element(), m, _) and
|
|
e = source.getSource(mc) and
|
|
result.(Variable).getAnAccess() = sink.getSink(mc)
|
|
)
|
|
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()
|