Skip to content

Commit 9bc7461

Browse files
capozTim Van Den Broecke
authored andcommitted
Add support for return instruction sinks in taint analysis
1 parent 41a3010 commit 9bc7461

File tree

14 files changed

+387
-128
lines changed

14 files changed

+387
-128
lines changed

base/src/main/java/proguard/analysis/cpa/domain/taint/TaintSink.java

Lines changed: 6 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,94 +18,33 @@
1818

1919
package proguard.analysis.cpa.domain.taint;
2020

21-
import java.util.Objects;
22-
import java.util.Set;
23-
import java.util.stream.Collectors;
24-
2521
/**
2622
* A {@link TaintSink} specifies a sink for the taint analysis. A sink can be sensitive to
2723
* the instance, the arguments, or the static fields. If a sink S is sensitive to X, then
2824
* if X is tainted, we conclude that the taint has reached S.
2925
*
3026
* @author Dmitry Ivanov
3127
*/
32-
public class TaintSink
28+
public abstract class TaintSink
3329
{
34-
public final String fqn;
35-
public final boolean takesInstance;
36-
public final Set<Integer> takesArgs;
37-
public final Set<String> takesGlobals;
30+
public final String fqn;
3831

3932
/**
4033
* Create a taint sink.
4134
*
4235
* @param fqn the fully qualified name of a sink method
43-
* @param takesInstance whether the sink is sensitive to the calling instance
44-
* @param takesArgs a set of sensitive arguments
45-
* @param takesGlobals a set of sensitive global variables
4636
*/
47-
public TaintSink(String fqn, boolean takesInstance, Set<Integer> takesArgs, Set<String> takesGlobals)
37+
public TaintSink(String fqn)
4838
{
49-
if (!takesInstance && takesArgs.isEmpty() && takesGlobals.isEmpty())
50-
{
51-
throw new RuntimeException(String.format("Tainted sink for method %s must have taint somewhere!", fqn));
52-
}
5339
this.fqn = fqn;
54-
this.takesInstance = takesInstance;
55-
this.takesArgs = takesArgs;
56-
this.takesGlobals = takesGlobals;
5740
}
5841

5942
@Override
60-
public boolean equals(Object o)
61-
{
62-
if (this == o)
63-
{
64-
return true;
65-
}
66-
if (!(o instanceof TaintSink))
67-
{
68-
return false;
69-
}
70-
TaintSink taintSink = (TaintSink) o;
71-
return takesInstance == taintSink.takesInstance
72-
&& Objects.equals(fqn, taintSink.fqn)
73-
&& Objects.equals(takesArgs, taintSink.takesArgs)
74-
&& Objects.equals(takesGlobals, taintSink.takesGlobals);
75-
}
43+
public abstract boolean equals(Object o);
7644

7745
@Override
78-
public int hashCode()
79-
{
80-
return Objects.hash(fqn, takesInstance, takesArgs, takesGlobals);
81-
}
46+
public abstract int hashCode();
8247

8348
@Override
84-
public String toString()
85-
{
86-
StringBuilder result = new StringBuilder("[TaintSink] ").append(fqn);
87-
if (takesInstance)
88-
{
89-
result.append(", takes instance");
90-
}
91-
if (!takesArgs.isEmpty())
92-
{
93-
result.append(", takes args (")
94-
.append(takesArgs.stream()
95-
.map(Objects::toString)
96-
.sorted()
97-
.collect(Collectors.joining(", ")))
98-
.append(")");
99-
}
100-
if (!takesGlobals.isEmpty())
101-
{
102-
result.append(", takes globals (")
103-
.append(takesGlobals.stream()
104-
.sorted()
105-
.collect(Collectors.joining(", ")))
106-
.append(")");
107-
}
108-
109-
return result.toString();
110-
}
49+
public abstract String toString();
11150
}

base/src/main/java/proguard/analysis/cpa/jvm/cfa/edges/JvmCallCfaEdge.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import proguard.analysis.datastructure.callgraph.Call;
2222
import proguard.analysis.cpa.interfaces.CallEdge;
2323
import proguard.analysis.cpa.jvm.cfa.nodes.JvmCfaNode;
24+
import proguard.classfile.MethodSignature;
2425

2526
/**
2627
* A {@link JvmCfaEdge} representing a call to another method, linking to the first node of the called method.
@@ -57,6 +58,12 @@ public JvmCallCfaEdge(JvmCfaNode source, JvmCfaNode target, Call call)
5758
this.call = call;
5859
}
5960

61+
@Override
62+
public MethodSignature targetSignature()
63+
{
64+
return call.getTarget();
65+
}
66+
6067
// Implementations for CallEdge
6168

6269
@Override

base/src/main/java/proguard/analysis/cpa/jvm/cfa/edges/JvmCfaEdge.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import proguard.analysis.cpa.interfaces.CfaEdge;
2222
import proguard.analysis.cpa.jvm.cfa.nodes.JvmCfaNode;
23+
import proguard.classfile.MethodSignature;
2324

2425
/**
2526
* Default implementation of {@link CfaEdge} for JVM instructions.
@@ -83,4 +84,13 @@ public void setTarget(JvmCfaNode target)
8384
this.target = target;
8485
target.addEnteringEdge(this);
8586
}
87+
88+
/**
89+
* Returns the signature of the target method. This is the current method or, for call edges,
90+
* the target method of the call.
91+
*/
92+
public MethodSignature targetSignature()
93+
{
94+
return target.getSignature();
95+
}
8696
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* ProGuardCORE -- library to process Java bytecode.
3+
*
4+
* Copyright (c) 2002-2022 Guardsquare NV
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package proguard.analysis.cpa.jvm.domain.taint;
20+
21+
import java.util.HashSet;
22+
import java.util.Objects;
23+
import java.util.Set;
24+
import java.util.stream.Collectors;
25+
import proguard.analysis.cpa.interfaces.CallEdge;
26+
import proguard.analysis.cpa.jvm.cfa.edges.JvmCallCfaEdge;
27+
import proguard.analysis.cpa.jvm.cfa.edges.JvmCfaEdge;
28+
import proguard.analysis.cpa.jvm.witness.JvmMemoryLocation;
29+
import proguard.analysis.cpa.jvm.witness.JvmStackLocation;
30+
import proguard.analysis.cpa.jvm.witness.JvmStaticFieldLocation;
31+
import proguard.classfile.util.ClassUtil;
32+
33+
/**
34+
* A {@link JvmTaintSink} on a method invocation.
35+
* This sinks can be sensitive to the instance, the arguments, or the static fields. If a sink S is sensitive to X, then
36+
* if X is tainted, we conclude that the taint has reached S.
37+
*
38+
* @author Dmitry Ivanov
39+
*/
40+
public class JvmInvokeTaintSink
41+
extends JvmTaintSink
42+
{
43+
44+
public final boolean takesInstance;
45+
public final Set<Integer> takesArgs;
46+
public final Set<String> takesGlobals;
47+
48+
/**
49+
* Create a taint sink.
50+
*
51+
* @param fqn the fully qualified name of a sink method
52+
* @param takesInstance whether the sink is sensitive to the calling instance
53+
* @param takesArgs a set of sensitive arguments
54+
* @param takesGlobals a set of sensitive global variables
55+
*/
56+
public JvmInvokeTaintSink(String fqn, boolean takesInstance, Set<Integer> takesArgs, Set<String> takesGlobals)
57+
{
58+
super(fqn);
59+
60+
if (!takesInstance && takesArgs.isEmpty() && takesGlobals.isEmpty())
61+
{
62+
throw new RuntimeException(String.format("Tainted sink for method %s must have taint somewhere!", fqn));
63+
}
64+
65+
this.takesInstance = takesInstance;
66+
this.takesArgs = takesArgs;
67+
this.takesGlobals = takesGlobals;
68+
}
69+
70+
/**
71+
* Returns memory locations which trigger this taint sink.
72+
*/
73+
@Override
74+
public Set<JvmMemoryLocation> getMemoryLocations()
75+
{
76+
Set<JvmMemoryLocation> result = new HashSet<>();
77+
String descriptor = fqn.substring(fqn.indexOf("("));
78+
int parameterSize = ClassUtil.internalMethodParameterSize(descriptor);
79+
if (takesInstance)
80+
{
81+
result.add(new JvmStackLocation(parameterSize));
82+
}
83+
takesArgs.forEach(i -> result.add(new JvmStackLocation(parameterSize - ClassUtil.internalMethodVariableIndex(descriptor, true, i))));
84+
takesGlobals.forEach(fqn -> result.add(new JvmStaticFieldLocation(fqn)));
85+
return result;
86+
}
87+
88+
/**
89+
* Returns true if the edge is a call to the sink method.
90+
*/
91+
@Override
92+
public boolean matchCfaEdge(JvmCfaEdge edge)
93+
{
94+
return edge instanceof JvmCallCfaEdge && fqn.equals(((CallEdge) edge).getCall().getTarget().getFqn());
95+
}
96+
97+
@Override
98+
public boolean equals(Object o)
99+
{
100+
if (this == o)
101+
{
102+
return true;
103+
}
104+
if (!(o instanceof JvmInvokeTaintSink))
105+
{
106+
return false;
107+
}
108+
JvmInvokeTaintSink taintSink = (JvmInvokeTaintSink) o;
109+
return takesInstance == taintSink.takesInstance
110+
&& Objects.equals(fqn, taintSink.fqn)
111+
&& Objects.equals(takesArgs, taintSink.takesArgs)
112+
&& Objects.equals(takesGlobals, taintSink.takesGlobals);
113+
}
114+
115+
@Override
116+
public int hashCode()
117+
{
118+
return Objects.hash(fqn, takesInstance, takesArgs, takesGlobals);
119+
}
120+
121+
@Override
122+
public String toString()
123+
{
124+
StringBuilder result = new StringBuilder("[JvmInvokeTaintSink] ").append(fqn);
125+
if (takesInstance)
126+
{
127+
result.append(", takes instance");
128+
}
129+
if (!takesArgs.isEmpty())
130+
{
131+
result.append(", takes args (")
132+
.append(takesArgs.stream()
133+
.map(Objects::toString)
134+
.sorted()
135+
.collect(Collectors.joining(", ")))
136+
.append(")");
137+
}
138+
if (!takesGlobals.isEmpty())
139+
{
140+
result.append(", takes globals (")
141+
.append(takesGlobals.stream()
142+
.sorted()
143+
.collect(Collectors.joining(", ")))
144+
.append(")");
145+
}
146+
147+
return result.toString();
148+
}
149+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* ProGuardCORE -- library to process Java bytecode.
3+
*
4+
* Copyright (c) 2002-2022 Guardsquare NV
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package proguard.analysis.cpa.jvm.domain.taint;
20+
21+
import java.util.Collections;
22+
import java.util.HashSet;
23+
import java.util.Objects;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
import java.util.stream.Stream;
27+
import proguard.analysis.cpa.jvm.cfa.edges.JvmCfaEdge;
28+
import proguard.analysis.cpa.jvm.cfa.edges.JvmInstructionCfaEdge;
29+
import proguard.analysis.cpa.jvm.witness.JvmMemoryLocation;
30+
import proguard.analysis.cpa.jvm.witness.JvmStackLocation;
31+
32+
/**
33+
* A {@link JvmTaintSink} triggered if the return value of
34+
* the specified method is tainted.
35+
*
36+
* @author Carlo Alberto Pozzoli
37+
*/
38+
public class JvmReturnTaintSink
39+
extends JvmTaintSink
40+
{
41+
42+
public JvmReturnTaintSink(String fqn)
43+
{
44+
super(fqn);
45+
}
46+
47+
// Implementations for JvmTaintSink
48+
49+
/**
50+
* The location of values returned by a method is the top of the stack.
51+
* Since in our convention just the top value is tainted for category 2
52+
* types just the top is enough.
53+
*/
54+
@Override
55+
public Set<JvmMemoryLocation> getMemoryLocations()
56+
{
57+
return Collections.singleton(new JvmStackLocation(0));
58+
}
59+
60+
/**
61+
* Returns true on the return edge of the sink method.
62+
*/
63+
@Override
64+
public boolean matchCfaEdge(JvmCfaEdge edge)
65+
{
66+
return edge instanceof JvmInstructionCfaEdge && edge.getTarget().isReturnExitNode();
67+
}
68+
69+
// Implementations for Object
70+
71+
@Override
72+
public boolean equals(Object o)
73+
{
74+
if (this == o)
75+
{
76+
return true;
77+
}
78+
if (!(o instanceof JvmReturnTaintSink))
79+
{
80+
return false;
81+
}
82+
JvmReturnTaintSink taintSink = (JvmReturnTaintSink) o;
83+
return Objects.equals(fqn, taintSink.fqn);
84+
}
85+
86+
@Override
87+
public int hashCode()
88+
{
89+
return fqn.hashCode();
90+
}
91+
92+
@Override
93+
public String toString()
94+
{
95+
return "[JvmReturnTaintSink] " + fqn;
96+
}
97+
}

0 commit comments

Comments
 (0)