Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions unified/ql/lib/codeql/unified/internal/dataflow/Content.qll
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
private import unified
private import AllDataFlow
private import codeql.unified.internal.typeinference.TypeInference as T
private import codeql.unified.internal.typeinference.TypeInferencePlugin as TP

/**
* Holds if a MaD access path token of form `name[arg]` exists.
Expand All @@ -8,8 +10,20 @@ predicate isAccessPathTokenPresent(string name, string arg) {
arg = any(FlowSummaryOutput::Private::AccessPathToken tok).getAnArgument(name)
}

private class ResolvedField extends VariableDeclaration {
ResolvedField() { this = T::resolveFieldAccess(_) }

string getName() {
result = super.getName() and
not this = any(TP::TupleType tt).getField(_)
or
this = any(TP::TupleType tt).getField(result)
}
}

private newtype TContent =
TArrayElement() or
TFieldContent(ResolvedField field) or
TNamedMember(string name) {
name = any(Identifier id).getValue()
or
Expand All @@ -24,23 +38,35 @@ private newtype TContent =
TCapturedVariable(CaptureSsaInput::CapturedVariable v)

class Content extends TContent {
ResolvedField asField() { this = TFieldContent(result) }

string asNamedMember() { this = TNamedMember(result) }

predicate isArrayElement() { this = TArrayElement() }

string toString() {
result = this.asNamedMember()
or
result = this.asField().getName()
or
this.isArrayElement() and result = "ArrayElement"
or
result = this.asCapturedVariable().toString()
}

LocalVariable asCapturedVariable() { this = TCapturedVariable(result) }

Location getLocation() { result = this.asCapturedVariable().getLocation() }
Location getLocation() {
result = this.asCapturedVariable().getLocation()
or
result = this.asField().getLocation()
}
}

private class TContentApprox = TArrayElement or TNamedMember or TCapturedVariable;

class ContentApprox extends Content, TContentApprox { }

private newtype TContentSet = TSingleton(Content content)

class ContentSet extends TContentSet {
Expand All @@ -52,10 +78,35 @@ class ContentSet extends TContentSet {

Content getAStoreContent() { result = this.asSingleton() }

Content getAReadContent() { result = this.asSingleton() }
Content getAReadContent() {
result = this.asSingleton()
or
result.asNamedMember() = this.asSingleton().asField().getName()
or
result.asField().getName() = this.asSingleton().asNamedMember()
Comment thread
hvitved marked this conversation as resolved.
Resolved
}
}

module ContentSet {
/**
* Gets the resolved field content for `access`, falling back to name-based content when resolution
* is missing.
*/
ContentSet member(Expr access) {
result.asSingleton().asField() = T::resolveFieldAccess(access)
or
not exists(T::resolveFieldAccess(access)) and
not exists(T::resolveAccessorAccess(access)) and
exists(string name |
name =
[
access.(MemberAccessExpr).getMemberName(),
access.(UnqualifiedMemberAccess).getMemberName()
] and
result = namedMember(name)
)
}

ContentSet namedMember(string name) { result.asSingleton().asNamedMember() = name }

ContentSet arrayElement() { result.asSingleton().isArrayElement() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ predicate step(BuilderNode node1, Step step, BuilderNode node2) {
or
exists(UnqualifiedMemberAccess access | access.isInstanceAccess() |
node1.isLocalVariableRead(access, access.getImplicitQualifierVariable()) and
step.readName(access.getMemberName()) and
step.read(ContentSet::member(access)) and
node2.isResultValue(access)
or
(node1.isIncomingValue(access) or node1.isPostUpdate(access)) and
step.storeName(access.getMemberName()) and
step.store(ContentSet::member(access)) and
node2.isLocalVariablePostUpdate(access, access.getImplicitQualifierVariable())
or
// Pass the receiver along to the getter/setter invoked by this expression
Expand Down Expand Up @@ -129,11 +129,11 @@ predicate step(BuilderNode node1, Step step, BuilderNode node2) {
or
exists(MemberAccessExpr expr |
node1.isResultValue(expr.getBase()) and
step.readName(expr.getMemberName()) and
step.read(ContentSet::member(expr)) and
node2.isResultValue(expr)
or
(node1.isIncomingValue(expr) or node1.isPostUpdate(expr)) and
step.storeName(expr.getMemberName()) and
step.store(ContentSet::member(expr)) and
node2.isPostUpdate(expr.getBase())
or
// Pass the receiver along to the getter/setter invoked by this expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ module DataFlowInput implements InputSig<Location> {

class ContentSet = D::ContentSet;

class ContentApprox = Content; // TODO
class ContentApprox = D::ContentApprox;

ContentApprox getContentApprox(Content c) { result = c } // TODO
ContentApprox getContentApprox(Content c) {
result = c
or
result.asNamedMember() = c.asField().getName()
}

//
// Parameter, argument, return, and out nodes and their positions/kinds
Expand Down
89 changes: 89 additions & 0 deletions unified/ql/test/library-tests/dataflow/fields.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
func source(_ s: String) -> String { return s }

@discardableResult
func sink(_ s: Any) -> String { return "" }

class FieldBox {
var field: String = ""
}

class OtherFieldBox {
var field: String = ""
}

class InheritedFieldBox: FieldBox {}

func resolvedFields() {
let box = FieldBox()
box.field = source("fields.resolved")
sink(box.field) // $ hasValueFlow=fields.resolved
sink((box as! OtherFieldBox).field) // no flow
}

func inheritedFields() {
let box = InheritedFieldBox()
box.field = source("fields.inherited")
sink((box as! FieldBox).field) // $ hasValueFlow=fields.inherited
}

func unresolvedFieldRead() {
let box = FieldBox()
box.field = source("fields.unresolved-read")
sink((box as! UnknownFieldBox).field) // $ hasValueFlow=fields.unresolved-read
}

func unresolvedFieldStore() {
let box = FieldBox()
(box as! UnknownFieldBox).field = source("fields.unresolved-store")
sink(box.field) // $ hasValueFlow=fields.unresolved-store
}

func summarizedFieldRead() {
let box = FieldBox()
box.field = source("fields.summary")
sink(TestType.readField(box)) // $ hasValueFlow=fields.summary
}

class BaseBox {
var field: String = ""
}

class OverridingBox: BaseBox {
override var field: String {
get { super.field }
set { super.field = newValue }
}
}

func overriddenFieldRead() {
let base: BaseBox = OverridingBox()
base.field = source("fields.override-read")
let sub = base as! OverridingBox
sink(sub.field) // $ hasValueFlow=fields.override-read
}

protocol FieldProtocol {
var field: String { get set }
}

class ProtocolBox: FieldProtocol {
var field: String = ""
}

func protocolFieldRead() {
let box = ProtocolBox()
box.field = source("fields.protocol")
sink(box.field) // $ hasValueFlow=fields.protocol
let p = box as FieldProtocol
sink(p.field) // $ MISSING: hasValueFlow=fields.protocol -- missing dispatch from `FieldProtocol.get_field` to `ProtocolBox.field`
}

func protocolFieldWrite() {
let box = ProtocolBox()
var p = box as FieldProtocol
p.field = source("fields.protocol")
sink(p.field) // $ MISSING: hasValueFlow=fields.protocol -- missing dispatch from `FieldProtocol.{get,set}_field` to `ProtocolBox.field`
sink(box.field) // $ MISSING: hasValueFlow=fields.protocol (aliasing)
let box2 = p as! OtherFieldBox
sink(box2.field) // no flow
}
85 changes: 85 additions & 0 deletions unified/ql/test/library-tests/dataflow/test.expected
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ edges
| accessors_instance.swift:119:13:119:17 | field | accessors_instance.swift:125:18:125:22 | field | provenance | |
| accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:119:13:119:17 | field | provenance | |
| accessors_instance.swift:134:24:134:31 | newValue [0] | accessors_instance.swift:134:24:134:33 | ... .0 | provenance | |
| accessors_instance.swift:134:24:134:31 | newValue [0] | accessors_instance.swift:134:24:134:33 | ... .0 | provenance | |
| accessors_instance.swift:143:5:143:7 | [post] ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | |
| accessors_instance.swift:143:5:143:9 | ... .0 | accessors_instance.swift:143:5:143:7 | [post] ... .p [0] | provenance | |
| accessors_instance.swift:143:13:143:27 | source(...) | accessors_instance.swift:143:5:143:9 | ... .0 | provenance | |
Expand Down Expand Up @@ -251,6 +252,40 @@ edges
| enums.swift:82:12:82:12 | e [foo.0] | enums.swift:83:10:83:23 | ... .foo(...) [foo.0] | provenance | |
| enums.swift:83:10:83:23 | ... .foo(...) [foo.0] | enums.swift:83:22:83:22 | x | provenance | |
| enums.swift:83:22:83:22 | x | enums.swift:84:14:84:14 | x | provenance | |
| fields.swift:18:5:18:7 | [post] box [field] | fields.swift:19:10:19:12 | box [field] | provenance | |
| fields.swift:18:5:18:13 | ... .field | fields.swift:18:5:18:7 | [post] box [field] | provenance | |
| fields.swift:18:17:18:41 | source(...) | fields.swift:18:5:18:13 | ... .field | provenance | |
| fields.swift:19:10:19:12 | box [field] | fields.swift:19:10:19:18 | ... .field | provenance | |
| fields.swift:25:5:25:7 | [post] box [field] | fields.swift:26:11:26:13 | box [field] | provenance | |
| fields.swift:25:5:25:13 | ... .field | fields.swift:25:5:25:7 | [post] box [field] | provenance | |
| fields.swift:25:17:25:42 | source(...) | fields.swift:25:5:25:13 | ... .field | provenance | |
| fields.swift:26:11:26:13 | box [field] | fields.swift:26:11:26:26 | TypeCastExpr [field] | provenance | |
| fields.swift:26:11:26:26 | TypeCastExpr [field] | fields.swift:26:10:26:33 | ... .field | provenance | |
| fields.swift:31:5:31:7 | [post] box [field] | fields.swift:32:11:32:13 | box [field] | provenance | |
| fields.swift:31:5:31:13 | ... .field | fields.swift:31:5:31:7 | [post] box [field] | provenance | |
| fields.swift:31:17:31:48 | source(...) | fields.swift:31:5:31:13 | ... .field | provenance | |
| fields.swift:32:11:32:13 | box [field] | fields.swift:32:11:32:33 | TypeCastExpr [field] | provenance | |
| fields.swift:32:11:32:33 | TypeCastExpr [field] | fields.swift:32:10:32:40 | ... .field | provenance | |
| fields.swift:37:5:37:35 | ... .field | fields.swift:37:6:37:28 | [post] TypeCastExpr [field] | provenance | |
| fields.swift:37:6:37:28 | [post] TypeCastExpr [field] | fields.swift:38:10:38:12 | box [field] | provenance | |
| fields.swift:37:39:37:71 | source(...) | fields.swift:37:5:37:35 | ... .field | provenance | |
| fields.swift:38:10:38:12 | box [field] | fields.swift:38:10:38:18 | ... .field | provenance | |
| fields.swift:43:5:43:7 | [post] box [field] | fields.swift:44:29:44:31 | box [field] | provenance | |
| fields.swift:43:5:43:13 | ... .field | fields.swift:43:5:43:7 | [post] box [field] | provenance | |
| fields.swift:43:17:43:40 | source(...) | fields.swift:43:5:43:13 | ... .field | provenance | |
| fields.swift:44:29:44:31 | box [field] | fields.swift:44:10:44:32 | ... .readField(...) | provenance | |
| fields.swift:53:15:53:19 | super [field] | fields.swift:53:15:53:25 | ... .field | provenance | |
| fields.swift:60:5:60:8 | [post] base [field] | fields.swift:61:15:61:18 | base [field] | provenance | |
| fields.swift:60:5:60:14 | ... .field | fields.swift:60:5:60:8 | [post] base [field] | provenance | |
| fields.swift:60:18:60:47 | source(...) | fields.swift:60:5:60:14 | ... .field | provenance | |
| fields.swift:61:9:61:11 | sub [field] | fields.swift:62:10:62:12 | sub [field] | provenance | |
| fields.swift:61:15:61:18 | base [field] | fields.swift:61:9:61:11 | sub [field] | provenance | |
| fields.swift:62:10:62:12 | sub [field] | fields.swift:53:15:53:19 | super [field] | provenance | |
| fields.swift:62:10:62:12 | sub [field] | fields.swift:62:10:62:18 | ... .field | provenance | |
| fields.swift:75:5:75:7 | [post] box [field] | fields.swift:76:10:76:12 | box [field] | provenance | |
| fields.swift:75:5:75:13 | ... .field | fields.swift:75:5:75:7 | [post] box [field] | provenance | |
| fields.swift:75:17:75:41 | source(...) | fields.swift:75:5:75:13 | ... .field | provenance | |
| fields.swift:76:10:76:12 | box [field] | fields.swift:76:10:76:18 | ... .field | provenance | |
| flow-summaries.swift:2:9:2:9 | x | flow-summaries.swift:3:35:3:35 | x | provenance | |
| flow-summaries.swift:2:13:2:26 | source(...) | flow-summaries.swift:2:9:2:9 | x | provenance | |
| flow-summaries.swift:3:9:3:9 | y | flow-summaries.swift:4:10:4:10 | y | provenance | |
Expand Down Expand Up @@ -576,6 +611,7 @@ nodes
| accessors_instance.swift:119:21:119:35 | source(...) | semmle.label | source(...) |
| accessors_instance.swift:125:18:125:22 | field | semmle.label | field |
| accessors_instance.swift:134:24:134:31 | newValue [0] | semmle.label | newValue [0] |
| accessors_instance.swift:134:24:134:31 | newValue [0] | semmle.label | newValue [0] |
| accessors_instance.swift:134:24:134:33 | ... .0 | semmle.label | ... .0 |
| accessors_instance.swift:143:5:143:7 | [post] ... .p [0] | semmle.label | [post] ... .p [0] |
| accessors_instance.swift:143:5:143:9 | ... .0 | semmle.label | ... .0 |
Expand Down Expand Up @@ -849,6 +885,47 @@ nodes
| enums.swift:83:10:83:23 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] |
| enums.swift:83:22:83:22 | x | semmle.label | x |
| enums.swift:84:14:84:14 | x | semmle.label | x |
| fields.swift:18:5:18:7 | [post] box [field] | semmle.label | [post] box [field] |
| fields.swift:18:5:18:13 | ... .field | semmle.label | ... .field |
| fields.swift:18:17:18:41 | source(...) | semmle.label | source(...) |
| fields.swift:19:10:19:12 | box [field] | semmle.label | box [field] |
| fields.swift:19:10:19:18 | ... .field | semmle.label | ... .field |
| fields.swift:25:5:25:7 | [post] box [field] | semmle.label | [post] box [field] |
| fields.swift:25:5:25:13 | ... .field | semmle.label | ... .field |
| fields.swift:25:17:25:42 | source(...) | semmle.label | source(...) |
| fields.swift:26:10:26:33 | ... .field | semmle.label | ... .field |
| fields.swift:26:11:26:13 | box [field] | semmle.label | box [field] |
| fields.swift:26:11:26:26 | TypeCastExpr [field] | semmle.label | TypeCastExpr [field] |
| fields.swift:31:5:31:7 | [post] box [field] | semmle.label | [post] box [field] |
| fields.swift:31:5:31:13 | ... .field | semmle.label | ... .field |
| fields.swift:31:17:31:48 | source(...) | semmle.label | source(...) |
| fields.swift:32:10:32:40 | ... .field | semmle.label | ... .field |
| fields.swift:32:11:32:13 | box [field] | semmle.label | box [field] |
| fields.swift:32:11:32:33 | TypeCastExpr [field] | semmle.label | TypeCastExpr [field] |
| fields.swift:37:5:37:35 | ... .field | semmle.label | ... .field |
| fields.swift:37:6:37:28 | [post] TypeCastExpr [field] | semmle.label | [post] TypeCastExpr [field] |
| fields.swift:37:39:37:71 | source(...) | semmle.label | source(...) |
| fields.swift:38:10:38:12 | box [field] | semmle.label | box [field] |
| fields.swift:38:10:38:18 | ... .field | semmle.label | ... .field |
| fields.swift:43:5:43:7 | [post] box [field] | semmle.label | [post] box [field] |
| fields.swift:43:5:43:13 | ... .field | semmle.label | ... .field |
| fields.swift:43:17:43:40 | source(...) | semmle.label | source(...) |
| fields.swift:44:10:44:32 | ... .readField(...) | semmle.label | ... .readField(...) |
| fields.swift:44:29:44:31 | box [field] | semmle.label | box [field] |
| fields.swift:53:15:53:19 | super [field] | semmle.label | super [field] |
| fields.swift:53:15:53:25 | ... .field | semmle.label | ... .field |
| fields.swift:60:5:60:8 | [post] base [field] | semmle.label | [post] base [field] |
| fields.swift:60:5:60:14 | ... .field | semmle.label | ... .field |
| fields.swift:60:18:60:47 | source(...) | semmle.label | source(...) |
| fields.swift:61:9:61:11 | sub [field] | semmle.label | sub [field] |
| fields.swift:61:15:61:18 | base [field] | semmle.label | base [field] |
| fields.swift:62:10:62:12 | sub [field] | semmle.label | sub [field] |
| fields.swift:62:10:62:18 | ... .field | semmle.label | ... .field |
| fields.swift:75:5:75:7 | [post] box [field] | semmle.label | [post] box [field] |
| fields.swift:75:5:75:13 | ... .field | semmle.label | ... .field |
| fields.swift:75:17:75:41 | source(...) | semmle.label | source(...) |
| fields.swift:76:10:76:12 | box [field] | semmle.label | box [field] |
| fields.swift:76:10:76:18 | ... .field | semmle.label | ... .field |
| flow-summaries.swift:2:9:2:9 | x | semmle.label | x |
| flow-summaries.swift:2:13:2:26 | source(...) | semmle.label | source(...) |
| flow-summaries.swift:3:9:3:9 | y | semmle.label | y |
Expand Down Expand Up @@ -1211,6 +1288,7 @@ subpaths
| capture.swift:49:13:49:13 | x | capture.swift:45:22:45:24 | arg | capture.swift:46:9:46:9 | y | capture.swift:50:10:50:10 | y |
| capture.swift:60:13:60:13 | x | capture.swift:56:22:56:24 | arg | capture.swift:57:9:57:9 | [post] y [0] | capture.swift:61:10:61:10 | y [0] |
| capture.swift:93:9:93:9 | x | capture.swift:99:16:99:16 | x | capture.swift:99:16:99:16 | x | capture.swift:94:13:94:19 | local(...) |
| fields.swift:62:10:62:12 | sub [field] | fields.swift:53:15:53:19 | super [field] | fields.swift:53:15:53:25 | ... .field | fields.swift:62:10:62:18 | ... .field |
| flow-summaries.swift:54:35:54:48 | source(...) | flow-summaries.swift:54:53:54:53 | y | flow-summaries.swift:54:65:54:65 | y | flow-summaries.swift:54:10:54:67 | ... .throughCallback(...) |
| flow-summaries.swift:55:35:55:48 | source(...) | flow-summaries.swift:55:53:55:53 | y | flow-summaries.swift:55:65:55:71 | ... + ... | flow-summaries.swift:55:10:55:73 | ... .throughCallback(...) |
| test.swift:196:30:196:44 | source(...) | test.swift:189:22:189:22 | s | test.swift:189:58:189:58 | s | test.swift:196:16:196:45 | asyncIdentity(...) |
Expand Down Expand Up @@ -1308,6 +1386,13 @@ testFailures
| enums.swift:65:10:65:14 | opt_x | enums.swift:63:31:63:44 | source(...) | enums.swift:65:10:65:14 | opt_x | $@ | enums.swift:63:31:63:44 | source(...) | source(...) |
| enums.swift:76:14:76:14 | x | enums.swift:73:34:73:47 | source(...) | enums.swift:76:14:76:14 | x | $@ | enums.swift:73:34:73:47 | source(...) | source(...) |
| enums.swift:84:14:84:14 | x | enums.swift:73:34:73:47 | source(...) | enums.swift:84:14:84:14 | x | $@ | enums.swift:73:34:73:47 | source(...) | source(...) |
| fields.swift:19:10:19:18 | ... .field | fields.swift:18:17:18:41 | source(...) | fields.swift:19:10:19:18 | ... .field | $@ | fields.swift:18:17:18:41 | source(...) | source(...) |
| fields.swift:26:10:26:33 | ... .field | fields.swift:25:17:25:42 | source(...) | fields.swift:26:10:26:33 | ... .field | $@ | fields.swift:25:17:25:42 | source(...) | source(...) |
| fields.swift:32:10:32:40 | ... .field | fields.swift:31:17:31:48 | source(...) | fields.swift:32:10:32:40 | ... .field | $@ | fields.swift:31:17:31:48 | source(...) | source(...) |
| fields.swift:38:10:38:18 | ... .field | fields.swift:37:39:37:71 | source(...) | fields.swift:38:10:38:18 | ... .field | $@ | fields.swift:37:39:37:71 | source(...) | source(...) |
| fields.swift:44:10:44:32 | ... .readField(...) | fields.swift:43:17:43:40 | source(...) | fields.swift:44:10:44:32 | ... .readField(...) | $@ | fields.swift:43:17:43:40 | source(...) | source(...) |
| fields.swift:62:10:62:18 | ... .field | fields.swift:60:18:60:47 | source(...) | fields.swift:62:10:62:18 | ... .field | $@ | fields.swift:60:18:60:47 | source(...) | source(...) |
| fields.swift:76:10:76:18 | ... .field | fields.swift:75:17:75:41 | source(...) | fields.swift:76:10:76:18 | ... .field | $@ | fields.swift:75:17:75:41 | source(...) | source(...) |
| flow-summaries.swift:4:10:4:10 | y | flow-summaries.swift:2:13:2:26 | source(...) | flow-summaries.swift:4:10:4:10 | y | $@ | flow-summaries.swift:2:13:2:26 | source(...) | source(...) |
| flow-summaries.swift:11:14:11:14 | y | flow-summaries.swift:8:13:8:26 | source(...) | flow-summaries.swift:11:14:11:14 | y | $@ | flow-summaries.swift:8:13:8:26 | source(...) | source(...) |
| flow-summaries.swift:18:10:18:10 | y | flow-summaries.swift:16:18:16:31 | source(...) | flow-summaries.swift:18:10:18:10 | y | $@ | flow-summaries.swift:16:18:16:31 | source(...) | source(...) |
Expand Down
Loading
Loading