Skip to content

Commit 56b6441

Browse files
authored
Merge pull request github#12391 from geoffw0/ptrout
Swift: Permit data flow out through pointer arguments
2 parents 5c7f2ac + 395bf67 commit 56b6441

File tree

10 files changed

+290
-12
lines changed

10 files changed

+290
-12
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ private import codeql.swift.dataflow.Ssa
77
private import codeql.swift.controlflow.BasicBlocks
88
private import codeql.swift.dataflow.FlowSummary as FlowSummary
99
private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
10+
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
1011

1112
/** Gets the callable in which this node occurs. */
1213
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
@@ -211,6 +212,8 @@ private predicate modifiable(Argument arg) {
211212
arg.getExpr() instanceof InOutExpr
212213
or
213214
arg.getExpr().getType() instanceof NominalType
215+
or
216+
arg.getExpr().getType() instanceof PointerType
214217
}
215218

216219
predicate modifiableParam(ParamDecl param) {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* Provides models for Swift pointer types including `UnsafePointer`,
3+
* `UnsafeBufferPointer` and similar types.
4+
*/
5+
6+
import swift
7+
8+
/**
9+
* A type that is used as a pointer in Swift, such as `UnsafePointer`,
10+
* `UnsafeBufferPointer` and similar types.
11+
*/
12+
class PointerType extends Type {
13+
PointerType() {
14+
this instanceof UnsafeTypedPointerType or
15+
this instanceof UnsafeRawPointerType or
16+
this instanceof OpaquePointerType or
17+
this instanceof AutoreleasingUnsafeMutablePointerType or
18+
this instanceof UnmanagedType or
19+
this instanceof CVaListPointerType or
20+
this instanceof ManagedBufferPointerType
21+
}
22+
}
23+
24+
/**
25+
* A Swift unsafe typed pointer type such as `UnsafePointer`,
26+
* `UnsafeMutablePointer` or `UnsafeBufferPointer`.
27+
*/
28+
class UnsafeTypedPointerType extends BoundGenericType {
29+
UnsafeTypedPointerType() { this.getName().regexpMatch("Unsafe(Mutable|)(Buffer|)Pointer<.*") }
30+
}
31+
32+
/**
33+
* A Swift unsafe raw pointer type such as `UnsafeRawPointer`,
34+
* `UnsafeMutableRawPointer` or `UnsafeRawBufferPointer`.
35+
*/
36+
class UnsafeRawPointerType extends NominalType {
37+
UnsafeRawPointerType() { this.getName().regexpMatch("Unsafe(Mutable|)Raw(Buffer|)Pointer") }
38+
}
39+
40+
/**
41+
* A Swift `OpaquePointer`.
42+
*/
43+
class OpaquePointerType extends NominalType {
44+
OpaquePointerType() { this.getName() = "OpaquePointer" }
45+
}
46+
47+
/**
48+
* A Swift `AutoreleasingUnsafeMutablePointer`.
49+
*/
50+
class AutoreleasingUnsafeMutablePointerType extends BoundGenericType {
51+
AutoreleasingUnsafeMutablePointerType() {
52+
this.getName().matches("AutoreleasingUnsafeMutablePointer<%")
53+
}
54+
}
55+
56+
/**
57+
* A Swift `Unmanaged` object reference.
58+
*/
59+
class UnmanagedType extends BoundGenericType {
60+
UnmanagedType() { this.getName().matches("Unmanaged<%") }
61+
}
62+
63+
/**
64+
* A Swift `CVaListPointer`.
65+
*/
66+
class CVaListPointerType extends NominalType {
67+
CVaListPointerType() { this.getName() = "CVaListPointer" }
68+
}
69+
70+
/**
71+
* A Swift `ManagedBufferPointer`.
72+
*/
73+
class ManagedBufferPointerType extends BoundGenericType {
74+
ManagedBufferPointerType() { this.getName().matches("ManagedBufferPointer<%") }
75+
}

swift/ql/test/library-tests/dataflow/taint/libraries/data.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,13 @@ func taintThroughData() {
158158
let dataTainted19 = source() as! Data
159159
let pointerTainted19 = UnsafeMutablePointer<UInt8>.allocate(capacity: 0)
160160
dataTainted19.copyBytes(to: pointerTainted19, count: 0)
161-
sink(arg: pointerTainted19) // $ MISSING: tainted=158
161+
sink(arg: pointerTainted19) // $ tainted=158
162162

163163
// ";Data;true;copyBytes(to:from:);;;Argument[-1];Argument[0];taint",
164164
let dataTainted20 = source() as! Data
165165
let pointerTainted20 = UnsafeMutablePointer<UInt8>.allocate(capacity: 0)
166166
dataTainted20.copyBytes(to: pointerTainted20, from: 0..<1)
167-
sink(arg: pointerTainted20) // $ MISSING: tainted=164
167+
sink(arg: pointerTainted20) // $ tainted=164
168168

169169
// ";Data;true;flatMap(_:);;;Argument[-1];ReturnValue;taint",
170170
let dataTainted21 = source() as! Data

swift/ql/test/library-tests/dataflow/taint/libraries/nsstring.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,14 +318,14 @@ func taintThroughInterpolatedStrings() {
318318
harmless.getCharacters(ptr1, range: myRange)
319319
sink(arg: ptr1)
320320
sourceNSString().getCharacters(ptr1, range: myRange)
321-
sink(arg: ptr1) // $ MISSING: tainted=
321+
sink(arg: ptr1) // $ tainted=320
322322

323323
var ptr2 = (nil as UnsafeMutablePointer<unichar>?)!
324324
sink(arg: ptr2)
325325
harmless.getCharacters(ptr2)
326326
sink(arg: ptr2)
327327
sourceNSString().getCharacters(ptr2)
328-
sink(arg: ptr2) // $ MISSING: tainted=
328+
sink(arg: ptr2) // $ tainted=327
329329

330330
var ptr3 = (nil as UnsafeMutableRawPointer?)!
331331
sink(arg: ptr3)
@@ -339,14 +339,14 @@ func taintThroughInterpolatedStrings() {
339339
harmless.getCString(ptr4, maxLength: 128, encoding: 0)
340340
sink(arg: ptr4)
341341
sourceNSString().getCString(ptr4, maxLength: 128, encoding: 0)
342-
sink(arg: ptr4) // $ MISSING: tainted=
342+
sink(arg: ptr4) // $ tainted=341
343343

344344
var ptr5 = (nil as UnsafeMutablePointer<CChar>?)!
345345
sink(arg: ptr5)
346346
harmless.getCString(ptr5)
347347
sink(arg: ptr5)
348348
sourceNSString().getCString(ptr5)
349-
sink(arg: ptr5) // $ MISSING: tainted=
349+
sink(arg: ptr5) // $ tainted=348
350350

351351
sink(arg: harmless.enumerateLines({
352352
line, stop in
@@ -363,18 +363,18 @@ func taintThroughInterpolatedStrings() {
363363
var outLongest = (nil as AutoreleasingUnsafeMutablePointer<NSString?>?)!
364364
var outArray = (nil as AutoreleasingUnsafeMutablePointer<NSArray?>?)!
365365
if (str10.completePath(into: outLongest, caseSensitive: false, matchesInto: outArray, filterTypes: nil) > 0) {
366-
sink(arg: outLongest) // $ MISSING: tainted=
366+
sink(arg: outLongest) // $ tainted=362
367367
sink(arg: outLongest.pointee) // $ MISSING: tainted=
368368
sink(arg: outLongest.pointee!) // $ MISSING: tainted=
369-
sink(arg: outArray) // $ MISSING: tainted=
369+
sink(arg: outArray) // $ tainted=362
370370
sink(arg: outArray.pointee) // $ MISSING: tainted=
371371
sink(arg: outArray.pointee!) // $ MISSING: tainted=
372372
}
373373

374374
var str11 = sourceNSString()
375375
var outBuffer = (nil as UnsafeMutablePointer<CChar>?)!
376376
if (str11.getFileSystemRepresentation(outBuffer, maxLength: 256)) {
377-
sink(arg: outBuffer) // $ MISSING: tainted=
377+
sink(arg: outBuffer) // $ tainted=374
378378
sink(arg: outBuffer.pointee) // $ MISSING: tainted=
379379
}
380380

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
2+
// --- stubs ---
3+
4+
func sourceString() -> String { return "" }
5+
func sourceUInt8() -> UInt8 { return 0 }
6+
func sink(arg: Any) {}
7+
8+
// --- tests ---
9+
10+
func clearPointer1(ptr: UnsafeMutablePointer<String>) {
11+
ptr.pointee = "abc"
12+
13+
sink(arg: ptr.pointee)
14+
sink(arg: ptr)
15+
}
16+
17+
func taintPointer(ptr: UnsafeMutablePointer<String>) {
18+
sink(arg: ptr.pointee)
19+
sink(arg: ptr)
20+
21+
ptr.pointee = sourceString()
22+
23+
sink(arg: ptr.pointee) // $ tainted=21
24+
sink(arg: ptr)
25+
}
26+
27+
func clearPointer2(ptr: UnsafeMutablePointer<String>) {
28+
sink(arg: ptr.pointee) // $ tainted=21
29+
sink(arg: ptr)
30+
31+
ptr.pointee = "abc"
32+
33+
sink(arg: ptr.pointee)
34+
sink(arg: ptr)
35+
}
36+
37+
func testMutatingPointerInCall(ptr: UnsafeMutablePointer<String>) {
38+
clearPointer1(ptr: ptr)
39+
40+
sink(arg: ptr.pointee)
41+
sink(arg: ptr)
42+
43+
taintPointer(ptr: ptr) // mutates `ptr` pointee with a tainted value
44+
45+
sink(arg: ptr.pointee) // $ tainted=21
46+
sink(arg: ptr)
47+
48+
clearPointer2(ptr: ptr)
49+
50+
sink(arg: ptr.pointee) // $ SPURIOUS: tainted=21
51+
sink(arg: ptr)
52+
}
53+
54+
// ---
55+
56+
func taintBuffer(buffer: UnsafeMutableBufferPointer<UInt8>) {
57+
sink(arg: buffer[0])
58+
sink(arg: buffer)
59+
60+
buffer[0] = sourceUInt8()
61+
62+
sink(arg: buffer[0]) // $ MISSING: tainted=60
63+
sink(arg: buffer)
64+
}
65+
66+
func testMutatingBufferInCall(ptr: UnsafeMutablePointer<UInt8>) {
67+
let buffer = UnsafeMutableBufferPointer<UInt8>(start: ptr, count: 1000)
68+
69+
sink(arg: buffer[0])
70+
sink(arg: buffer)
71+
72+
taintBuffer(buffer: buffer) // mutates `buffer` contents with a tainted value
73+
74+
sink(arg: buffer[0]) // $ MISSING: tainted=60
75+
sink(arg: buffer)
76+
77+
}
78+
79+
// ---
80+
81+
typealias MyPointer = UnsafeMutablePointer<String>
82+
83+
func taintMyPointer(ptr: MyPointer) {
84+
sink(arg: ptr.pointee)
85+
sink(arg: ptr)
86+
87+
ptr.pointee = sourceString()
88+
89+
sink(arg: ptr.pointee) // $ tainted=87
90+
sink(arg: ptr)
91+
}
92+
93+
func testMutatingMyPointerInCall(ptr: MyPointer) {
94+
sink(arg: ptr.pointee)
95+
sink(arg: ptr)
96+
97+
taintMyPointer(ptr: ptr) // mutates `ptr` pointee with a tainted value
98+
99+
sink(arg: ptr.pointee) // $ MISSING: tainted=87
100+
sink(arg: ptr)
101+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
struct AutoreleasingUnsafeMutablePointer<Pointee> {
3+
}
4+
5+
class MyClass {
6+
init() {
7+
val = 0
8+
}
9+
10+
var val: Int
11+
}
12+
13+
func test() {
14+
var p1: UnsafePointer<Int>
15+
var p2: UnsafeMutablePointer<UInt8>
16+
var p3: UnsafeBufferPointer<String>
17+
var p4: UnsafeMutableBufferPointer<MyClass>
18+
var p5: UnsafeRawPointer
19+
var p6: UnsafeMutableRawPointer
20+
var p7: UnsafeRawBufferPointer
21+
var p8: UnsafeMutableRawBufferPointer
22+
23+
var op: OpaquePointer // C-interop
24+
var aump: AutoreleasingUnsafeMutablePointer<UInt8> // ObjC-interop
25+
var um: Unmanaged<MyClass> // C-interop
26+
var cvlp: CVaListPointer // varargs list pointer
27+
28+
var mbp: ManagedBufferPointer<Int, MyClass>
29+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
| pointers.swift:2:8:2:8 | self | AutoreleasingUnsafeMutablePointer<Pointee> | AutoreleasingUnsafeMutablePointerType, PointerType |
2+
| pointers.swift:14:6:14:6 | p1 | UnsafePointer<Int> | PointerType, UnsafeTypedPointerType |
3+
| pointers.swift:15:6:15:6 | p2 | UnsafeMutablePointer<UInt8> | PointerType, UnsafeTypedPointerType |
4+
| pointers.swift:16:6:16:6 | p3 | UnsafeBufferPointer<String> | PointerType, UnsafeTypedPointerType |
5+
| pointers.swift:17:6:17:6 | p4 | UnsafeMutableBufferPointer<MyClass> | PointerType, UnsafeTypedPointerType |
6+
| pointers.swift:18:6:18:6 | p5 | UnsafeRawPointer | PointerType, UnsafeRawPointerType |
7+
| pointers.swift:19:6:19:6 | p6 | UnsafeMutableRawPointer | PointerType, UnsafeRawPointerType |
8+
| pointers.swift:20:6:20:6 | p7 | UnsafeRawBufferPointer | PointerType, UnsafeRawPointerType |
9+
| pointers.swift:21:6:21:6 | p8 | UnsafeMutableRawBufferPointer | PointerType, UnsafeRawPointerType |
10+
| pointers.swift:23:6:23:6 | op | OpaquePointer | OpaquePointerType, PointerType |
11+
| pointers.swift:24:6:24:6 | aump | AutoreleasingUnsafeMutablePointer<UInt8> | AutoreleasingUnsafeMutablePointerType, PointerType |
12+
| pointers.swift:25:6:25:6 | um | Unmanaged<MyClass> | PointerType, UnmanagedType |
13+
| pointers.swift:26:6:26:6 | cvlp | CVaListPointer | CVaListPointerType, PointerType |
14+
| pointers.swift:28:6:28:6 | mbp | ManagedBufferPointer<Int, MyClass> | ManagedBufferPointerType, PointerType |
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import swift
2+
import codeql.swift.frameworks.StandardLibrary.PointerTypes
3+
4+
string describe(Type t) {
5+
t instanceof PointerType and result = "PointerType"
6+
or
7+
t instanceof BuiltinRawPointerType and result = "BuiltinRawPointerType"
8+
or
9+
t instanceof UnsafeTypedPointerType and result = "UnsafeTypedPointerType"
10+
or
11+
t instanceof UnsafeRawPointerType and result = "UnsafeRawPointerType"
12+
or
13+
t instanceof OpaquePointerType and result = "OpaquePointerType"
14+
or
15+
t instanceof AutoreleasingUnsafeMutablePointerType and
16+
result = "AutoreleasingUnsafeMutablePointerType"
17+
or
18+
t instanceof UnmanagedType and result = "UnmanagedType"
19+
or
20+
t instanceof CVaListPointerType and result = "CVaListPointerType"
21+
or
22+
t instanceof ManagedBufferPointerType and result = "ManagedBufferPointerType"
23+
}
24+
25+
from VarDecl v, Type t
26+
where
27+
v.getLocation().getFile().getBaseName() != "" and
28+
t = v.getType()
29+
select v, t.toString(), strictconcat(describe(t), ", ")

0 commit comments

Comments
 (0)