1. Taming Reflection
Aiding Static Analysis in the Presence of Reflection
and Custom Class Loaders
Eric Bodden, Andreas Sewe, Jan Sinschek,
Hela Oueslati and Mira Mezini
3. Reflective method calls
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
Evil.doEvil(c); m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
public static void doEvil(Connection c) {
c.close();
}
}
3
4. Reflective method calls
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
Evil.doEvil(c); m.invoke(null,c);
c.write("ohoh...");
}
public class Evil { hard to analyze statically!
public static void doEvil(Connection c) {
c.close();
}
}
3
5. Reflective method calls
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( args[0] );
"Evil"
Method m = c.getMethod("doEvil", Connection.class);
Evil.doEvil(c); m.invoke(null,c);
c.write("ohoh...");
}
imp ossible
public class Evil { hard to analyze statically!
public static void doEvil(Connection c) {
c.close();
}
}
3
6. Reflective method calls
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( args[0] );
"Evil"
Method m = c.getMethod("doEvil", Connection.class);
Evil.doEvil(c); m.invoke(null,c);
c.write("ohoh...");
}
imp ossible
public class Evil { hard to analyze statically!
public static void doEvil(Connection c) {
c.close();
}
Present a pragmatic,
} partial solution
3
12. Play-Out
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
public static void doEvil(Connection c) {
c.close();
}
}
9
13. Play-Out
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
$ javac Main.java
public static void doEvil(Connection c) {
$ java ‐javaagent:poa‐trunk.jar=out Main
c.close();
}
=============================================
} TamiFlex Play‐Out Agent Version trunk
Found 2 new log entries.
Log file written to: out/refl.log
$ cat out/refl.log
Class.forName;Evil;Main.main;6
Method.invoke;<Evil: void doEvil(Connection)>;Main.main;8;
9
14. Play-Out
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
$ javac Main.java
public static void doEvil(Connection c) {
$ java ‐javaagent:poa‐trunk.jar=out Main
c.close();
}
=============================================
} TamiFlex Play‐Out Agent Version trunk
Found 2 new log entries.
Log file written to: out/refl.log
$ cat out/refl.log
Class.forName;Evil;Main.main;6
Method.invoke;<Evil: void doEvil(Connection)>;Main.main;8;
kind of call
9
15. Play-Out
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
$ javac Main.java
public static void doEvil(Connection c) {
$ java ‐javaagent:poa‐trunk.jar=out Main
c.close();
}
=============================================
} TamiFlex Play‐Out Agent Version trunk
Found 2 new log entries.
Log file written to: out/refl.log
$ cat out/refl.log
Class.forName;Evil;Main.main;6
fully resolved Method.invoke;<Evil: void doEvil(Connection)>;Main.main;8;
target of call
9
16. Play-Out
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
$ javac Main.java
public static void doEvil(Connection c) {
$ java ‐javaagent:poa‐trunk.jar=out Main
c.close();
}
=============================================
} TamiFlex Play‐Out Agent Version trunk
Found 2 new log entries.
Log file written to: out/refl.log
$ cat out/refl.log
Class.forName;Evil;Main.main;6
Method.invoke;<Evil: void doEvil(Connection)>;Main.main;8;
location of call
9
17. Play-Out
public class Main {
public static void main(String[] args) throws Exception {
Connection c = new Connection();
Class c = Class.forName( "Evil" );
Method m = c.getMethod("doEvil", Connection.class);
m.invoke(null,c);
c.write("ohoh...");
}
public class Evil {
$ find out ‐name *.class
public static void doEvil(Connection c) {
out/com/apple/java/BackwardsCompatibility.class
c.close();
} out/java/io/BufferedInputStream.class
out/java/io/BufferedOutputStream.class
} out/java/io/BufferedReader.class
out/java/io/BufferedWriter.class
out/java/io/ByteArrayInputStream.class
out/java/io/ByteArrayOutputStream.class
out/java/io/Closeable.class
out/java/io/DataInput.class
...
9
24. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Play‐Out Agent
dump every
loaded class to disk
Main.class
11
25. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Method.class
Play‐Out Agent
dump every
loaded class to disk
Main.class
11
26. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Method.class
Play‐Out Agent
dump every
loaded class to disk
Main.class
11
27. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Method.class
Play‐Out Agent
dump every
loaded class to disk
Main.class
Method.class
11
28. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Method.class
call to
Method.invoke
Play‐Out Agent
dump every
loaded class to disk
Main.class
Method.class
11
29. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Method.class
call to
Method.invoke
Play‐Out Agent
dump every
loaded class to disk
write entry Main.class
to log file Method.class
11
30. Play-out Agent
load class java.lang.reflect.Method
Program ClassLoader
Method.class
call to
Method.invoke
Play‐Out Agent
Constructor.class
dump every
loaded class to disk
Class.class
write entry Main.class
Constructor.class
to log file Method.class
Class.class 11
32. Booster transforms program into
statically analyzable version
public class Main {
Connection.<init>
public static void main(String[] args) throws Exception {
?
Connection c = new Connection();
Class cl = Class.forName(args[0]);
?
Method m = cl.getMethod("do"+"Evil", Connection.class);
Class.forName
Main.main
m.invoke(null,c);
c.write("ohoh..."); Class.getMethod
}
public class Evil { Method.invoke
public static void doEvil(Connection c) {
c.close();
} Connection.write
}
13
33. Booster transforms program into
statically analyzable version
public class Main {
Connection.<init>
public static void main(String[] args) throws Exception {
?
Connection c = new Connection();
Class cl = Class.forName(args[0]);
?
Method m = cl.getMethod("do"+"Evil", Connection.class);
Class.forName
Main.main
m.invoke(null,c);
c.write("ohoh..."); Class.getMethod
}
public class Evil { Method.invoke
public static void doEvil(Connection c) {
c.close();
} Connection.write
} $ cat out/refl.log
Class.forName;Evil;Main.main;6
Method.invoke;<Evil: void doEvil(Connection)>;Main.main;8;
13
34. Booster transforms program into
statically analyzable version
public class Main {
public static void main(String[] args) throws Exception { Connection.<init>
Connection c = new Connection();
Class cl;
if(args[0].equals("Evil"))
cl = Evil.class;
else {
Class.forName
TamiFlexRT.warnClassForName(args[0]); Main.main
cl = Class.forName(args[0]);
}
Class.getMethod
Method m = cl.getMethod("do"+"Evil", Connection.class);
if(m.getDeclaringClass().getName().equals("Evil") && Method.invoke
m.getName().equals("doEvil"))
Evil.doEvil(c);
else {
TamiFlexRT.warnMethodInvoke(m); Connection.write
m.invoke(null,c);
}
c.write("ohoh...");
} Evil.doEvil Connection.close
...
}
13
35. Booster transforms program into
statically analyzable version
public class Main {
public static void main(String[] args) throws Exception { Connection.<init>
Connection c = new Connection();
Class cl;
if(args[0].equals("Evil"))
cl = Evil.class;
else {
Class.forName
TamiFlexRT.warnClassForName(args[0]); Main.main
cl = Class.forName(args[0]);
}
Class.getMethod
Method m = cl.getMethod("do"+"Evil", Connection.class);
if(m.getDeclaringClass().getName().equals("Evil") && Method.invoke
m.getName().equals("doEvil"))
Evil.doEvil(c);
else {
TamiFlexRT.warnMethodInvoke(m); Connection.write
m.invoke(null,c);
}
c.write("ohoh...");
} Evil.doEvil Connection.close
...
}
13
36. Booster
Implemented on top of Soot
Operates on 3-address code (Jimple)
generated from bytecode
Input: class files recorded by play-out agent
Emits class files of “boosted” program
Some nitty-gritty details with respect to the
use of non-standard class loaders (see paper)
14
37. Booster
Program ClassLoader
Main.class
Method.class
Main.class
Play‐Out Agent
Constructor.class
dump every
loaded class to disk
Class.class
write entry
to log file
Main.class
15
38. Booster
Program ClassLoader
Main.class
Method.class
Main.class
Play‐Out Agent
Constructor.class
dump every
loaded class to disk
Class.class
write entry
to log file Booster Main.class
Main.class
15
39. Booster
Program ClassLoader
Main.class
Method.class
Main.class
Play‐Out Agent
Main.class
Constructor.class
dump every
loaded class to disk
Class.class
write entry Static Analysis
to log file Booster Main.class
Main.class
15
40. Booster
Program ClassLoader
Main.class
Method.class
Main.class
Play‐Out Agent
?
Main.class
Constructor.class
dump every
loaded class to disk
Class.class
write entry Static Analysis
to log file Booster Main.class
Main.class
15
42. Play-in Agent
load from
somewhere,
load class Main or generate
Program Custom
ClassLoader
Play‐In Agent
Main.class
17
43. Play-in Agent
load from
somewhere,
load class Main or generate
Program Custom
ClassLoader
Play‐In Agent
Main.class
17
44. Play-in Agent
load from
somewhere,
load class Main or generate
Program Custom
ClassLoader
Main.class
Play‐In Agent
Main.class
17
45. Play-in Agent
load from
somewhere,
load class Main or generate
Program Custom
ClassLoader
Play‐In Agent
search
Main.class
transformed
version of Main
17
46. Play-in Agent
load from
somewhere,
load class Main or generate
Program Custom
ClassLoader
replace Main by
Main.class
transformed
class
Play‐In Agent
search
transformed
version of Main
17
52. Solution
Normalization: replace randomized name by
normalized name
in the log, the binaries, everywhere...
Quite involved: classes to normalize may be
interdependent
Details in the paper
More details in our Tech Report
Yet more details in the source
21
58. Correctness
static call graph ⊇ ∪{recorded call graph} ?
computed with Soot/Spark recorded with JVMTI agent
23
59. Correctness
static call graph ⊇ ∪{recorded call graph} ?
computed with Soot/Spark recorded with JVMTI agent
Yes!
(call graphs are representative;
tested with Lhotak’s Call-graph comparison tool ProBe)
23
60. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
61. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
62. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
63. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
64. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
65. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
66. Effectiveness: How much does log-file
quality depend on program input?
Dacapo “bach” release
24
67. Effectiveness: How much does log-file
quality depend on program input?
Can now safely use static analyses on DaCapo:
Warnings never triggered!
24
68. Effectiveness: How much does log-file
quality depend on program input?
Other prominent programs/frameworks
25
69. Effectiveness: How much does log-file
quality depend on program input?
Appro ach requ ires go o d
“feature co verage”
26
70. 3.00
2.62
2.00
1.22 1.18
1.02 1.02 1.08 1.06 1.09 1.03 1.05 1.04
1.00 1.00
Play-Out 1.00
vro
ra
ati
k e
ips fo
p h2 on e x h
arc pm
d ow s p
an soa xala
n
a b cl j yth ind e nfl e
eb rade
e lu lus su d
tra t
3.00
2.00
1.12 1.09 1.06 1.06 1.04 1.05
1.03 1.00 1.04 1.03 1.04 1.00 1.00
Play-In 1.00
vro
r a
ati
k e
ips fo
p h2 o n e x h
arc pm
d o w s p
an soa xala
n
a b cl jyth i nd e nfl e
eb rade
e lu lus su d
tra t
71. Related Work
Partial evaluation
Braux & Noyé, PEPM 1999
String analysis
Christensen et al., SAS 2003
Livshits et al., APLAS 2005
Application extraction
Tip et. al, TOPLAS 2002
“Finding the code”
Bessey et. al (Coverity), CACM 2010
28
72. Related Work
Online Call-Graph Construction
Hirzel et al., TOPLAS 2007
Blended Analysis
Dufour et al., ISSTA 2007
Runtime type inference in PRuby
Furr et al., OOPSLA 2009
29
75. http://tamiflex.googlecode.com/
MODULARITY - aosd 2012 submit to
2nd of 3 submission deadlines: July 14th
Record
number of
submitted &
accepted
July 17th-21st, 2011, Toronto papers
attend
Editor's Notes
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
With this, even works for runtime-generated classes!\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
TODO: Warnings never triggered\nTODO: Venn: num of Methods or num of edges?\n
\n
\n
TODO: redo with Numbers, split up into POA and PIA\n
\n
TODO: talk about relative number of resolved call sites\n