Skip to content

Commit a247ae4

Browse files
authored
Merge pull request github#5843 from JLLeitschuh/feat/JLL/improve_kryo_support
[Java] Fix Kryo FP & Kryo 5 Support
2 parents 74ae2e0 + 0d9a85c commit a247ae4

File tree

8 files changed

+194
-2
lines changed

8 files changed

+194
-2
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Add support for version 5 of the Kryo serialization/deserialization framework.
3+
* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder`. [#4992](https://github.com/github/codeql/issues/4992)

java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ private predicate summaryModelCsv(string row) {
292292
"java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint",
293293
"java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint",
294294
"com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint",
295+
"com.esotericsoftware.kryo5.io;Input;false;Input;;;Argument[0];Argument[-1];taint",
295296
"java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint",
296297
"java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint",
297298
"java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint",

java/ql/src/semmle/code/java/frameworks/Kryo.qll

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,60 @@
33
*/
44

55
import java
6+
private import semmle.code.java.dataflow.DataFlow
7+
private import semmle.code.java.dataflow.FlowSteps
68

79
/**
810
* The type `com.esotericsoftware.kryo.Kryo`.
911
*/
1012
class Kryo extends RefType {
11-
Kryo() { this.hasQualifiedName("com.esotericsoftware.kryo", "Kryo") }
13+
Kryo() {
14+
hasQualifiedName("com.esotericsoftware.kryo", "Kryo") or
15+
hasQualifiedName("com.esotericsoftware.kryo5", "Kryo")
16+
}
1217
}
1318

1419
/**
1520
* A Kryo input stream.
1621
*/
1722
class KryoInput extends RefType {
18-
KryoInput() { this.hasQualifiedName("com.esotericsoftware.kryo.io", "Input") }
23+
KryoInput() {
24+
hasQualifiedName("com.esotericsoftware.kryo.io", "Input") or
25+
hasQualifiedName("com.esotericsoftware.kryo5.io", "Input")
26+
}
27+
}
28+
29+
/**
30+
* A Kryo pool.
31+
*/
32+
class KryoPool extends RefType {
33+
KryoPool() {
34+
hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool") or
35+
hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool")
36+
}
37+
}
38+
39+
/**
40+
* A Kryo pool builder.
41+
*/
42+
class KryoPoolBuilder extends RefType {
43+
KryoPoolBuilder() {
44+
hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool$Builder") or
45+
hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool$Builder")
46+
}
47+
}
48+
49+
/**
50+
* A Kryo pool builder method used in a fluent API call chain.
51+
*/
52+
class KryoPoolBuilderMethod extends Method {
53+
KryoPoolBuilderMethod() {
54+
getDeclaringType() instanceof KryoPoolBuilder and
55+
(
56+
getReturnType() instanceof KryoPoolBuilder or
57+
getReturnType() instanceof KryoPool
58+
)
59+
}
1960
}
2061

2162
/**
@@ -45,3 +86,13 @@ class KryoEnableWhiteListing extends MethodAccess {
4586
)
4687
}
4788
}
89+
90+
/**
91+
* A KryoPool method that uses a Kryo instance.
92+
*/
93+
class KryoPoolRunMethod extends Method {
94+
KryoPoolRunMethod() {
95+
getDeclaringType() instanceof KryoPool and
96+
hasName("run")
97+
}
98+
}

java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,65 @@ class SafeKryo extends DataFlow2::Configuration {
4848
ma.getMethod() instanceof KryoReadObjectMethod
4949
)
5050
}
51+
52+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
53+
stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or
54+
stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or
55+
stepKryoPoolBuilderChainMethod(node1, node2) or
56+
stepKryoPoolBorrowMethod(node1, node2)
57+
}
58+
59+
/**
60+
* Holds when a functional expression is used to create a `KryoPool.Builder`.
61+
* Eg. `new KryoPool.Builder(() -> new Kryo())`
62+
*/
63+
private predicate stepKryoPoolBuilderFactoryArgToConstructor(
64+
DataFlow::Node node1, DataFlow::Node node2
65+
) {
66+
exists(ConstructorCall cc, FunctionalExpr fe |
67+
cc.getConstructedType() instanceof KryoPoolBuilder and
68+
fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and
69+
node2.asExpr() = cc and
70+
cc.getArgument(0) = fe
71+
)
72+
}
73+
74+
/**
75+
* Holds when a `KryoPool.run` is called to use a `Kryo` instance.
76+
* Eg. `pool.run(kryo -> ...)`
77+
*/
78+
private predicate stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(
79+
DataFlow::Node node1, DataFlow::Node node2
80+
) {
81+
exists(MethodAccess ma |
82+
ma.getMethod() instanceof KryoPoolRunMethod and
83+
node1.asExpr() = ma.getQualifier() and
84+
ma.getArgument(0).(FunctionalExpr).asMethod().getParameter(0) = node2.asParameter()
85+
)
86+
}
87+
88+
/**
89+
* Holds when a `KryoPool.Builder` method is called fluently.
90+
*/
91+
private predicate stepKryoPoolBuilderChainMethod(DataFlow::Node node1, DataFlow::Node node2) {
92+
exists(MethodAccess ma |
93+
ma.getMethod() instanceof KryoPoolBuilderMethod and
94+
ma = node2.asExpr() and
95+
ma.getQualifier() = node1.asExpr()
96+
)
97+
}
98+
99+
/**
100+
* Holds when a `KryoPool.borrow` method is called.
101+
*/
102+
private predicate stepKryoPoolBorrowMethod(DataFlow::Node node1, DataFlow::Node node2) {
103+
exists(MethodAccess ma |
104+
ma.getMethod() =
105+
any(Method m | m.getDeclaringType() instanceof KryoPool and m.hasName("borrow")) and
106+
node1.asExpr() = ma.getQualifier() and
107+
node2.asExpr() = ma
108+
)
109+
}
51110
}
52111

53112
predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
import java.io.*;
3+
import java.net.Socket;
4+
import com.esotericsoftware.kryo.Kryo;
5+
import com.esotericsoftware.kryo.pool.KryoPool;
6+
import com.esotericsoftware.kryo.io.Input;
7+
8+
public class KryoTest {
9+
10+
private Kryo getSafeKryo() {
11+
Kryo kryo = new Kryo();
12+
kryo.setRegistrationRequired(true);
13+
// ... kryo.register(A.class) ...
14+
return kryo;
15+
}
16+
17+
public void kryoDeserialize(Socket sock) throws java.io.IOException {
18+
KryoPool kryoPool = new KryoPool.Builder(this::getSafeKryo).softReferences().build();
19+
Input input = new Input(sock.getInputStream());
20+
Object o = kryoPool.run(kryo -> kryo.readClassAndObject(input)); // OK
21+
}
22+
23+
public void kryoDeserialize2(Socket sock) throws java.io.IOException {
24+
KryoPool kryoPool = new KryoPool.Builder(this::getSafeKryo).softReferences().build();
25+
Input input = new Input(sock.getInputStream());
26+
Kryo k = kryoPool.borrow();
27+
try {
28+
Object o = k.readClassAndObject(input); // OK
29+
} finally {
30+
kryoPool.release(k);
31+
}
32+
}
33+
34+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.esotericsoftware.kryo.pool;
2+
3+
import com.esotericsoftware.kryo.Kryo;
4+
5+
public interface KryoCallback<T> {
6+
T execute (Kryo kryo);
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.esotericsoftware.kryo.pool;
2+
3+
import com.esotericsoftware.kryo.Kryo;
4+
5+
public interface KryoFactory {
6+
Kryo create ();
7+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.esotericsoftware.kryo.pool;
2+
3+
import com.esotericsoftware.kryo.Kryo;
4+
import java.util.Queue;
5+
6+
public interface KryoPool {
7+
8+
Kryo borrow ();
9+
10+
void release (Kryo kryo);
11+
12+
<T> T run (KryoCallback<T> callback);
13+
14+
static class Builder {
15+
public Builder (KryoFactory factory) {
16+
}
17+
18+
public Builder queue (Queue<Kryo> queue) {
19+
return null;
20+
}
21+
22+
public Builder softReferences () {
23+
return null;
24+
}
25+
26+
public KryoPool build () {
27+
return null;
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)