Skip to content

Commit 3ac67c5

Browse files
authored
Merge pull request #22560 from asgerf/unified/dataflow2
Unified: Basic data flow and clear-text logging query
2 parents c32480d + 343459c commit 3ac67c5

34 files changed

Lines changed: 1521 additions & 8 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
private import unified
2+
private import codeql.unified.internal.dataflow.AllDataFlow
3+
private import codeql.dataflow.internal.DataFlowImplConsistency
4+
5+
module ConsistencyInput implements InputSig<Location, DataFlowInput> { }
6+
7+
module ConsistencyOutput =
8+
MakeConsistency<Location, DataFlowInput, TaintTrackingInput, ConsistencyInput>;
9+
10+
import ConsistencyOutput
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
private import unified
2+
private import codeql.unified.internal.dataflow.LocalSsa
3+
import LocalSsaOutput::Consistency

unified/ql/lib/codeql/unified/internal/AstExtra.qll

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,20 @@ module Public {
6060
TopLevelStmt() { this = any(TopLevel t).getBody().getAStmt() }
6161
}
6262

63+
/** An identifier appearing in the context of a break/continue label, argument/parameter name, or name of a member lookup. */
64+
final class IdentifierLabel extends Identifier {
65+
IdentifierLabel() {
66+
this = any(MemberAccessExpr e).getMemberNameNode() or
67+
this = any(Argument a).getNameNode() or
68+
this = any(Parameter p).getExternalNameNode() or
69+
this = any(LabeledStmt stmt).getLabelNameNode() or
70+
this = any(BreakExpr expr).getLabelNameNode() or
71+
this = any(ContinueExpr expr).getLabelNameNode()
72+
}
73+
}
74+
6375
/** An identifier appearing in the context of an expression, pattern, or type annotation. */
6476
final class IdentifierExpr extends Identifier {
65-
IdentifierExpr() {
66-
not this = any(MemberAccessExpr e).getMemberNameNode() and
67-
not this = any(Argument a).getNameNode() and
68-
not this = any(Parameter p).getExternalNameNode() and
69-
not this = any(LabeledStmt stmt).getLabelNameNode() and
70-
not this = any(BreakExpr expr).getLabelNameNode() and
71-
not this = any(ContinueExpr expr).getLabelNameNode()
72-
}
77+
IdentifierExpr() { not this instanceof IdentifierLabel }
7378
}
7479
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
private import unified
2+
private import NameBinding as NameBinding
3+
4+
/**
5+
* Holds if `expr` appears in the context of a type annotation.
6+
*/
7+
predicate isInTypeContext(Expr expr) {
8+
expr = any(TypeCastExpr n).getType()
9+
or
10+
expr = any(TypeTestExpr n).getType()
11+
or
12+
expr = any(VariableDeclaration n).getType()
13+
or
14+
expr = any(FunctionDeclaration n).getReturnType()
15+
or
16+
expr = any(FunctionExpr n).getReturnType()
17+
or
18+
expr = any(AccessorDeclaration n).getType()
19+
or
20+
expr = any(Parameter n).getType()
21+
or
22+
expr = any(TypeAliasDeclaration n).getType()
23+
or
24+
expr = any(BaseType n).getType()
25+
or
26+
expr = any(TypeParameter n).getBound()
27+
or
28+
expr = any(AssociatedTypeDeclaration n).getBound()
29+
or
30+
expr.getParent() instanceof TypeConstraint
31+
or
32+
isInTypeContext(expr.getEnclosingExpr())
33+
}
34+
35+
/** Holds if `e` appears in a name-binding position inside `declaration` */
36+
predicate isInBindingContext(Expr e, AstNode declaration) {
37+
NameBinding::bindingContext(e, _, declaration)
38+
}
39+
40+
/** Holds if `e` is part of the target of `assignment`. */
41+
predicate isInAssignmentContext(Expr e, AstNode assignment) {
42+
e = assignment.(AssignExpr).getTarget()
43+
or
44+
e = assignment.(CompoundAssignExpr).getTarget()
45+
or
46+
exists(TupleExpr tuple |
47+
isInAssignmentContext(tuple, assignment) and
48+
e = tuple.getAnElement().getValue()
49+
)
50+
}
51+
52+
/**
53+
* Holds if `e` receives an incoming value because it is part of a binding pattern
54+
* or assignment target.
55+
*/
56+
predicate hasIncomingValue(Expr e, AstNode declarationOrAssignment) {
57+
isInBindingContext(e, declarationOrAssignment)
58+
or
59+
isInAssignmentContext(e, declarationOrAssignment)
60+
}
61+
62+
/**
63+
* Holds if `e` evaluates to a result.
64+
*/
65+
predicate hasResultValue(Expr e) {
66+
not isInTypeContext(e) and
67+
not isInBindingContext(e, _) and
68+
not isInAssignmentContext(e, any(AssignExpr n)) and // non-compound assignment target
69+
not e instanceof IdentifierLabel
70+
}

unified/ql/lib/codeql/unified/internal/FacadeAst.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,5 +197,8 @@ module Unified {
197197
result = arg.getValue()
198198
)
199199
}
200+
201+
/** Gets the number of arguments passed to this call, not counting implicit arguments like receiver. */
202+
int getNumberOfArguments() { result = count(this.getAnArgument()) }
200203
}
201204
}

unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,9 @@ module Public {
667667
LocalVariable getImplicitQualifierVariable() {
668668
ResolveImplicitReceiverAccess::access(this, result)
669669
}
670+
671+
/** Gets the simple name of this identifier, that is, the name of the member being accessed. */
672+
string getName() { result = this.getValue() }
670673
}
671674
}
672675

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/** Re-exports all the files in the internal dataflow folder (except DataFlowPublic). */
2+
3+
import Content
4+
import DataFlowGraph
5+
import DataFlowInstantiation
6+
import DataFlowNode
7+
import DataFlowPlugin
8+
import Step
9+
import LocalSsa
10+
import TaintTrackingInstantiation
11+
import VariableRefKind
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
private import unified
2+
private import AllDataFlow
3+
4+
private newtype TContent =
5+
TNamedMember(string name) {
6+
name = any(Identifier id).getValue()
7+
or
8+
// Tuple elements can be accessed as named members, e.g. `tuple.0`, `tuple.1`, etc,
9+
// so just model their elements as named members.
10+
name = [0 .. 20].toString()
11+
}
12+
13+
class Content extends TContent {
14+
string asNamedMember() { this = TNamedMember(result) }
15+
16+
string toString() { result = this.asNamedMember() }
17+
18+
Location getLocation() { none() }
19+
}
20+
21+
private newtype TContentSet = TSingleton(Content content)
22+
23+
class ContentSet extends TContentSet {
24+
Content asSingleton() { this = TSingleton(result) }
25+
26+
string toString() { result = this.asSingleton().toString() }
27+
28+
Location getLocation() { result = this.asSingleton().getLocation() }
29+
30+
Content getAStoreContent() { result = this.asSingleton() }
31+
32+
Content getAReadContent() { result = this.asSingleton() }
33+
}
34+
35+
module ContentSet {
36+
ContentSet namedMember(string name) { result.asSingleton().asNamedMember() = name }
37+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
private import unified
2+
private import AllDataFlow
3+
4+
predicate step(Node node1, Step step, Node node2) {
5+
any(DataFlowPlugin p).step(node1, step, node2)
6+
or
7+
exists(VariableDeclaration decl |
8+
node1.isResultValue(decl.getValue()) and
9+
step.value() and
10+
node2.isIncomingValue(decl.getPattern())
11+
)
12+
or
13+
exists(AssignExpr assign |
14+
node1.isResultValue(assign.getValue()) and
15+
step.value() and
16+
node2.isIncomingValue(assign.getTarget())
17+
)
18+
or
19+
exists(LocalVariableAccess access |
20+
node1.isLocalVariableRead(access, access.getLocalVariable()) and
21+
step.value() and
22+
node2.isResultValue(access)
23+
or
24+
node1.isIncomingValue(access) and
25+
step.value() and
26+
node2.isLocalVariableWrite(access, access.getLocalVariable())
27+
or
28+
node1.isPostUpdate(access) and
29+
step.value() and
30+
node2.isLocalVariablePostUpdate(access, access.getLocalVariable())
31+
)
32+
or
33+
exists(UnqualifiedMemberAccess access | access.isInstanceAccess() |
34+
node1.isLocalVariableRead(access, access.getImplicitQualifierVariable()) and
35+
step.readName(access.getName()) and
36+
node2.isResultValue(access)
37+
or
38+
(node1.isIncomingValue(access) or node1.isPostUpdate(access)) and
39+
step.storeName(access.getName()) and
40+
node2.isLocalVariablePostUpdate(access, access.getImplicitQualifierVariable())
41+
)
42+
or
43+
exists(StringInterpolationExpr expr |
44+
node1.isResultValue(expr.getAnElement()) and
45+
step.taint() and
46+
node2.isResultValue(expr)
47+
)
48+
or
49+
exists(TupleExpr expr, int i |
50+
node1.isResultValue(expr.getElement(i).getValue()) and
51+
step.storeName(i.toString()) and
52+
node2.isResultValue(expr)
53+
or
54+
node1.isIncomingValue(expr) and
55+
step.readName(i.toString()) and
56+
node2.isIncomingValue(expr.getElement(i).getValue())
57+
)
58+
or
59+
exists(MemberAccessExpr expr |
60+
node1.isResultValue(expr.getBase()) and
61+
step.readName(expr.getMemberName()) and
62+
node2.isResultValue(expr)
63+
or
64+
(node1.isIncomingValue(expr) or node1.isPostUpdate(expr)) and
65+
step.storeName(expr.getMemberName()) and
66+
node2.isPostUpdate(expr.getBase())
67+
)
68+
or
69+
none() // Temporarily disable compilation errors from unsatisfiable types
70+
}
71+
72+
/** Holds if `node` should be included in the debug view. */
73+
private signature predicate relevantNodeSig(AstNode node);
74+
75+
module DebugGraph<relevantNodeSig/1 relevantNode> {
76+
private Node adjacent(Node n) {
77+
step(n, _, result)
78+
or
79+
step(result, _, n)
80+
or
81+
localSsaStep(n, result, _)
82+
or
83+
localSsaStep(result, n, _)
84+
}
85+
86+
private predicate relevantDataFlowNode(Node node) {
87+
relevantNode(node.getWrappedAstNode())
88+
or
89+
not exists(node.getWrappedAstNode()) and
90+
relevantDataFlowNode(adjacent(node))
91+
}
92+
93+
query predicate nodes(Node node, string key, string value) {
94+
relevantDataFlowNode(node) and
95+
key = "semmle.label" and
96+
value = node.toString()
97+
}
98+
99+
query predicate edges(Node node1, Node node2, string key, string value) {
100+
key = "semmle.label" and
101+
relevantDataFlowNode(node1) and
102+
relevantDataFlowNode(node2) and
103+
(
104+
exists(Step step |
105+
step(node1, step, node2) and
106+
value = step.toString()
107+
)
108+
or
109+
exists(boolean isUseStep |
110+
localSsaStep(node1, node2, isUseStep) and
111+
if isUseStep = true then value = "use-use" else value = "def-use"
112+
)
113+
or
114+
node2 = getPostUpdateNode(node1) and
115+
value = "post-update"
116+
)
117+
}
118+
}

0 commit comments

Comments
 (0)