Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cb6941d
Account for JVM type equivalency when recognising unspecialised types
smowton Apr 21, 2022
12e3401
Map special getters onto their correct JVM names
smowton Apr 22, 2022
134f88f
Accept test results
smowton Apr 25, 2022
1e78f28
Add test for special method getters
smowton Apr 25, 2022
25fce5f
Identify data classes during extraction
tamasvajk Apr 26, 2022
fa0bd03
Fix extension property labels
tamasvajk Apr 26, 2022
ce87a89
Replace Map and similar functions with their Java cousins
smowton Apr 19, 2022
71d2e7b
Don't replace own callables, and use a more exact replacement-finding…
smowton Apr 20, 2022
77056c9
Add test expectations
smowton Apr 20, 2022
16af811
Allow imprecise matching for Kotlin -> Java method translation
smowton Apr 20, 2022
22e48ca
Accept test changes
smowton Apr 20, 2022
b1849f5
Expand error message
smowton Apr 26, 2022
7e17074
Allow arithmetic functions not mapping to Java equivalents
smowton Apr 27, 2022
776322b
Add foreach dataflow tests
tamasvajk Apr 26, 2022
538e059
Fix dataflow for `kotlin.Array.iterator()`
tamasvajk Apr 27, 2022
fbae0f5
Revert dataflow changes, extract actual iterator function
tamasvajk Apr 28, 2022
8d970a3
Don't extract private members of instantiated or external classes
smowton Apr 28, 2022
301fa11
Only extract parameter and method type-accesses once
smowton Apr 28, 2022
2600dcd
Fix extracting type accesses relating to proprerty getters/setters an…
smowton Apr 28, 2022
4ceb2f1
Add test
smowton Apr 28, 2022
394ec56
Add test case for local class declaration in field initializer
tamasvajk May 5, 2022
857a74c
Adjust class label generation to handle classes in field initializers
tamasvajk May 5, 2022
a51c2c4
Add test with colliding property accessor and function names
tamasvajk May 5, 2022
5958143
Fix colliding property accessor and function names
tamasvajk May 5, 2022
b0ee557
Fix expected test files
tamasvajk May 9, 2022
de003fd
Add test for return type of `<clinit>` methods
tamasvajk May 10, 2022
8b1a7c8
Fix return type of `<clinit>` methods
tamasvajk May 10, 2022
a8cf038
Add test for companion field declaring type
tamasvajk May 10, 2022
ccaafd7
Fix declaring type of companion field
tamasvajk May 10, 2022
a0f4960
Add test case for extension function called from java
tamasvajk May 10, 2022
cc92c65
Fix labels of extension function parameters
tamasvajk May 10, 2022
e3c2656
Update java/ql/lib/semmle/code/java/frameworks/KotlinStdLib.qll
atorralba May 12, 2022
63dadd8
Revert "Identify data classes during extraction"
smowton May 12, 2022
5ec9390
Autoformat
smowton May 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
ftw.writeCupackage(ftw.fileId, pkgId)

fileExtractor.extractClassSource(irDecl, !irDecl.isFileClass, false)
fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false)
} else {
fileExtractor.extractDeclaration(irDecl)
fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false)
}
}

Expand Down
99 changes: 67 additions & 32 deletions java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Large diffs are not rendered by default.

188 changes: 148 additions & 40 deletions java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion java/kotlin-extractor/src/main/kotlin/utils/ClassNames.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement

import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource

Expand Down Expand Up @@ -84,4 +86,7 @@ fun getContainingClassOrSelf(decl: IrDeclaration): IrClass? {
is IrClass -> decl
else -> decl.parentClassOrNull
}
}
}

fun getJavaEquivalentClassId(c: IrClass) =
c.fqNameWhenAvailable?.toUnsafe()?.let { JavaToKotlinClassMap.mapKotlinToJava(it) }
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.codeql.utils

import com.github.codeql.KotlinUsesExtractor
import com.github.codeql.getJavaEquivalentClassId
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
Expand All @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.constructedClassType
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.parentAsClass
Expand Down Expand Up @@ -195,13 +197,25 @@ fun IrTypeArgument.withQuestionMark(b: Boolean): IrTypeArgument =

typealias TypeSubstitution = (IrType, KotlinUsesExtractor.TypeContext, IrPluginContext) -> IrType

fun matchingTypeParameters(l: IrTypeParameter?, r: IrTypeParameter): Boolean {
if (l === r)
return true
if (l == null)
return false
// Special case: match List's E and MutableList's E, for example, because in the JVM lowering they will map to the same thing.
val lParent = l.parent as? IrClass ?: return false
val rParent = r.parent as? IrClass ?: return false
val lJavaId = getJavaEquivalentClassId(lParent) ?: lParent.classId
return (getJavaEquivalentClassId(rParent) ?: rParent.classId) == lJavaId && l.name == r.name
}

// Returns true if type is C<T1, T2, ...> where C is declared `class C<T1, T2, ...> { ... }`
fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List<IrTypeArgument>): Boolean {
val unspecialisedHere = paramsContainer.typeParameters.zip(args).all { paramAndArg ->
(paramAndArg.second as? IrTypeProjection)?.let {
// Type arg refers to the class' own type parameter?
it.variance == Variance.INVARIANT &&
it.type.classifierOrNull?.owner === paramAndArg.first
matchingTypeParameters(it.type.classifierOrNull?.owner as? IrTypeParameter, paramAndArg.first)
} ?: false
}
val remainingArgs = args.drop(paramsContainer.typeParameters.size)
Expand Down
1 change: 1 addition & 0 deletions java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.JMS
private import semmle.code.java.frameworks.RabbitMQ
private import semmle.code.java.regex.RegexFlowModels
private import semmle.code.java.frameworks.KotlinStdLib
}

private predicate sourceModelCsv(string row) {
Expand Down
11 changes: 11 additions & 0 deletions java/ql/lib/semmle/code/java/frameworks/KotlinStdLib.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** Definitions of taint steps in the KotlinStdLib framework */

import java
private import semmle.code.java.dataflow.ExternalFlow

private class KotlinStdLibSummaryCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
"kotlin.jvm.internal;ArrayIteratorKt;false;iterator;(Object[]);;Argument[0].ArrayElement;ReturnValue.Element;value"
}
}
65 changes: 63 additions & 2 deletions java/ql/test/kotlin/library-tests/classes/PrintAst.expected
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ generic_anonymous.kt:
# 3| 1: [ExprStmt] <Expr>;
# 3| 0: [ClassInstanceExpr] new (...)
# 3| -3: [TypeAccess] Object
# 3| 5: [Method] getX
# 3| 5: [Method] getX$private
# 3| 3: [TypeAccess] new Object(...) { ... }
# 3| 0: [TypeAccess] T
# 3| 5: [BlockStmt] { ... }
Expand All @@ -590,8 +590,69 @@ generic_anonymous.kt:
# 7| 5: [BlockStmt] { ... }
# 7| 0: [ReturnStmt] return ...
# 7| 0: [MethodAccess] getMember(...)
# 7| -1: [MethodAccess] getX(...)
# 7| -1: [MethodAccess] getX$private(...)
# 7| -1: [ThisAccess] this
localClassField.kt:
# 0| [CompilationUnit] localClassField
# 1| 1: [Class] A
# 1| 1: [Constructor] A
# 1| 5: [BlockStmt] { ... }
# 1| 0: [SuperConstructorInvocationStmt] super(...)
# 1| 1: [BlockStmt] { ... }
# 2| 0: [ExprStmt] <Expr>;
# 2| 0: [KtInitializerAssignExpr] ...=...
# 2| 0: [VarAccess] x
# 7| 1: [ExprStmt] <Expr>;
# 7| 0: [KtInitializerAssignExpr] ...=...
# 7| 0: [VarAccess] y
# 2| 2: [Method] getX
# 2| 3: [TypeAccess] Object
# 2| 5: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 2| 0: [VarAccess] this.x
# 2| -1: [ThisAccess] this
# 2| 2: [FieldDeclaration] Object x;
# 2| -1: [TypeAccess] Object
# 2| 0: [WhenExpr] when ...
# 2| 0: [WhenBranch] ... -> ...
# 2| 0: [BooleanLiteral] true
# 2| 1: [BlockStmt] { ... }
# 3| 0: [LocalTypeDeclStmt] class ...
# 3| 0: [LocalClass] L
# 3| 1: [Constructor] L
# 3| 5: [BlockStmt] { ... }
# 3| 0: [SuperConstructorInvocationStmt] super(...)
# 3| 1: [BlockStmt] { ... }
# 4| 1: [ExprStmt] <Expr>;
# 4| 0: [ClassInstanceExpr] new L(...)
# 4| -3: [TypeAccess] L
# 2| 1: [WhenBranch] ... -> ...
# 2| 0: [BooleanLiteral] true
# 5| 1: [BlockStmt] { ... }
# 7| 4: [Method] getY
# 7| 3: [TypeAccess] Object
# 7| 5: [BlockStmt] { ... }
# 7| 0: [ReturnStmt] return ...
# 7| 0: [VarAccess] this.y
# 7| -1: [ThisAccess] this
# 7| 4: [FieldDeclaration] Object y;
# 7| -1: [TypeAccess] Object
# 7| 0: [WhenExpr] when ...
# 7| 0: [WhenBranch] ... -> ...
# 7| 0: [BooleanLiteral] true
# 7| 1: [BlockStmt] { ... }
# 8| 0: [LocalTypeDeclStmt] class ...
# 8| 0: [LocalClass] L
# 8| 1: [Constructor] L
# 8| 5: [BlockStmt] { ... }
# 8| 0: [SuperConstructorInvocationStmt] super(...)
# 8| 1: [BlockStmt] { ... }
# 9| 1: [ExprStmt] <Expr>;
# 9| 0: [ClassInstanceExpr] new L(...)
# 9| -3: [TypeAccess] L
# 7| 1: [WhenBranch] ... -> ...
# 7| 0: [BooleanLiteral] true
# 10| 1: [BlockStmt] { ... }
local_anonymous.kt:
# 0| [CompilationUnit] local_anonymous
# 3| 1: [Class] Class1
Expand Down
3 changes: 3 additions & 0 deletions java/ql/test/kotlin/library-tests/classes/classes.expected
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
| generic_anonymous.kt:0:0:0:0 | Generic_anonymousKt | Generic_anonymousKt | final, public |
| generic_anonymous.kt:1:1:9:1 | Generic | Generic | final, private |
| generic_anonymous.kt:3:19:5:3 | new Object(...) { ... } | <anonymous class> | final, private |
| localClassField.kt:1:1:11:1 | A | A | final, public |
| localClassField.kt:3:9:3:19 | L | A$L | final, private |
| localClassField.kt:8:9:8:19 | L | A$L | final, private |
| local_anonymous.kt:3:1:36:1 | Class1 | LocalAnonymous.Class1 | final, public |
| local_anonymous.kt:5:16:7:9 | new Object(...) { ... } | <anonymous class> | final, private |
| local_anonymous.kt:11:9:11:24 | | Class1$ | final, private |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ superCall
| classes.kt:129:17:131:17 | super(...) |
| generic_anonymous.kt:1:1:9:1 | super(...) |
| generic_anonymous.kt:3:19:5:3 | super(...) |
| localClassField.kt:1:1:11:1 | super(...) |
| localClassField.kt:3:9:3:19 | super(...) |
| localClassField.kt:8:9:8:19 | super(...) |
| local_anonymous.kt:3:1:36:1 | super(...) |
| local_anonymous.kt:5:16:7:9 | super(...) |
| local_anonymous.kt:11:9:11:24 | super(...) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
| generic_anonymous.kt:4:20:4:20 | Generic.this | Generic |
| generic_anonymous.kt:4:20:4:20 | getT(...) | T |
| generic_anonymous.kt:7:3:7:22 | T | T |
| generic_anonymous.kt:7:15:7:15 | getX(...) | new Object(...) { ... } |
| generic_anonymous.kt:7:15:7:15 | getX$private(...) | new Object(...) { ... } |
| generic_anonymous.kt:7:15:7:15 | this | Generic |
| generic_anonymous.kt:7:17:7:22 | getMember(...) | T |
| generic_anonymous.kt:11:1:11:56 | String | String |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
| classes.kt:118:9:123:9 | class ... | classes.kt:118:9:123:9 | | classes.kt:117:5:124:5 | fn2 | classes.kt:109:1:136:1 | C1 |
| classes.kt:119:13:121:13 | class ... | classes.kt:119:13:121:13 | Local2 | classes.kt:118:9:123:9 | localFn | classes.kt:109:1:136:1 | C1 |
| classes.kt:129:17:131:17 | class ... | classes.kt:129:17:131:17 | Local3 | classes.kt:128:13:133:13 | fn | classes.kt:127:16:134:9 | new Object(...) { ... } |
| localClassField.kt:3:9:3:19 | class ... | localClassField.kt:3:9:3:19 | L | localClassField.kt:1:1:11:1 | A | localClassField.kt:1:1:11:1 | A |
| localClassField.kt:8:9:8:19 | class ... | localClassField.kt:8:9:8:19 | L | localClassField.kt:1:1:11:1 | A | localClassField.kt:1:1:11:1 | A |
| local_anonymous.kt:11:9:11:24 | class ... | local_anonymous.kt:11:9:11:24 | | local_anonymous.kt:10:5:13:5 | fn2 | local_anonymous.kt:3:1:36:1 | Class1 |
| local_anonymous.kt:25:9:25:27 | class ... | local_anonymous.kt:25:9:25:27 | LocalClass | local_anonymous.kt:24:5:27:5 | fn5 | local_anonymous.kt:3:1:36:1 | Class1 |
11 changes: 11 additions & 0 deletions java/ql/test/kotlin/library-tests/classes/localClassField.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class A {
val x = if (true) {
class L { }
L()
} else {}

val y = if (true) {
class L { }
L()
} else {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
| file://<external>/SuperChain2.class:0:0:0:0 | SuperChain2<T5,String> | file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T5,String> |
| generic_anonymous.kt:1:1:9:1 | Generic | file://<external>/Object.class:0:0:0:0 | Object |
| generic_anonymous.kt:3:19:5:3 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object |
| localClassField.kt:1:1:11:1 | A | file://<external>/Object.class:0:0:0:0 | Object |
| localClassField.kt:3:9:3:19 | L | file://<external>/Object.class:0:0:0:0 | Object |
| localClassField.kt:8:9:8:19 | L | file://<external>/Object.class:0:0:0:0 | Object |
| local_anonymous.kt:3:1:36:1 | Class1 | file://<external>/Object.class:0:0:0:0 | Object |
| local_anonymous.kt:5:16:7:9 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object |
| local_anonymous.kt:11:9:11:24 | | file://<external>/Object.class:0:0:0:0 | Object |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ commentOwners
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | comments.kt:12:1:31:1 | Group |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | members |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | members |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:46 | getMembers |
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:46 | getMembers$private |
| comments.kt:19:5:22:7 | /**\n * Adds a [member] to this group.\n * @return the new size of the group.\n */ | comments.kt:23:5:26:5 | add |
| comments.kt:35:5:35:34 | /** Medium is in the middle */ | comments.kt:36:5:36:14 | Medium |
| comments.kt:37:5:37:23 | /** This is high */ | comments.kt:38:5:38:11 | High |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
| companion_objects.kt:1:1:6:1 | MyClass | companion_objects.kt:3:5:5:5 | MyClassCompanion | companion_objects.kt:3:5:5:5 | MyClassCompanion | final,public,static |
| companion_objects.kt:8:1:13:1 | MyInterface | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | final,public,static |
| companion_objects.kt:1:1:6:1 | MyClass | companion_objects.kt:3:5:5:5 | MyClassCompanion | companion_objects.kt:3:5:5:5 | MyClassCompanion | companion_objects.kt:1:1:6:1 | MyClass | final,public,static |
| companion_objects.kt:8:1:13:1 | MyInterface | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | companion_objects.kt:8:1:13:1 | MyInterface | final,public,static |
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ where
c.fromSource() and
cco = c.getCompanionObject() and
f = cco.getInstance()
select c, f, cco, concat(f.getAModifier().toString(), ",")
select c, f, cco, f.getDeclaringType(), concat(f.getAModifier().toString(), ",")
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
| dc.kt:0:0:0:0 | times(...) | Int.times |
| dc.kt:0:0:0:0 | toString(...) | Arrays.toString |
| dc.kt:0:0:0:0 | toString(...) | Arrays.toString |
| dc.kt:1:1:1:71 | super(...) | Any.Any |
| dc.kt:1:1:1:71 | super(...) | Object.Object |
22 changes: 22 additions & 0 deletions java/ql/test/kotlin/library-tests/dataflow/foreach/C1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
public final class C1 {
public final String taint(String t) {
return t;
}

public final void sink(Object a) {
}

public final void test() {
String[] l = new String[]{this.taint("a"), ""};
this.sink(l);
this.sink(l[0]);

for(int i = 0; i < l.length; i++) {
this.sink(l[i]);
}

for (String s : l) {
this.sink(s);
}
}
}
18 changes: 18 additions & 0 deletions java/ql/test/kotlin/library-tests/dataflow/foreach/C2.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class C2 {
fun taint(t: String): String {
return t
}

fun sink(a: Any?) {}
fun test() {
val l = arrayOf(taint("a"), "")
sink(l)
sink(l[0])
for (i in l.indices) {
sink(l[i])
}
for (s in l) {
sink(s)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| C1.java:10:44:10:46 | "a" | C1.java:11:17:11:17 | l |
| C1.java:10:44:10:46 | "a" | C1.java:12:17:12:20 | ...[...] |
| C1.java:10:44:10:46 | "a" | C1.java:15:20:15:23 | ...[...] |
| 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 |
19 changes: 19 additions & 0 deletions java/ql/test/kotlin/library-tests/dataflow/foreach/test.ql
Original file line number Diff line number Diff line change
@@ -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:foreach-array-iterator" }

override predicate isSource(DataFlow::Node n) {
n.asExpr().(Argument).getCall().getCallee().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
Loading