boomerang.results.NullPointerDereference Maven / Gradle / Ivy
package boomerang.results;
import boomerang.Query;
import boomerang.scene.ControlFlowGraph;
import boomerang.scene.Field;
import boomerang.scene.Method;
import boomerang.scene.Pair;
import boomerang.scene.Statement;
import boomerang.scene.Val;
import java.util.List;
import sync.pds.solver.nodes.INode;
import sync.pds.solver.nodes.Node;
import wpds.impl.PAutomaton;
public class NullPointerDereference implements AffectedLocation {
public static final int RULE_INDEX = 0;
private final ControlFlowGraph.Edge statement;
private final Val variable;
private PAutomaton> openingContext;
private PAutomaton> closingContext;
private List dataFlowPath;
private final ControlFlowGraph.Edge sourceStatement;
private final Val sourceVariable;
private Query query;
public NullPointerDereference(ControlFlowGraph.Edge statement) {
this(null, statement, null, null, null, null);
}
public NullPointerDereference(
Query query,
ControlFlowGraph.Edge statement,
Val variable,
PAutomaton> openingContext,
PAutomaton> closingContext,
List dataFlowPath) {
this.query = query;
this.sourceStatement = query.cfgEdge();
this.sourceVariable = query.var();
this.statement = statement;
this.variable = variable;
this.openingContext = openingContext;
this.closingContext = closingContext;
this.dataFlowPath = dataFlowPath;
}
/**
* The variable that contains "null" and which provokes at {@link #getStatement() the statement} a
* NullPointerException.
*
* @return the variable that contains a null pointer
*/
public Val getVariable() {
return variable;
}
@Override
public List getDataFlowPath() {
return dataFlowPath;
}
@Override
public String getMessage() {
return "Potential **null pointer** dereference";
}
@Override
public int getRuleIndex() {
return RULE_INDEX;
}
/**
* The statement at which a null pointer occurred.
*
* A null pointer can occur at three different types of statements: y = x.toString(); or y =
* lengthof(x); or y = x.f;
*
* @return the statement where the respective {@link #getVariable() getVariable} is null
*/
public ControlFlowGraph.Edge getStatement() {
return statement;
}
/**
* The source statement of the data-flow, i.e., the statement that assigns null to a variable.
*
*
Examples are: x = null or x = System.getProperty(...).
*
* @return The source statement of the data-flow/null pointer.
*/
public ControlFlowGraph.Edge getSourceStatement() {
return sourceStatement;
}
/**
* The source variable at the source statement. At a statement x = null or x = System.getProperty,
* this will be the variable x.
*
* @return The source variable of the data-flow propagation
*/
public Val getSourceVariable() {
return sourceVariable;
}
/**
* Returns the method of the statement at which the null pointer occurs.
*
* @return The SootMethod of the null pointer statement
*/
public Method getMethod() {
return getStatement().getStart().getMethod();
}
/**
* The opening context of a NullPointer provides the call stack under which the null pointer
* occurs.
*
*
* main(){
* Object x = null;
* foo(x); //call site context "c1"
* Object y = new Object();
* foo(y); //call site context "c2"
* }
* foo(Object z){
* z.toString() //<- Variable z is null here under context c1, but *not* under c2)
* }
*
*
* In the example above, z is null under the calling context of call site c1.
*
* In the case of branching, there can be multiple call site contexts leading to a null
* pointer. Therefore, the opening context is represented as an automaton (or graph). The edges of
* the automaton are labeled by the call sites, the nodes are labeled by variables or by variables
* at a context. For the example above, the automaton contains a transition with label foo(x)
*
* @return The automaton representation of the opening context.
*/
public PAutomaton> getOpeningContext() {
return openingContext;
}
/**
* The closing context of a NullPointer provides the call stack via which a variable containing
* null returns to a caller.
*
*
* main(){
* Object x;
* if(...){
* x = returnNull(); //b1
* } else {
* x = returnNotNull(); //b2
* }
* x.toString() //<- Variable x is null here when the program executes along branch b1
* }
* Object returnNull(){
* Object y = null;
* return y;
* }
*
*
* In the case above, a null pointer exception occurs when the program executes along branch b1.
*
* There can be multiple contexts leading to a null pointer. Therefore, the closing context is
* represented as an automaton (or graph). The edges of the automaton are labeled by the call
* sites, the nodes are labeled by variables or by variables at a context. For the example above,
* the automaton contains a transition with label returnNull(). This indicates, that the null
* pointer only occurs along branch b1 but not b2.
*
* @return The automaton representation of the closing context.
*/
public PAutomaton> getClosingContext() {
return closingContext;
}
@Override
public String toString() {
String str = "Null Pointer: \n";
str += "defined at " + getSourceStatement().getStart().getMethod();
str += (getVariable() != null ? "\tVariable: " + getVariable() : "");
str += "\n\tStatement: " + getStatement() + "\n\tMethod: " + getMethod();
return str;
}
public Query getQuery() {
return query;
}
public static boolean isNullPointerNode(Node nullPointerNode) {
Val fact = nullPointerNode.fact();
Method m = fact.m();
// A this variable can never be null.
if (!m.isStatic() && m.getThisLocal().equals(fact)) {
return false;
}
Statement curr = nullPointerNode.stmt().getStart();
if (curr.containsInvokeExpr()) {
if (curr.getInvokeExpr().isInstanceInvokeExpr()) {
Val invocationBase = curr.getInvokeExpr().getBase();
if (invocationBase.equals(fact)) {
return true;
}
}
}
if (curr.isAssign()) {
if (curr.isFieldLoad()) {
Pair ifr = curr.getFieldLoad();
if (ifr.getX().equals(fact)) {
return true;
}
}
if (curr.getRightOp().isLengthExpr()) {
Val lengthOp = curr.getRightOp().getLengthOp();
if (lengthOp.equals(fact)) {
return true;
}
}
}
return false;
}
}