Skip to content

Commit fa23a45

Browse files
authored
Merge branch 'main' into csharp-hardcoded-cred-identity-fp
2 parents d4b5a4d + 389294b commit fa23a45

File tree

32 files changed

+1813
-139
lines changed

32 files changed

+1813
-139
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -781,26 +781,12 @@ class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDef
781781
override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() }
782782
}
783783

784-
pragma[nomagic]
785-
predicate indirectReturnOutNodeOperand0(CallInstruction call, Operand operand, int indirectionIndex) {
786-
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
787-
operandForFullyConvertedCall(operand, call)
788-
}
789-
790-
pragma[nomagic]
791-
predicate indirectReturnOutNodeInstruction0(
792-
CallInstruction call, Instruction instr, int indirectionIndex
793-
) {
794-
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
795-
instructionForFullyConvertedCall(instr, call)
796-
}
797-
798784
/**
799785
* Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and
800786
* `operand` represents a use of the fully converted value of `call`.
801787
*/
802788
private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) {
803-
indirectReturnOutNodeOperand0(call, operand, indirectionIndex) and
789+
operandForFullyConvertedCall(operand, call) and
804790
hasOperandAndIndex(node, operand, indirectionIndex)
805791
}
806792

@@ -813,7 +799,7 @@ private predicate hasOperand(Node node, CallInstruction call, int indirectionInd
813799
private predicate hasInstruction(
814800
Node node, CallInstruction call, int indirectionIndex, Instruction instr
815801
) {
816-
indirectReturnOutNodeInstruction0(call, instr, indirectionIndex) and
802+
instructionForFullyConvertedCall(instr, call) and
817803
hasInstructionAndIndex(node, instr, indirectionIndex)
818804
}
819805

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: majorAnalysis
3+
---
4+
* Improved support for flow through captured variables that properly adheres to inter-procedural control flow.

java/ql/lib/ext/java.lang.model.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ extensions:
176176
- ["java.lang", "Object", "getClass", "()", "summary", "manual"]
177177
- ["java.lang", "Object", "hashCode", "()", "summary", "manual"]
178178
- ["java.lang", "Object", "toString", "()", "summary", "manual"]
179-
- ["java.lang", "Runnable", "run", "()", "summary", "manual"]
180179
- ["java.lang", "Runtime", "getRuntime", "()", "summary", "manual"]
181180
- ["java.lang", "String", "compareTo", "(String)", "summary", "manual"]
182181
- ["java.lang", "String", "contains", "(CharSequence)", "summary", "manual"]

java/ql/lib/semmle/code/java/dataflow/FlowSummary.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ abstract class SyntheticCallable extends string {
101101
* A module for importing frameworks that define synthetic callables.
102102
*/
103103
private module SyntheticCallables {
104+
private import semmle.code.java.dispatch.WrappedInvocation
104105
private import semmle.code.java.frameworks.android.Intent
105106
private import semmle.code.java.frameworks.Stream
106107
}
@@ -170,6 +171,8 @@ class SummarizedCallableBase extends TSummarizedCallableBase {
170171
}
171172
}
172173

174+
class Provenance = Impl::Public::Provenance;
175+
173176
class SummarizedCallable = Impl::Public::SummarizedCallable;
174177

175178
class NeutralCallable = Impl::Public::NeutralCallable;

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ private module Cached {
5555
)
5656
} or
5757
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
58-
TFieldValueNode(Field f)
58+
TFieldValueNode(Field f) or
59+
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn)
5960

6061
cached
6162
newtype TContent =
@@ -64,6 +65,7 @@ private module Cached {
6465
TCollectionContent() or
6566
TMapKeyContent() or
6667
TMapValueContent() or
68+
TCapturedVariableContent(CapturedVariable v) or
6769
TSyntheticFieldContent(SyntheticField s)
6870

6971
cached
@@ -73,6 +75,7 @@ private module Cached {
7375
TCollectionContentApprox() or
7476
TMapKeyContentApprox() or
7577
TMapValueContentApprox() or
78+
TCapturedVariableContentApprox(CapturedVariable v) or
7679
TSyntheticFieldApproxContent()
7780
}
7881

@@ -127,6 +130,8 @@ module Public {
127130
or
128131
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
129132
or
133+
result = this.(CaptureNode).getTypeImpl()
134+
or
130135
result = this.(FieldValueNode).getField().getType()
131136
}
132137

@@ -372,6 +377,7 @@ module Private {
372377
result.asCallable() = n.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
373378
result = nodeGetEnclosingCallable(n.(ImplicitPostUpdateNode).getPreUpdateNode()) or
374379
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
380+
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
375381
result.asFieldScope() = n.(FieldValueNode).getField()
376382
}
377383

@@ -491,6 +497,28 @@ module Private {
491497
c.asSummarizedCallable() = this.getSummarizedCallable() and pos = this.getPosition()
492498
}
493499
}
500+
501+
/**
502+
* A synthesized data flow node representing a closure object that tracks
503+
* captured variables.
504+
*/
505+
class CaptureNode extends Node, TCaptureNode {
506+
private CaptureFlow::SynthesizedCaptureNode cn;
507+
508+
CaptureNode() { this = TCaptureNode(cn) }
509+
510+
CaptureFlow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn }
511+
512+
override Location getLocation() { result = cn.getLocation() }
513+
514+
override string toString() { result = cn.toString() }
515+
516+
Type getTypeImpl() {
517+
exists(Variable v | cn.isVariableAccess(v) and result = v.getType())
518+
or
519+
cn.isInstanceAccess() and result = cn.getEnclosingCallable().getDeclaringType()
520+
}
521+
}
494522
}
495523

496524
private import Private
@@ -520,3 +548,14 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
520548

521549
override Node getPreUpdateNode() { result = pre }
522550
}
551+
552+
private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
553+
private CaptureNode pre;
554+
555+
CapturePostUpdateNode() {
556+
CaptureFlow::capturePostUpdateNode(this.getSynthesizedCaptureNode(),
557+
pre.getSynthesizedCaptureNode())
558+
}
559+
560+
override Node getPreUpdateNode() { result = pre }
561+
}

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll

Lines changed: 143 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import semmle.code.java.dataflow.FlowSummary
1010
private import FlowSummaryImpl as FlowSummaryImpl
1111
private import DataFlowImplConsistency
1212
private import DataFlowNodes
13+
private import codeql.dataflow.VariableCapture as VariableCapture
1314
import DataFlowNodes::Private
1415

1516
private newtype TReturnKind = TNormalReturnKind()
@@ -51,37 +52,138 @@ private predicate fieldStep(Node node1, Node node2) {
5152
)
5253
}
5354

54-
/**
55-
* Holds if data can flow from `node1` to `node2` through variable capture.
56-
*/
57-
private predicate variableCaptureStep(Node node1, ExprNode node2) {
58-
exists(SsaImplicitInit closure, SsaVariable captured |
59-
closure.captures(captured) and
60-
node2.getExpr() = closure.getAFirstUse()
61-
|
62-
node1.asExpr() = captured.getAUse()
63-
or
64-
not exists(captured.getAUse()) and
65-
exists(SsaVariable capturedDef | capturedDef = captured.getAnUltimateDefinition() |
66-
capturedDef.(SsaImplicitInit).isParameterDefinition(node1.asParameter()) or
67-
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() =
68-
node1.asExpr() or
69-
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(AssignOp) = node1.asExpr()
70-
)
55+
private predicate closureFlowStep(Expr e1, Expr e2) {
56+
simpleAstFlowStep(e1, e2)
57+
or
58+
exists(SsaVariable v |
59+
v.getAUse() = e2 and
60+
v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() =
61+
e1
7162
)
7263
}
7364

65+
private module CaptureInput implements VariableCapture::InputSig {
66+
private import java as J
67+
68+
class Location = J::Location;
69+
70+
class BasicBlock instanceof J::BasicBlock {
71+
string toString() { result = super.toString() }
72+
73+
Callable getEnclosingCallable() { result = super.getEnclosingCallable() }
74+
75+
Location getLocation() { result = super.getLocation() }
76+
}
77+
78+
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { bbIDominates(result, bb) }
79+
80+
BasicBlock getABasicBlockSuccessor(BasicBlock bb) {
81+
result = bb.(J::BasicBlock).getABBSuccessor()
82+
}
83+
84+
//TODO: support capture of `this` in lambdas
85+
class CapturedVariable instanceof LocalScopeVariable {
86+
CapturedVariable() {
87+
2 <=
88+
strictcount(J::Callable c |
89+
c = this.getCallable() or c = this.getAnAccess().getEnclosingCallable()
90+
)
91+
}
92+
93+
string toString() { result = super.toString() }
94+
95+
Callable getCallable() { result = super.getCallable() }
96+
97+
Location getLocation() { result = super.getLocation() }
98+
}
99+
100+
class CapturedParameter extends CapturedVariable instanceof Parameter { }
101+
102+
class Expr instanceof J::Expr {
103+
string toString() { result = super.toString() }
104+
105+
Location getLocation() { result = super.getLocation() }
106+
107+
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i) }
108+
}
109+
110+
class VariableWrite extends Expr instanceof VariableUpdate {
111+
CapturedVariable v;
112+
113+
VariableWrite() { super.getDestVar() = v }
114+
115+
CapturedVariable getVariable() { result = v }
116+
117+
Expr getSource() {
118+
result = this.(VariableAssign).getSource() or
119+
result = this.(AssignOp)
120+
}
121+
}
122+
123+
class VariableRead extends Expr instanceof RValue {
124+
CapturedVariable v;
125+
126+
VariableRead() { super.getVariable() = v }
127+
128+
CapturedVariable getVariable() { result = v }
129+
}
130+
131+
class ClosureExpr extends Expr instanceof ClassInstanceExpr {
132+
NestedClass nc;
133+
134+
ClosureExpr() {
135+
nc.(AnonymousClass).getClassInstanceExpr() = this
136+
or
137+
nc instanceof LocalClass and
138+
super.getConstructedType().getASourceSupertype*().getSourceDeclaration() = nc
139+
}
140+
141+
predicate hasBody(Callable body) { nc.getACallable() = body }
142+
143+
predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) }
144+
}
145+
146+
class Callable extends J::Callable {
147+
predicate isConstructor() { this instanceof Constructor }
148+
}
149+
}
150+
151+
class CapturedVariable = CaptureInput::CapturedVariable;
152+
153+
class CapturedParameter = CaptureInput::CapturedParameter;
154+
155+
module CaptureFlow = VariableCapture::Flow<CaptureInput>;
156+
157+
private CaptureFlow::ClosureNode asClosureNode(Node n) {
158+
result = n.(CaptureNode).getSynthesizedCaptureNode() or
159+
result.(CaptureFlow::ExprNode).getExpr() = n.asExpr() or
160+
result.(CaptureFlow::ExprPostUpdateNode).getExpr() =
161+
n.(PostUpdateNode).getPreUpdateNode().asExpr() or
162+
result.(CaptureFlow::ParameterNode).getParameter() = n.asParameter() or
163+
result.(CaptureFlow::ThisParameterNode).getCallable() = n.(InstanceParameterNode).getCallable() or
164+
exprNode(result.(CaptureFlow::MallocNode).getClosureExpr()).(PostUpdateNode).getPreUpdateNode() =
165+
n
166+
}
167+
168+
private predicate captureStoreStep(Node node1, CapturedVariableContent c, Node node2) {
169+
CaptureFlow::storeStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
170+
}
171+
172+
private predicate captureReadStep(Node node1, CapturedVariableContent c, Node node2) {
173+
CaptureFlow::readStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
174+
}
175+
176+
predicate captureValueStep(Node node1, Node node2) {
177+
CaptureFlow::localFlowStep(asClosureNode(node1), asClosureNode(node2))
178+
}
179+
74180
/**
75181
* Holds if data can flow from `node1` to `node2` through a field or
76182
* variable capture.
77183
*/
78184
predicate jumpStep(Node node1, Node node2) {
79185
fieldStep(node1, node2)
80186
or
81-
variableCaptureStep(node1, node2)
82-
or
83-
variableCaptureStep(node1.(PostUpdateNode).getPreUpdateNode(), node2)
84-
or
85187
any(AdditionalValueStep a).step(node1, node2) and
86188
node1.getEnclosingCallable() != node2.getEnclosingCallable()
87189
or
@@ -117,6 +219,8 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
117219
or
118220
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), f,
119221
node2.(FlowSummaryNode).getSummaryNode())
222+
or
223+
captureStoreStep(node1, f, node2)
120224
}
121225

122226
/**
@@ -149,6 +253,8 @@ predicate readStep(Node node1, ContentSet f, Node node2) {
149253
or
150254
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), f,
151255
node2.(FlowSummaryNode).getSummaryNode())
256+
or
257+
captureReadStep(node1, f, node2)
152258
}
153259

154260
/**
@@ -231,19 +337,29 @@ private newtype TDataFlowCallable =
231337
TSummarizedCallable(SummarizedCallable c) or
232338
TFieldScope(Field f)
233339

340+
/**
341+
* A callable or scope enclosing some number of data flow nodes. This can either
342+
* be a source callable, a synthesized callable for which we have a summary
343+
* model, or a synthetic scope for a field value node.
344+
*/
234345
class DataFlowCallable extends TDataFlowCallable {
346+
/** Gets the source callable corresponding to this callable, if any. */
235347
Callable asCallable() { this = TSrcCallable(result) }
236348

349+
/** Gets the summary model callable corresponding to this callable, if any. */
237350
SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
238351

352+
/** Gets the field corresponding to this callable, if it is a field value scope. */
239353
Field asFieldScope() { this = TFieldScope(result) }
240354

355+
/** Gets a textual representation of this callable. */
241356
string toString() {
242357
result = this.asCallable().toString() or
243358
result = "Synthetic: " + this.asSummarizedCallable().toString() or
244359
result = "Field scope: " + this.asFieldScope().toString()
245360
}
246361

362+
/** Gets the location of this callable. */
247363
Location getLocation() {
248364
result = this.asCallable().getLocation() or
249365
result = this.asSummarizedCallable().getLocation() or
@@ -406,6 +522,8 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
406522
*/
407523
predicate allowParameterReturnInSelf(ParameterNode p) {
408524
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
525+
or
526+
CaptureFlow::heuristicAllowInstanceParameterReturnInSelf(p.(InstanceParameterNode).getCallable())
409527
}
410528

411529
/** An approximated `Content`. */
@@ -447,6 +565,10 @@ ContentApprox getContentApprox(Content c) {
447565
or
448566
c instanceof MapValueContent and result = TMapValueContentApprox()
449567
or
568+
exists(CapturedVariable v |
569+
c = TCapturedVariableContent(v) and result = TCapturedVariableContentApprox(v)
570+
)
571+
or
450572
c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent()
451573
}
452574

0 commit comments

Comments
 (0)