Skip to content

Commit bc40a49

Browse files
committed
Rust: Track closure types in data flow
1 parent ee54aa5 commit bc40a49

File tree

2 files changed

+81
-12
lines changed

2 files changed

+81
-12
lines changed

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,75 @@ predicate lambdaCallExpr(CallExprImpl::DynamicCallExpr call, LambdaCallKind kind
263263
exists(kind)
264264
}
265265

266+
// NOTE: We do not yet track type information, except for closures where
267+
// we use the closure itself to represent the unique type.
268+
final class DataFlowType extends TDataFlowType {
269+
Expr asClosureExpr() { this = TClosureExprType(result) }
270+
271+
predicate isUnknownType() { this = TUnknownType() }
272+
273+
predicate isSourceContextParameterType() { this = TSourceContextParameterType() }
274+
275+
string toString() {
276+
exists(this.asClosureExpr()) and
277+
result = "... => .."
278+
or
279+
this.isUnknownType() and
280+
result = ""
281+
or
282+
this.isSourceContextParameterType() and
283+
result = "<source context parameter type>"
284+
}
285+
}
286+
287+
pragma[nomagic]
288+
private predicate compatibleTypesLeft(DataFlowType t1, DataFlowType t2) {
289+
t1.isUnknownType()
290+
or
291+
t1.asClosureExpr() = t2.asClosureExpr()
292+
or
293+
t1.isSourceContextParameterType() and not exists(t2.asClosureExpr())
294+
}
295+
296+
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
297+
compatibleTypesLeft(t1, t2)
298+
or
299+
compatibleTypesLeft(t2, t1)
300+
}
301+
302+
pragma[nomagic]
303+
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { exists(t1) and t2.isUnknownType() }
304+
305+
DataFlowType getNodeType(NodePublic node) {
306+
result.asClosureExpr() = node.asExpr()
307+
or
308+
result.asClosureExpr() = node.(ClosureParameterNode).getCfgScope()
309+
or
310+
exists(VariableCapture::Flow::SynthesizedCaptureNode scn |
311+
scn = node.(CaptureNode).getSynthesizedCaptureNode() and
312+
if scn.isInstanceAccess()
313+
then result.asClosureExpr() = scn.getEnclosingCallable()
314+
else result.isUnknownType()
315+
)
316+
or
317+
not lambdaCreationExpr(node.asExpr()) and
318+
not node instanceof ClosureParameterNode and
319+
not node instanceof CaptureNode and
320+
result.isUnknownType()
321+
}
322+
266323
// Defines a set of aliases needed for the `RustDataFlow` module
267324
private module Aliases {
268325
class DataFlowCallableAlias = DataFlowCallable;
269326

327+
class DataFlowTypeAlias = DataFlowType;
328+
329+
predicate compatibleTypesAlias = compatibleTypes/2;
330+
331+
predicate typeStrongerThanAlias = typeStrongerThan/2;
332+
333+
predicate getNodeTypeAlias = getNodeType/1;
334+
270335
class ReturnKindAlias = ReturnKind;
271336

272337
class DataFlowCallAlias = DataFlowCall;
@@ -398,8 +463,6 @@ module RustDataFlowGen<RustDataFlowInputSig Input> implements InputSig<Location>
398463
result = node.(Node::Node).getEnclosingCallable()
399464
}
400465

401-
DataFlowType getNodeType(Node node) { any() }
402-
403466
predicate nodeIsHidden(Node node) {
404467
node instanceof SsaNode or
405468
node.(FlowSummaryNode).getSummaryNode().isHidden() or
@@ -486,15 +549,15 @@ module RustDataFlowGen<RustDataFlowInputSig Input> implements InputSig<Location>
486549
*/
487550
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) }
488551

489-
// NOTE: For now we use the type `Unit` and do not benefit from type
490-
// information in the data flow analysis.
491-
final class DataFlowType extends Unit {
492-
string toString() { result = "" }
493-
}
552+
class DataFlowType = DataFlowTypeAlias;
494553

495-
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
554+
predicate compatibleTypes = compatibleTypesAlias/2;
496555

497-
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
556+
predicate typeStrongerThan = typeStrongerThanAlias/2;
557+
558+
DataFlowType getSourceContextParameterNodeType() { result.isSourceContextParameterType() }
559+
560+
predicate getNodeType = getNodeTypeAlias/1;
498561

499562
class Content = ContentAlias;
500563

@@ -1110,6 +1173,12 @@ private module Cached {
11101173
TCfgScope(CfgScope scope) or
11111174
TSummarizedCallable(SummarizedCallable c)
11121175

1176+
cached
1177+
newtype TDataFlowType =
1178+
TClosureExprType(Expr e) { lambdaCreationExpr(e) } or
1179+
TUnknownType() or
1180+
TSourceContextParameterType()
1181+
11131182
/** This is the local flow predicate that is exposed. */
11141183
cached
11151184
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {

rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ private module Debug {
280280
private import Content
281281
private import codeql.rust.dataflow.internal.DataFlowImpl
282282
private import codeql.rust.internal.typeinference.TypeMention
283-
private import codeql.rust.internal.typeinference.Type
283+
private import codeql.rust.internal.typeinference.Type as Type
284284

285285
private predicate relevantManualModel(SummarizedCallableImpl sc, string can) {
286286
exists(Provenance manual |
@@ -298,7 +298,7 @@ private module Debug {
298298
sc.propagatesFlow(input, _, _, _, _, _) and
299299
input.head() = SummaryComponent::argument(pos) and
300300
p = pos.getParameterIn(sc.getParamList()) and
301-
tm.getType() instanceof RefType and
301+
tm.getType() instanceof Type::RefType and
302302
not input.tail().head() = SummaryComponent::content(TSingletonContentSet(TReferenceContent()))
303303
|
304304
tm = p.getTypeRepr()
@@ -313,7 +313,7 @@ private module Debug {
313313
exists(TypeMention tm |
314314
relevantManualModel(sc, can) and
315315
sc.propagatesFlow(_, output, _, _, _, _) and
316-
tm.getType() instanceof RefType and
316+
tm.getType() instanceof Type::RefType and
317317
output.head() = SummaryComponent::return(_) and
318318
not output.tail().head() =
319319
SummaryComponent::content(TSingletonContentSet(TReferenceContent())) and

0 commit comments

Comments
 (0)