diff --git a/java/ql/lib/change-notes/2023-12-05-kotlin-array-get-set.md b/java/ql/lib/change-notes/2023-12-05-kotlin-array-get-set.md new file mode 100644 index 00000000000..60c56a8f8f3 --- /dev/null +++ b/java/ql/lib/change-notes/2023-12-05-kotlin-array-get-set.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Taint tracking now understands Kotlin's `Array.get` and `Array.set` methods. diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 70e3258566c..a113c28de82 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -4,6 +4,10 @@ import semmle.code.java.Maps private import semmle.code.java.dataflow.SSA private import DataFlowUtil +private class ArrayType extends RefType { + ArrayType() { this.getSourceDeclaration().getASourceSupertype*() instanceof Array } +} + private class EntryType extends RefType { EntryType() { this.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "Map$Entry") @@ -446,6 +450,14 @@ predicate arrayStoreStep(Node node1, Node node2) { exists(Assignment assign | assign.getSource() = node1.asExpr() | node2.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getDest().(ArrayAccess).getArray() ) + or + exists(Expr arr, Call call | + arr = node2.asExpr() and + call.getArgument(1) = node1.asExpr() and + call.getQualifier() = arr and + arr.getType() instanceof ArrayType and + call.getCallee().getName() = "set" + ) } private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType) { @@ -470,6 +482,14 @@ predicate arrayReadStep(Node node1, Node node2, Type elemType) { node2.asExpr() = aa ) or + exists(Expr arr, Call call | + arr = node1.asExpr() and + call = node2.asExpr() and + arr.getType() instanceof ArrayType and + call.getCallee().getName() = "get" and + call.getQualifier() = arr + ) + or exists(Array arr | enhancedForStmtStep(node1, node2, arr) and arr.getComponentType() = elemType diff --git a/java/ql/test-kotlin1/library-tests/dataflow/foreach/C2.kt b/java/ql/test-kotlin1/library-tests/dataflow/foreach/C2.kt index 7a98abaa110..e8bec66e974 100644 --- a/java/ql/test-kotlin1/library-tests/dataflow/foreach/C2.kt +++ b/java/ql/test-kotlin1/library-tests/dataflow/foreach/C2.kt @@ -8,6 +8,7 @@ class C2 { val l = arrayOf(taint("a"), "") sink(l) sink(l[0]) + sink(l.get(0)) for (i in l.indices) { sink(l[i]) } @@ -15,4 +16,15 @@ class C2 { sink(s) } } + + fun test2() { + val l1 = arrayOf("") + val l2 = arrayOf("") + l1[0] = taint("a") + l2.set(0, taint("a")) + sink(l1[0]) + sink(l2[0]) + sink(l1.get(0)) + sink(l2.get(0)) + } } diff --git a/java/ql/test-kotlin1/library-tests/dataflow/foreach/test.expected b/java/ql/test-kotlin1/library-tests/dataflow/foreach/test.expected index 7c7b382a9ad..29b8c7b7c82 100644 --- a/java/ql/test-kotlin1/library-tests/dataflow/foreach/test.expected +++ b/java/ql/test-kotlin1/library-tests/dataflow/foreach/test.expected @@ -4,5 +4,10 @@ | C1.java:10:44:10:46 | "a" | C1.java:19:20:19:20 | s | | C2.kt:8:32:8:32 | "a" | C2.kt:9:14:9:14 | l | | C2.kt:8:32:8:32 | "a" | C2.kt:10:14:10:17 | ...[...] | -| C2.kt:8:32:8:32 | "a" | C2.kt:12:18:12:21 | ...[...] | -| C2.kt:8:32:8:32 | "a" | C2.kt:15:18:15:18 | s | +| C2.kt:8:32:8:32 | "a" | C2.kt:11:14:11:21 | get(...) | +| C2.kt:8:32:8:32 | "a" | C2.kt:13:18:13:21 | ...[...] | +| C2.kt:8:32:8:32 | "a" | C2.kt:16:18:16:18 | s | +| C2.kt:23:24:23:24 | "a" | C2.kt:25:14:25:18 | ...[...] | +| C2.kt:23:24:23:24 | "a" | C2.kt:27:14:27:22 | get(...) | +| C2.kt:24:26:24:26 | "a" | C2.kt:26:14:26:18 | ...[...] | +| C2.kt:24:26:24:26 | "a" | C2.kt:28:14:28:22 | get(...) |