Add dataflow tests for lambda-like constructs

This commit adds tests for dataflow involving lambdas, big-arity lambdas, SAM conversions, and function references.
This commit is contained in:
Tamas Vajk
2022-03-17 09:26:16 +01:00
committed by Ian Lynagh
parent aab271d81e
commit abcb367495
8 changed files with 132 additions and 1 deletions

View File

@@ -3908,7 +3908,7 @@ open class KotlinFileExtractor(
addModifiers(id, "final")
addVisibilityModifierToLocalOrAnonymousClass(id)
extractClassSupertypes(superTypes, listOf(), id)
extractClassSupertypes(superTypes, listOf(), id, inReceiverContext = true)
var parent: IrDeclarationParent? = currentDeclaration.parent
while (parent != null) {

View File

@@ -0,0 +1,14 @@
class FunctionReference {
fun fn1(s: String) = s
fun test() {
fun fn2(s: String) = s
Helper.sink(Processor().process(this::fn1, Helper.taint()))
Helper.sink(Processor().process(FunctionReference::fn1, this, Helper.taint()))
Helper.sink(Processor().process(this::fn1, Helper.notaint()))
Helper.sink(Processor().process(::fn2, Helper.taint()))
Helper.sink(Processor().process(::fn2, Helper.notaint()))
}
}

View File

@@ -0,0 +1,32 @@
class Lambda {
fun test() {
Helper.sink(Processor().process({ it: String -> Helper.notaint() }, ""))
Helper.sink(Processor().process({ it: String -> Helper.taint() }, ""))
Helper.sink(Processor().process({ i -> i }, Helper.taint()))
Helper.sink(Processor().process(fun (i: String) = i, Helper.taint()))
Helper.sink(Processor().processExt({ i -> i }, Helper.taint(), Helper.notaint()))
Helper.sink(Processor().processExt({ i -> i }, Helper.notaint(), Helper.taint()))
Helper.sink(Processor().processExt({ i -> this }, Helper.taint(), Helper.notaint()))
Helper.sink(Processor().processExt({ i -> this }, Helper.notaint(), Helper.taint()))
Helper.sink(Processor().process({ i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15,i16,i17,i18,i19,i20,i21,i22,i23 -> i0 }, Helper.taint(), Helper.notaint()))
Helper.sink(Processor().process({ i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15,i16,i17,i18,i19,i20,i21,i22,i23 -> i0 }, Helper.notaint(), Helper.taint())) // False positive
}
}
class ManualBigLambda {
fun invoke(s0: String, s1: String): String {
return s0
}
fun invoke(a: Array<Any?>): String {
return invoke(a[0] as String, a[1] as String)
}
fun call() {
Helper.sink(invoke(Helper.taint(), Helper.notaint()))
Helper.sink(invoke(Helper.notaint(), Helper.taint()))
Helper.sink(invoke(arrayOf(Helper.taint(), Helper.notaint())))
Helper.sink(invoke(arrayOf(Helper.notaint(), Helper.taint()))) // False positive
}
}

View File

@@ -0,0 +1,9 @@
class LocalFunction {
fun test() {
fun fn1() = Helper.taint()
fun fn2(s: String) = s
Helper.sink(fn1())
Helper.sink(fn2(Helper.taint()))
}
}

View File

@@ -0,0 +1,13 @@
fun interface Predicate {
fun go(s: String): String
}
class SamConversion {
fun test() {
val p1 = Predicate { Helper.taint() }
val p2 = Predicate { it -> it }
Helper.sink(p1.go(""))
Helper.sink(p1.go(Helper.taint()))
}
}

View File

@@ -0,0 +1,17 @@
| functionReference.kt:8:59:8:65 | taint(...) | functionReference.kt:8:33:8:66 | process(...) |
| functionReference.kt:9:78:9:84 | taint(...) | functionReference.kt:9:33:9:85 | process(...) |
| functionReference.kt:11:55:11:61 | taint(...) | functionReference.kt:11:33:11:62 | process(...) |
| lambda.kt:4:64:4:70 | taint(...) | lambda.kt:4:33:4:77 | process(...) |
| lambda.kt:5:60:5:66 | taint(...) | lambda.kt:5:33:5:67 | process(...) |
| lambda.kt:6:69:6:75 | taint(...) | lambda.kt:6:33:6:76 | process(...) |
| lambda.kt:9:81:9:87 | taint(...) | lambda.kt:9:33:9:88 | processExt(...) |
| lambda.kt:10:66:10:72 | taint(...) | lambda.kt:10:33:10:91 | processExt(...) |
| lambda.kt:13:145:13:151 | taint(...) | lambda.kt:13:33:13:170 | process(...) |
| lambda.kt:14:163:14:169 | taint(...) | lambda.kt:14:33:14:170 | process(...) |
| lambda.kt:27:35:27:41 | taint(...) | lambda.kt:27:21:27:60 | invoke(...) |
| lambda.kt:29:43:29:49 | taint(...) | lambda.kt:29:21:29:69 | invoke(...) |
| lambda.kt:30:61:30:67 | taint(...) | lambda.kt:30:21:30:69 | invoke(...) |
| localFunction.kt:3:28:3:34 | taint(...) | localFunction.kt:6:21:6:25 | fn1(...) |
| localFunction.kt:7:32:7:38 | taint(...) | localFunction.kt:7:21:7:39 | fn2(...) |
| samConversion.kt:7:37:7:43 | taint(...) | samConversion.kt:10:24:10:29 | go(...) |
| samConversion.kt:7:37:7:43 | taint(...) | samConversion.kt:11:24:11:41 | go(...) |

View File

@@ -0,0 +1,19 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.ExternalFlow
class Conf extends TaintTracking::Configuration {
Conf() { this = "qltest:lambdaFlow" }
override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getMethod().hasName("taint")
}
override predicate isSink(DataFlow::Node n) {
n.asExpr().(Argument).getCall().getCallee().hasName("sink")
}
}
from DataFlow::Node src, DataFlow::Node sink, Conf conf
where conf.hasFlow(src, sink)
select src, sink

View File

@@ -0,0 +1,27 @@
class Processor {
fun <T, R> process(f: (T) -> R, arg: T) : R {
return f(arg)
}
fun <T0, T1, R> process(f: (T0, T1) -> R, arg0: T0, arg1: T1) : R {
return f(arg0, arg1)
}
fun <T, R> process(
f: (T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T) -> R,
a: T, b: T) : R {
return f(a,b,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a)
}
fun <T, R> processExt(f: T.(T) -> R, ext: T, arg: T) : R {
return ext.f(arg)
}
}
class Helper {
companion object {
fun taint(): String = "taint"
fun notaint(): String = "notaint"
fun sink(a: Any?) { }
}
}