Merge pull request #14925 from geoffw0/flows

Swift: Imprecise Taint Flows
This commit is contained in:
Mathias Vorreiter Pedersen 2023-12-11 10:06:01 +00:00 коммит произвёл GitHub
Родитель 17cd22f9d0 e60dc9a9ed
Коммит d8f53e5524
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 251 добавлений и 6 удалений

Просмотреть файл

@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added imprecise flow models for `append` and `insert` methods, and initializer calls with a `data` argument.

Просмотреть файл

@ -6,6 +6,7 @@
import swift
private import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.FlowSources
private import codeql.swift.dataflow.FlowSteps
/**
* An initializer call `ce` that has a "contentsOf" argument, along with a
@ -51,3 +52,60 @@ private class InitializerContentsOfLocalSource extends LocalFlowSource {
override string getSourceType() { result = "contentsOf initializer" }
}
/**
* An imprecise flow step for an initializer call with a "data" argument. For
* example:
* ```
* let mc = MyClass(data: taintedData)
* ```
*/
private class InitializerFromDataStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(InitializerCallExpr ce, Argument arg |
ce.getAnArgument() = arg and
arg.getLabel() = "data" and
node1.asExpr() = arg.getExpr() and
node2.asExpr() = ce
)
}
}
/**
* An imprecise flow step for an `append`, `insert` or similar function. For
* example:
* ```
* mc.append(taintedObj)
* mc.insert(taintedObj, at: 0)
* ```
*/
private class AppendCallStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(CallExpr ce, Argument arg |
ce.getAnArgument() = arg and
ce.getStaticTarget().(Function).getShortName() = ["append", "insert"] and
arg.getLabel() = ["", "contentsOf"] and
node1.asExpr() = arg.getExpr() and
node2.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = ce.getQualifier()
)
}
}
/**
* An imprecise flow step for an `appending` or similar function. For
* example:
* ```
* let mc2 = mc.appending(taintedObj)
* ```
*/
private class AppendingCallStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(CallExpr ce, Argument arg |
ce.getAnArgument() = arg and
ce.getStaticTarget().(Function).getShortName() = ["appending", "inserting"] and
arg.getLabel() = ["", "contentsOf"] and
node1.asExpr() = arg.getExpr() and
node2.asExpr() = ce
)
}
}

Просмотреть файл

@ -0,0 +1,43 @@
// --- stubs ---
// --- tests ---
func source(_ label: String) -> Int { return 0 }
func sink(arg: Any) {}
// ---
func testArray() {
let arrClean = Array<Int>()
var arr1 = Array<Int>()
arr1.append(1)
sink(arg: arr1[arr1.endIndex - 1])
arr1.append(source("int1"))
sink(arg: arr1[arr1.endIndex - 1]) // $ tainted=int1
var arr2 = Array<Int>()
arr2.append(contentsOf: arrClean)
sink(arg: arr2[arr2.endIndex - 1])
arr2.append(contentsOf: arr1)
sink(arg: arr2[arr2.endIndex - 1]) // $ tainted=int1
var arr3 = Array<Int>()
arr3.insert(1, at: 0)
sink(arg: arr3[0])
arr3.insert(source("int3"), at: 0)
sink(arg: arr3[0]) // $ tainted=int3
var arr4 = Array<Int>()
arr4.insert(contentsOf: arrClean, at: 0)
sink(arg: arr4[0])
arr4.insert(contentsOf: arr3, at: 0)
sink(arg: arr4[0]) // $ tainted=int3
var arr5 = Array<Int>()
arr5.insert(contentsOf: 1...10, at: 0)
sink(arg: arr5[arr5.endIndex - 1])
arr5.insert(contentsOf: 1...source("int5"), at: 0)
sink(arg: arr5[arr5.endIndex - 1]) // $ MISSING: tainted=int5
}

Просмотреть файл

@ -0,0 +1,120 @@
// --- stubs ---
class Data {
init<S>(_ elements: S) {}
}
struct URL {
init?(string: String) {}
}
// A `MyContainer` contains `Data` in something rather like a `Sequence`.
struct MyContainer {
init() { }
init(data: Data) { }
init(data: Data, flags: Int) { }
mutating func append(_ data: Data) { }
mutating func insert(_ data: Data, at: Int) { }
func appending(_ data: Data) -> MyContainer { return self }
func inserting(_ data: Data, at: Int) -> MyContainer { return self }
mutating func append(contentsOf other: MyContainer) { }
mutating func insert(contentsOf other: MyContainer, at: Int) { }
func appending(contentsOf other: MyContainer) -> MyContainer { return self }
func inserting(contentsOf other: MyContainer, at: Int) -> MyContainer { return self }
mutating func append(_ string: String) { }
mutating func append(contentsOf array: Array<Data>) { }
subscript(index: Int) -> Data { return Data(0) }
}
// --- tests ---
func source(_ label: String) -> Data { return Data(0) }
func sourceString(_ label: String) -> String { return "" }
func sink(arg: Any) {}
// ---
func testCustom() {
let clean = MyContainer(data: Data(0))
let tainted = MyContainer(data: source("data1"))
let tainted2 = MyContainer(data: source("data2"), flags: 123)
sink(arg: clean)
sink(arg: clean[0])
sink(arg: tainted) // $ tainted=data1
sink(arg: tainted[0]) // $ tainted=data1
sink(arg: tainted2) // $ tainted=data2
sink(arg: tainted2[0]) // $ tainted=data2
var mc1 = MyContainer()
mc1.append(Data(0))
sink(arg: mc1)
sink(arg: mc1[0])
mc1.append(source("data3"))
sink(arg: mc1) // $ tainted=data3
sink(arg: mc1[0]) // $ tainted=data3
var mc2 = MyContainer()
mc2.insert(Data(0), at: 0)
sink(arg: mc2)
sink(arg: mc2[0])
mc2.insert(source("data4"), at: 0)
sink(arg: mc2) // $ tainted=data4
sink(arg: mc2[0]) // $ tainted=data4
var mc3 = MyContainer()
mc3.append(contentsOf: clean)
sink(arg: mc3)
sink(arg: mc3[0])
mc3.append(contentsOf: tainted)
sink(arg: mc3) // $ tainted=data1
sink(arg: mc3[0]) // $ tainted=data1
var mc4 = MyContainer()
mc4.insert(contentsOf: clean, at: 0)
sink(arg: mc4)
sink(arg: mc4[0])
mc4.insert(contentsOf: tainted, at: 0)
sink(arg: mc4) // $ tainted=data1
sink(arg: mc4[0]) // $ tainted=data1
let mc5 = MyContainer()
sink(arg: mc5.appending(Data(0)))
sink(arg: mc5.appending(Data(0))[0])
sink(arg: mc5.appending(source("data5"))) // $ tainted=data5
sink(arg: mc5.appending(source("data6"))[0]) // $ tainted=data6
sink(arg: mc5.inserting(Data(0), at: 0))
sink(arg: mc5.inserting(Data(0), at: 0)[0])
sink(arg: mc5.inserting(source("data7"), at: 0)) // $ tainted=data7
sink(arg: mc5.inserting(source("data8"), at: 0)[0]) // $ tainted=data8
sink(arg: mc5.appending(contentsOf: clean))
sink(arg: mc5.appending(contentsOf: clean)[0])
sink(arg: mc5.appending(contentsOf: tainted)) // $ tainted=data1
sink(arg: mc5.appending(contentsOf: tainted)[0]) // $ tainted=data1
sink(arg: mc5.inserting(contentsOf: clean, at: 0))
sink(arg: mc5.inserting(contentsOf: clean, at: 0)[0])
sink(arg: mc5.inserting(contentsOf: tainted, at: 0)) // $ tainted=data1
sink(arg: mc5.inserting(contentsOf: tainted, at: 0)[0]) // $ tainted=data1
sink(arg: mc5)
let taintedString = sourceString("data9")
var mc6 = MyContainer()
mc6.append("")
sink(arg: mc6)
sink(arg: mc6[0])
mc6.append(taintedString)
sink(arg: mc6) // $ tainted=data9
sink(arg: mc6[0]) // $ tainted=data9
let taintedArray = [source("data10")]
var mc7 = MyContainer()
mc7.append(contentsOf: [])
sink(arg: mc7)
sink(arg: mc7[0])
mc7.append(contentsOf: taintedArray)
sink(arg: mc7) // $ MISSING: tainted=data10
sink(arg: mc7[0]) // $ MISSING: tainted=data10
}

Просмотреть файл

@ -41,31 +41,42 @@ class UIScene {
class OpenURLOptions {}
}
struct CGFloat { }
class Data {
init<S>(_ elements: S) {}
}
class UIImage {
init?(data: Data) { }
init?(data: Data, scale: CGFloat) { }
}
// --- tests ---
func source() -> Any { return "" }
func source(_ label: String) -> Any { return "" }
func sink(_: Any) {}
func testUIOpenURLContext() {
let safe = UIOpenURLContext()
let tainted = source() as! UIOpenURLContext
let tainted = source("OpenURLContext") as! UIOpenURLContext
sink(safe.url)
sink(safe.options)
sink(tainted.url) // $ tainted=51
sink(tainted.url) // $ tainted=OpenURLContext
sink(tainted.options)
}
func testConnectionOptions() {
let safe = UIScene.ConnectionOptions()
let tainted = source() as! UIScene.ConnectionOptions
let tainted = source("ConnectionOptions") as! UIScene.ConnectionOptions
sink(safe.userActivities)
sink(tainted.userActivities) // $ tainted=61
sink(tainted.userActivities) // $ tainted=ConnectionOptions
sink(safe.shortcutItem)
sink(tainted.shortcutItem)
sink(safe.urlContexts)
sink(tainted.urlContexts) // $ tainted=61
sink(tainted.urlContexts) // $ tainted=ConnectionOptions
sink(safe.handoffUserActivityType)
sink(tainted.handoffUserActivityType)
sink(safe.cloudKitShareMetadata)
@ -75,3 +86,12 @@ func testConnectionOptions() {
sink(safe.sourceApplication)
sink(tainted.sourceApplication)
}
func testUIImage(scale: CGFloat) {
let taintedData = source("UIImage") as! Data
sink(UIImage(data: Data(0))!)
sink(UIImage(data: Data(taintedData))!) // $ tainted=UIImage
sink(UIImage(data: Data(0), scale: scale)!)
sink(UIImage(data: Data(taintedData), scale: scale)!) // $ tainted=UIImage
}