Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift: Improve getABaseType implementions #14252

Merged
merged 10 commits into from
Sep 22, 2023
1 change: 0 additions & 1 deletion swift/ql/.generated.list

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion swift/ql/.gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions swift/ql/lib/change-notes/2023-09-18-get-a-base-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
category: majorAnalysis
---

* The predicates `getABaseType`, `getABaseTypeDecl`, `getADerivedType` and `getADerivedTypeDecl` on `Type` and `TypeDecl` now behave more usefully and consistently. They now explore through type aliases used in base class declarations, and include protocols added in extensions.

To examine base class declarations at a low level without these enhancements, use `TypeDecl.getInheritedType`.

`Type.getABaseType` (only) previously resolved a type alias it was called directly on. This behaviour no longer exists. To find any base type of a type that could be an alias, the construct `Type.getUnderlyingType().getABaseType*()` is recommended.
16 changes: 0 additions & 16 deletions swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -417,14 +417,6 @@ private Element interpretElement0(
subtypes = true and
declWithMethod.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*()
or
// member declared in a type that's extended with a protocol that is the named type
exists(ExtensionDecl e |
e.getExtendedTypeDecl().getADerivedTypeDecl*() = declWithMethod.asNominalTypeDecl()
|
subtypes = true and
e.getAProtocol() = namedTypeDecl.getADerivedTypeDecl*()
)
or
// member declared directly in the named type (or an extension of it)
subtypes = false and
declWithMethod.asNominalTypeDecl() = namedTypeDecl
Expand All @@ -442,14 +434,6 @@ private Element interpretElement0(
subtypes = true and
declWithField.asNominalTypeDecl() = namedTypeDecl.getADerivedTypeDecl*()
or
// field declared in a type that's extended with a protocol that is the named type
exists(ExtensionDecl e |
e.getExtendedTypeDecl().getADerivedTypeDecl*() = declWithField.asNominalTypeDecl()
|
subtypes = true and
e.getAProtocol() = namedTypeDecl.getADerivedTypeDecl*()
)
or
// field declared directly in the named type (or an extension of it)
subtypes = false and
declWithField.asNominalTypeDecl() = namedTypeDecl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet cs)
// So when the node is a `PostUpdateNode` we allow any sequence of implicit read steps of an appropriate
// type to make sure we arrive at the sink with an empty access path.
exists(NominalTypeDecl d, Decl cx |
node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getType() =
node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getType().getUnderlyingType() =
d.getType().getABaseType*() and
cx.asNominalTypeDecl() = d and
cs.getAReadContent().(DataFlow::Content::FieldContent).getField() = cx.getAMember()
Expand Down
7 changes: 6 additions & 1 deletion swift/ql/lib/codeql/swift/elements/decl/TypeAliasDecl.qll
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.decl.TypeAliasDecl

/**
* A declaration of a type alias to another type. For example:
* ```
* typealias MyInt = Int
* ```
*/
class TypeAliasDecl extends Generated::TypeAliasDecl { }
37 changes: 32 additions & 5 deletions swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,24 @@ class TypeDecl extends Generated::TypeDecl {
deprecated Type getBaseType(int index) { result = this.getInheritedType(index) }

/**
* Gets any of the base types of this type declaration.
* Gets any of the base types of this type declaration. Expands protocols added in
* extensions and expands type aliases. For example in the following code, `B` has
* base type `A`:
* ```
* typealias A_alias = A
*
* class B : A_alias {}
* ```
*/
Type getABaseType() {
// TODO generalize this to resolve `TypeAliasDecl`s and consider bases added by extensions
result = this.getAnInheritedType()
// direct base type
result = this.getAnInheritedType().getUnderlyingType()
or
// protocol added in an extension of the type
exists(ExtensionDecl ed |
ed.getExtendedTypeDecl() = this and
ed.getAProtocol().getType() = result
)
}

/**
Expand All @@ -51,7 +64,14 @@ class TypeDecl extends Generated::TypeDecl {
}

/**
* Gets the declaration of any of the base types of this type declaration.
* Gets the declaration of any of the base types of this type declaration. Expands
* protocols added in extensions and expands type aliases. For example in the following
* code, `B` has base type `A`.
* ```
* typealias A_alias = A
*
* class B : A_alias {}
* ```
*/
TypeDecl getABaseTypeDecl() { result = this.getABaseType().(AnyGenericType).getDeclaration() }

Expand All @@ -63,7 +83,14 @@ class TypeDecl extends Generated::TypeDecl {
deprecated TypeDecl getDerivedTypeDecl(int i) { result.getBaseTypeDecl(i) = this }

/**
* Gets the declaration of any type derived from this type declaration.
* Gets the declaration of any type derived from this type declaration. Expands protocols
* added in extensions and expands type aliases. For example in the following code, `B`
* is derived from `A`.
* ```
* typealias A_alias = A
*
* class B : A_alias {}
* ```
*/
TypeDecl getADerivedTypeDecl() { result.getABaseTypeDecl() = this }

Expand Down
11 changes: 4 additions & 7 deletions swift/ql/lib/codeql/swift/elements/type/NominalType.qll
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
private import codeql.swift.generated.type.NominalType
private import codeql.swift.elements.decl.NominalTypeDecl
private import codeql.swift.elements.type.Type

class NominalType extends Generated::NominalType {
override Type getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }

NominalType getADerivedType() { result.getABaseType() = this }
}
/**
* A class, struct, enum or protocol.
*/
class NominalType extends Generated::NominalType { }
23 changes: 16 additions & 7 deletions swift/ql/lib/codeql/swift/elements/type/Type.qll
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
private import codeql.swift.generated.type.Type
private import codeql.swift.elements.type.AnyGenericType

/**
* A Swift type.
Expand Down Expand Up @@ -42,16 +43,24 @@ class Type extends Generated::Type {
Type getUnderlyingType() { result = this }

/**
* Gets any base type of this type. For a `typealias`, this is a base type
* of the aliased type. For example in the following code, both `B` and
* `B_alias` have base type `A`.
* Gets any base type of this type. Expands protocols added in extensions and expands
* type aliases. For example in the following code, `B` has base type `A`:
* ```
* class A {}
* typealias A_alias = A
*
* class B : A {}
* class B : A_alias {}
* ```
*/
Type getABaseType() { result = this.(AnyGenericType).getDeclaration().getABaseType() }

/**
* Gets a type derived from this type. Expands type aliases, for example in the following
* code, `B` derives from type `A`.
* ```
* typealias A_alias = A
*
* typealias B_alias = B
* class B : A_alias {}
* ```
*/
Type getABaseType() { none() }
Type getADerivedType() { result.getABaseType() = this }
}
8 changes: 6 additions & 2 deletions swift/ql/lib/codeql/swift/elements/type/TypeAliasType.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
private import codeql.swift.elements.type.Type
private import codeql.swift.generated.type.TypeAliasType

/**
* A type alias to another type. For example:
* ```
* typealias MyInt = Int
* ```
*/
class TypeAliasType extends Generated::TypeAliasType {
/**
* Gets the aliased type of this type alias type.
Expand All @@ -13,6 +19,4 @@ class TypeAliasType extends Generated::TypeAliasType {
Type getAliasedType() { result = this.getDecl().getAliasedType() }

override Type getUnderlyingType() { result = this.getAliasedType().getUnderlyingType() }

override Type getABaseType() { result = this.getAliasedType().getABaseType() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private class CoreDataStore extends CleartextStorageDatabaseSink {
// with `coreDataObj.data` is a sink.
// (ideally this would be only members with the `@NSManaged` attribute)
exists(NominalType t, Expr e |
t.getABaseType*().getUnderlyingType().getName() = "NSManagedObject" and
t.getUnderlyingType().getABaseType*().getName() = "NSManagedObject" and
this.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = t and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
Expand All @@ -67,7 +67,7 @@ private class RealmStore extends CleartextStorageDatabaseSink instanceof DataFlo
// example in `realmObj.data = sensitive` the post-update node corresponding
// with `realmObj.data` is a sink.
exists(NominalType t, Expr e |
t.getABaseType*().getUnderlyingType().getName() = "RealmSwiftObject" and
t.getUnderlyingType().getABaseType*().getName() = "RealmSwiftObject" and
this.getPreUpdateNode().asExpr() = e and
e.getFullyConverted().getType() = t and
not e.(DeclRefExpr).getDecl() instanceof SelfParamDecl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module CleartextStorageDatabaseConfig implements DataFlow::ConfigSig {
// for example in `realmObj.data = sensitive`.
isSink(node) and
exists(NominalTypeDecl d, Decl cx |
d.getType().getABaseType*().getUnderlyingType().getName() =
d.getType().getUnderlyingType().getABaseType*().getName() =
["NSManagedObject", "RealmSwiftObject"] and
cx.asNominalTypeDecl() = d and
c.getAReadContent().(DataFlow::Content::FieldContent).getField() = cx.getAMember()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
| nominaltype.swift:54:6:54:6 | a | A | getFullName:A, getName:A, getUnderlyingType:A |
| nominaltype.swift:55:6:55:6 | a_alias | A_alias | getAliasedType:A, getFullName:A_alias, getName:A_alias, getUnderlyingType:A |
| nominaltype.swift:56:6:56:6 | a_optional_alias | A_optional_alias | getAliasedType:A?, getFullName:A_optional_alias, getName:A_optional_alias, getUnderlyingType:A? |
| nominaltype.swift:57:6:57:6 | b1 | B1 | getABaseType:A, getFullName:B1, getName:B1, getUnderlyingType:B1 |
| nominaltype.swift:58:6:58:6 | b2 | B2 | getABaseType:A_alias, getFullName:B2, getName:B2, getUnderlyingType:B2 |
| nominaltype.swift:59:6:59:6 | b1_alias | B1_alias | getABaseType:A, getAliasedType:B1, getFullName:B1_alias, getName:B1_alias, getUnderlyingType:B1 |
| nominaltype.swift:60:6:60:6 | b2_alias | B2_alias | getABaseType:A_alias, getAliasedType:B2, getFullName:B2_alias, getName:B2_alias, getUnderlyingType:B2 |
| nominaltype.swift:61:6:61:6 | p | P | getFullName:P, getName:P, getUnderlyingType:P |
| nominaltype.swift:62:6:62:6 | p_alias | P_alias | getFullName:P_alias, getName:P_alias, getUnderlyingType:P_alias |
| nominaltype.swift:63:6:63:6 | c1 | C1 | getABaseType:P, getFullName:C1, getName:C1, getUnderlyingType:C1 |
| nominaltype.swift:64:6:64:6 | c2 | C2 | getABaseType:P_alias, getFullName:C2, getName:C2, getUnderlyingType:C2 |
| nominaltype.swift:65:6:65:6 | c1_alias | C1_alias | getABaseType:P, getAliasedType:C1, getFullName:C1_alias, getName:C1_alias, getUnderlyingType:C1 |
| nominaltype.swift:66:6:66:6 | c2_alias | C2_alias | getABaseType:P_alias, getAliasedType:C2, getFullName:C2_alias, getName:C2_alias, getUnderlyingType:C2 |
| nominaltype.swift:67:6:67:6 | o | Outer | getFullName:Outer, getName:Outer, getUnderlyingType:Outer |
| nominaltype.swift:68:6:68:6 | oi | Outer.Inner | getFullName:Outer.Inner, getName:Inner, getUnderlyingType:Outer.Inner |
| nominaltype.swift:69:6:69:6 | oia | Outer.Inner.InnerAlias | getABaseType:FixedWidthInteger, getABaseType:SignedInteger, getABaseType:_ExpressibleByBuiltinIntegerLiteral, getAliasedType:Int, getFullName:Outer.Inner.InnerAlias, getName:InnerAlias, getUnderlyingType:Int |
| nominaltype.swift:70:6:70:6 | aa | Any? | getFullName:Any?, getName:Any?, getUnderlyingType:Any? |
| nominaltype.swift:71:6:71:6 | p1p2 | P1P2 | getFullName:P1P2, getName:P1P2, getUnderlyingType:P1P2 |
| nominaltype.swift:72:6:72:6 | boxInt | Box<Int> | getFullName:Box<Int>, getName:Box<Int>, getUnderlyingType:Box<Int> |
| nominaltype.swift:84:6:84:6 | i | Int | getABaseType:CVarArg, getABaseType:CodingKeyRepresentable, getABaseType:CustomReflectable, getABaseType:Decodable, getABaseType:Encodable, getABaseType:Equatable, getABaseType:FixedWidthInteger, getABaseType:Hashable, getABaseType:MirrorPath, getABaseType:SIMDScalar, getABaseType:Sendable, getABaseType:SignedInteger, getABaseType:_CustomPlaygroundQuickLookable, getABaseType:_ExpressibleByBuiltinIntegerLiteral, getABaseType:_HasCustomAnyHashableRepresentation, getFullName:Int, getName:Int, getUnderlyingType:Int |
| nominaltype.swift:85:6:85:6 | j | Any? | getFullName:Any?, getName:Any?, getUnderlyingType:Any? |
| nominaltype.swift:86:6:86:6 | a | A | getFullName:A, getName:A, getUnderlyingType:A |
| nominaltype.swift:87:6:87:6 | a_alias | A_alias | getAliasedType:A, getFullName:A_alias, getName:A_alias, getUnderlyingType:A |
| nominaltype.swift:88:6:88:6 | a_optional_alias | A_optional_alias | getAliasedType:A?, getFullName:A_optional_alias, getName:A_optional_alias, getUnderlyingType:A? |
| nominaltype.swift:89:6:89:6 | b1 | B1 | getABaseType:A, getFullName:B1, getName:B1, getUnderlyingType:B1 |
| nominaltype.swift:90:6:90:6 | b2 | B2 | getABaseType:A, getFullName:B2, getName:B2, getUnderlyingType:B2 |
| nominaltype.swift:91:6:91:6 | b1_alias | B1_alias | getAliasedType:B1, getFullName:B1_alias, getName:B1_alias, getUnderlyingType:B1 |
| nominaltype.swift:92:6:92:6 | b2_alias | B2_alias | getAliasedType:B2, getFullName:B2_alias, getName:B2_alias, getUnderlyingType:B2 |
| nominaltype.swift:93:6:93:6 | p | P | getFullName:P, getName:P, getUnderlyingType:P |
| nominaltype.swift:94:6:94:6 | p_alias | P_alias | getFullName:P_alias, getName:P_alias, getUnderlyingType:P_alias |
| nominaltype.swift:95:6:95:6 | c1 | C1 | getABaseType:P, getFullName:C1, getName:C1, getUnderlyingType:C1 |
| nominaltype.swift:96:6:96:6 | c2 | C2 | getABaseType:P, getFullName:C2, getName:C2, getUnderlyingType:C2 |
| nominaltype.swift:97:6:97:6 | o | Outer | getFullName:Outer, getName:Outer, getUnderlyingType:Outer |
| nominaltype.swift:98:6:98:6 | oi | Outer.Inner | getFullName:Outer.Inner, getName:Inner, getUnderlyingType:Outer.Inner |
| nominaltype.swift:99:6:99:6 | oia | Outer.Inner.InnerAlias | getAliasedType:A, getFullName:Outer.Inner.InnerAlias, getName:InnerAlias, getUnderlyingType:A |
| nominaltype.swift:100:6:100:6 | p1p2 | P1P2 | getFullName:P1P2, getName:P1P2, getUnderlyingType:P1P2 |
| nominaltype.swift:101:6:101:6 | boxInt | Box<A> | getFullName:Box<A>, getName:Box<A>, getUnderlyingType:Box<A> |
| nominaltype.swift:102:6:102:6 | d1 | D1 | getABaseType:P3, getFullName:D1, getName:D1, getUnderlyingType:D1 |
| nominaltype.swift:103:6:103:6 | d2 | D2 | getABaseType:P4, getFullName:D2, getName:D2, getUnderlyingType:D2 |
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ typealias B1_alias = B1

typealias B2_alias = B2

// ---

protocol P {
}

Expand All @@ -27,16 +29,16 @@ class C1 : P {
class C2 : P_alias {
}

typealias C1_alias = C1

typealias C2_alias = C2
// ---

class Outer {
class Inner {
typealias InnerAlias = Int
typealias InnerAlias = A
}
}

// ---

protocol P1 {
}

Expand All @@ -45,12 +47,42 @@ protocol P2 {

typealias P1P2 = P1 & P2

// ---

class Box<T> {
}

// ---

class D1 {
}

protocol P3 {
}

extension D1 : P3 {
}

// ---

class D2 {
}

typealias D2_alias = D2

protocol P4 {
}

typealias P4_alias = P4

extension D2 : P4_alias {
}

// ---

func test() {
var i : Int
var j : Any?
var a : A
var a_alias : A_alias
var a_optional_alias : A_optional_alias
Expand All @@ -62,12 +94,11 @@ func test() {
var p_alias : P_alias
var c1 : C1
var c2 : C2
var c1_alias : C1_alias
var c2_alias : C2_alias
var o : Outer
var oi : Outer.Inner
var oia : Outer.Inner.InnerAlias
var aa : Any?
var p1p2 : P1P2
var boxInt : Box<Int>
var boxInt : Box<A>
var d1: D1
var d2: D2
}
Loading