Skip to content

Commit 10eb548

Browse files
authored
Merge pull request #10699 from MathiasVP/swift-mad-summaries
2 parents 28fa06a + cfbb9e3 commit 10eb548

File tree

15 files changed

+438
-121
lines changed

15 files changed

+438
-121
lines changed

swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ private import internal.FlowSummaryImplSpecific
8080
private module Frameworks {
8181
private import codeql.swift.frameworks.StandardLibrary.String
8282
private import codeql.swift.frameworks.StandardLibrary.Url
83+
private import codeql.swift.frameworks.StandardLibrary.UrlSession
8384
}
8485

8586
/**
@@ -410,8 +411,10 @@ pragma[nomagic]
410411
private Element interpretElement0(
411412
string namespace, string type, boolean subtypes, string name, string signature
412413
) {
414+
elementSpec(namespace, type, subtypes, name, signature, _) and
413415
namespace = "" and // TODO: Fill out when we properly extract modules.
414416
(
417+
// Non-member functions
415418
exists(AbstractFunctionDecl func |
416419
func.getName() = name and
417420
type = "" and
@@ -421,10 +424,11 @@ private Element interpretElement0(
421424
result = func
422425
)
423426
or
427+
// Member functions
424428
exists(NominalType nomType, IterableDeclContext decl, MethodDecl method |
425429
method.getName() = name and
426430
method = decl.getAMember() and
427-
nomType.getName() = type and
431+
nomType.getFullName() = type and
428432
matchesSignature(method, signature) and
429433
result = method
430434
|
@@ -434,6 +438,20 @@ private Element interpretElement0(
434438
subtypes = false and
435439
getDeclType(decl) = nomType
436440
)
441+
or
442+
signature = "" and
443+
exists(NominalType nomType, IterableDeclContext decl, FieldDecl field |
444+
field.getName() = name and
445+
field = decl.getAMember() and
446+
nomType.getFullName() = type and
447+
result = field
448+
|
449+
subtypes = true and
450+
getDeclType(decl) = nomType.getADerivedType*()
451+
or
452+
subtypes = false and
453+
getDeclType(decl) = nomType
454+
)
437455
)
438456
}
439457

@@ -447,11 +465,18 @@ Element interpretElement(
447465
)
448466
}
449467

450-
/**
451-
* Holds if `c` has a `generated` summary.
452-
*/
453-
predicate hasSummary(SummarizedCallable c, boolean generated) {
454-
summaryElement(c, _, _, _, generated)
468+
private predicate parseField(AccessPathToken c, Content::FieldContent f) {
469+
exists(string fieldRegex, string name |
470+
c.getName() = "Field" and
471+
fieldRegex = "^([^.]+)$" and
472+
name = c.getAnArgument().regexpCapture(fieldRegex, 1) and
473+
f.getField().getName() = name
474+
)
475+
}
476+
477+
/** Holds if the specification component parses as a `Content`. */
478+
predicate parseContent(AccessPathToken component, Content content) {
479+
parseField(component, content)
455480
}
456481

457482
cached

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,9 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
229229
cached
230230
private module Cached {
231231
cached
232-
newtype TDataFlowCallable = TDataFlowFunc(CfgScope scope)
232+
newtype TDataFlowCallable =
233+
TDataFlowFunc(CfgScope scope) or
234+
TSummarizedCallable(FlowSummary::SummarizedCallable c)
233235

234236
/** Gets a viable run-time target for the call `call`. */
235237
cached
@@ -241,6 +243,8 @@ private module Cached {
241243
result = TDataFlowFunc(call.(PropertySetterCall).getAccessorDecl())
242244
or
243245
result = TDataFlowFunc(call.(PropertyObserverCall).getAccessorDecl())
246+
or
247+
result = TSummarizedCallable(call.asCall().getStaticTarget())
244248
}
245249

246250
cached

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ private module Cached {
6464
TExprNode(CfgNode n, Expr e) { hasExprNode(n, e) } or
6565
TSsaDefinitionNode(Ssa::Definition def) or
6666
TInoutReturnNode(ParamDecl param) { modifiableParam(param) } or
67-
TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) or
67+
TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) {
68+
FlowSummaryImpl::Private::summaryNodeRange(c, state)
69+
} or
70+
TSourceParameterNode(ParamDecl param) or
71+
TSummaryParameterNode(FlowSummary::SummarizedCallable c, ParameterPosition pos) {
72+
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
73+
} or
6874
TExprPostUpdateNode(CfgNode n) {
6975
// Obviously, the base of setters needs a post-update node
7076
n = any(PropertySetterCfgNode setter).getBase()
@@ -93,6 +99,20 @@ private module Cached {
9399
)
94100
}
95101

102+
private SsaDefinitionNode getParameterDefNode(ParamDecl p) {
103+
exists(BasicBlock bb, int i |
104+
bb.getNode(i).getNode().asAstNode() = p and
105+
result.asDefinition().definesAt(_, bb, i)
106+
)
107+
}
108+
109+
/**
110+
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
111+
*/
112+
private predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
113+
nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
114+
}
115+
96116
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
97117
exists(Ssa::Definition def |
98118
// Step from assignment RHS to def
@@ -117,6 +137,8 @@ private module Cached {
117137
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
118138
)
119139
or
140+
localFlowSsaParamInput(nodeFrom, nodeTo)
141+
or
120142
// flow through `&` (inout argument)
121143
nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr()
122144
or
@@ -125,6 +147,9 @@ private module Cached {
125147
or
126148
// flow through `!`
127149
nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr()
150+
or
151+
// flow through a flow summary (extension of `SummaryModelCsv`)
152+
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
128153
}
129154

130155
/**
@@ -138,7 +163,10 @@ private module Cached {
138163

139164
/** This is the local flow predicate that is exposed. */
140165
cached
141-
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { localFlowStepCommon(nodeFrom, nodeTo) }
166+
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
167+
localFlowStepCommon(nodeFrom, nodeTo) or
168+
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
169+
}
142170

143171
cached
144172
newtype TContentSet = TSingletonContent(Content c)
@@ -181,17 +209,15 @@ predicate nodeIsHidden(Node n) { none() }
181209
private module ParameterNodes {
182210
abstract class ParameterNodeImpl extends NodeImpl {
183211
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
212+
213+
/** Gets the parameter associated with this node, if any. */
214+
ParamDecl getParameter() { none() }
184215
}
185216

186-
class NormalParameterNode extends ParameterNodeImpl, SsaDefinitionNodeImpl {
217+
class SourceParameterNode extends ParameterNodeImpl, TSourceParameterNode {
187218
ParamDecl param;
188219

189-
NormalParameterNode() {
190-
exists(BasicBlock bb, int i |
191-
super.asDefinition().definesAt(param, bb, i) and
192-
bb.getNode(i).getNode().asAstNode() = param
193-
)
194-
}
220+
SourceParameterNode() { this = TSourceParameterNode(param) }
195221

196222
override Location getLocationImpl() { result = param.getLocation() }
197223

@@ -206,6 +232,26 @@ private module ParameterNodes {
206232
}
207233

208234
override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) }
235+
236+
override ParamDecl getParameter() { result = param }
237+
}
238+
239+
class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
240+
FlowSummary::SummarizedCallable sc;
241+
ParameterPosition pos;
242+
243+
SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) }
244+
245+
override predicate isParameterOf(DataFlowCallable c, ParameterPosition p) {
246+
c.getUnderlyingCallable() = sc and
247+
p = pos
248+
}
249+
250+
override Location getLocationImpl() { result = sc.getLocation() }
251+
252+
override string toStringImpl() { result = "[summary param] " + pos + " in " + sc }
253+
254+
override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) }
209255
}
210256
}
211257

@@ -300,6 +346,14 @@ private module ArgumentNodes {
300346
)
301347
}
302348
}
349+
350+
class SummaryArgumentNode extends SummaryNode, ArgumentNode {
351+
SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) }
352+
353+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
354+
FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos)
355+
}
356+
}
303357
}
304358

305359
import ArgumentNodes
@@ -370,6 +424,12 @@ private module OutNodes {
370424
}
371425
}
372426

427+
class SummaryOutNode extends OutNode, SummaryNode {
428+
override DataFlowCall getCall(ReturnKind kind) {
429+
FlowSummaryImpl::Private::summaryOutNode(result, this, kind)
430+
}
431+
}
432+
373433
class InOutUpdateArgNode extends OutNode, ExprPostUpdateNode {
374434
Argument arg;
375435

@@ -435,6 +495,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
435495
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = ref.getBase() and
436496
c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember()))
437497
)
498+
or
499+
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
438500
}
439501

440502
predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e }
@@ -556,6 +618,9 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
556618
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
557619
kind = TLambdaCallKind() and
558620
receiver.asExpr() = call.asCall().getExpr().(ApplyExpr).getFunction()
621+
or
622+
kind = TLambdaCallKind() and
623+
receiver = call.(SummaryCall).getReceiver()
559624
}
560625

561626
/** Extra data-flow steps needed for lambda flow analysis. */

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,15 @@ class ExprNode extends Node, TExprNode {
7070
* The value of a parameter at function entry, viewed as a node in a data
7171
* flow graph.
7272
*/
73-
class ParameterNode extends Node, SsaDefinitionNode instanceof ParameterNodeImpl { }
73+
class ParameterNode extends Node instanceof ParameterNodeImpl {
74+
override ControlFlowNode getCfgNode() { result = this.(ParameterNodeImpl).getCfgNode() }
75+
76+
DataFlowCallable getDeclaringFunction() {
77+
result = this.(ParameterNodeImpl).getEnclosingCallable()
78+
}
79+
80+
ParamDecl getParameter() { result = this.(ParameterNodeImpl).getParameter() }
81+
}
7482

7583
/**
7684
*/

swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,19 @@ predicate sinkElement(Element e, string input, string kind, boolean generated) {
105105
/** Gets the summary component for specification component `c`, if any. */
106106
bindingset[c]
107107
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
108-
none() // TODO once we have field flow
108+
exists(ContentSet cs, Content content |
109+
cs.isSingleton(content) and
110+
parseContent(c, content) and
111+
result = SummaryComponent::content(cs)
112+
)
109113
}
110114

111115
/** Gets the textual representation of the content in the format used for flow summaries. */
112-
private string getContentSpecificCsv(ContentSet c) {
113-
none() // TODO once we have field flow
116+
private string getContentSpecificCsv(ContentSet cs) {
117+
exists(Content::FieldContent c |
118+
cs.isSingleton(c) and
119+
result = "Field[" + c.getField().getName() + "]"
120+
)
114121
}
115122

116123
/** Gets the textual representation of a summary component in the format used for flow summaries. */
@@ -182,10 +189,17 @@ class InterpretNode extends TInterpretNode {
182189
}
183190
}
184191

185-
/** Provides additional sink specification logic required for attributes. */
186-
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
192+
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
193+
// Allow fields to be picked as output nodes.
194+
exists(Node n, AstNode ast |
195+
n = node.asNode() and
196+
ast = mid.asElement()
197+
|
198+
c = "" and
199+
n.asExpr().(MemberRefExpr).getMember() = ast
200+
)
201+
}
187202

188-
/** Provides additional sink specification logic required for attributes. */
189203
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) { none() }
190204

191205
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */

swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import TaintTrackingPublic
44
private import codeql.swift.dataflow.DataFlow
55
private import codeql.swift.dataflow.Ssa
66
private import codeql.swift.controlflow.CfgNodes
7+
private import FlowSummaryImpl as FlowSummaryImpl
78

89
/**
910
* Holds if `node` should be a sanitizer in all global taint flow configurations
@@ -48,15 +49,8 @@ private module Cached {
4849
ae.getType().getName() = "String"
4950
)
5051
or
51-
// allow flow through `URL.init`.
52-
exists(CallExpr call, StructDecl c, AbstractFunctionDecl f |
53-
c.getName() = "URL" and
54-
c.getAMember() = f and
55-
f.getName() = ["init(string:)", "init(string:relativeTo:)"] and
56-
call.getStaticTarget() = f and
57-
nodeFrom.asExpr() = call.getAnArgument().getExpr() and
58-
nodeTo.asExpr() = call
59-
)
52+
// flow through a flow summary (extension of `SummaryModelCsv`)
53+
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
6054
}
6155

6256
/**

swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import codeql.swift.generated.decl.TypeDecl
22
private import codeql.swift.generated.type.Type
33
private import codeql.swift.elements.type.AnyGenericType
4+
private import swift
45

56
class TypeDecl extends TypeDeclBase {
67
override string toString() { result = this.getName() }
@@ -12,4 +13,23 @@ class TypeDecl extends TypeDeclBase {
1213
TypeDecl getDerivedTypeDecl(int i) { result.getBaseTypeDecl(i) = this }
1314

1415
TypeDecl getADerivedTypeDecl() { result = this.getDerivedTypeDecl(_) }
16+
17+
/**
18+
* Gets the full name of this `TypeDecl`. For example in:
19+
* ```swift
20+
* struct A {
21+
* struct B {
22+
* // ...
23+
* }
24+
* }
25+
* ```
26+
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
27+
* full name of `B` is `A.B`.
28+
*/
29+
string getFullName() {
30+
not this.getEnclosingDecl() instanceof TypeDecl and
31+
result = this.getName()
32+
or
33+
result = this.getEnclosingDecl().(TypeDecl).getFullName() + "." + this.getName()
34+
}
1535
}

swift/ql/lib/codeql/swift/elements/type/NominalType.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,18 @@ class NominalType extends NominalTypeBase {
55
NominalType getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
66

77
NominalType getADerivedType() { result.getABaseType() = this }
8+
9+
/**
10+
* Gets the full name of this `NominalType`. For example in:
11+
* ```swift
12+
* struct A {
13+
* struct B {
14+
* // ...
15+
* }
16+
* }
17+
* ```
18+
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
19+
* full name of `B` is `A.B`.
20+
*/
21+
string getFullName() { result = this.getDeclaration().(NominalTypeDecl).getFullName() }
822
}

0 commit comments

Comments
 (0)