Java tutorial
/******************************************************************************* * Copyright (c) 2012 Secure Software Engineering Group at EC SPRIDE. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: Christian Fritz, Steven Arzt, Siegfried Rasthofer, Eric * Bodden, and others. ******************************************************************************/ package soot.jimple.infoflow.data; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import com.google.common.collect.Sets; import heros.solver.LinkedNode; import soot.NullType; import soot.SootMethod; import soot.Type; import soot.Unit; import soot.Value; import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowConfiguration; import soot.jimple.infoflow.collect.AtomicBitSet; import soot.jimple.infoflow.data.AccessPath.ArrayTaintType; import soot.jimple.infoflow.solver.cfg.IInfoflowCFG.UnitContainer; import soot.jimple.infoflow.solver.fastSolver.FastSolverLinkedNode; import soot.jimple.internal.JimpleLocal; /** * The abstraction class contains all information that is necessary to track the taint. * * @author Steven Arzt * @author Christian Fritz */ public class Abstraction implements Cloneable, FastSolverLinkedNode<Abstraction, Unit>, LinkedNode<Abstraction> { private static boolean flowSensitiveAliasing = true; /** * the access path contains the currently tainted variable or field */ private AccessPath accessPath; private Abstraction predecessor = null; private Set<Abstraction> neighbors = null; private Stmt currentStmt = null; private Stmt correspondingCallSite = null; private SourceContext sourceContext = null; /** * Unit/Stmt which activates the taint when the abstraction passes it */ private Unit activationUnit = null; /** * taint is thrown by an exception (is set to false when it reaches the catch-Stmt) */ private boolean exceptionThrown = false; private int hashCode = 0; /** * The postdominators we need to pass in order to leave the current conditional * branch. Do not use the synchronized Stack class here to avoid deadlocks. */ private List<UnitContainer> postdominators = null; private boolean isImplicit = false; /** * Only valid for inactive abstractions. Specifies whether an access paths * has been cut during alias analysis. */ private boolean dependsOnCutAP = false; private AtomicBitSet pathFlags = null; public Abstraction(AccessPath sourceVal, Stmt sourceStmt, Object userData, boolean exceptionThrown, boolean isImplicit) { this(sourceVal, new SourceContext(sourceVal, sourceStmt, userData), exceptionThrown, isImplicit); } protected Abstraction(AccessPath apToTaint, SourceContext sourceContext, boolean exceptionThrown, boolean isImplicit) { this.sourceContext = sourceContext; this.accessPath = apToTaint; this.activationUnit = null; this.exceptionThrown = exceptionThrown; this.neighbors = null; this.isImplicit = isImplicit; this.currentStmt = sourceContext == null ? null : sourceContext.getStmt(); } /** * Creates an abstraction as a copy of an existing abstraction, * only exchanging the access path. -> only used by AbstractionWithPath * @param p The access path for the new abstraction * @param original The original abstraction to copy */ protected Abstraction(AccessPath p, Abstraction original) { if (original == null) { sourceContext = null; exceptionThrown = false; activationUnit = null; isImplicit = false; } else { sourceContext = original.sourceContext; exceptionThrown = original.exceptionThrown; activationUnit = original.activationUnit; assert activationUnit == null || flowSensitiveAliasing; postdominators = original.postdominators == null ? null : new ArrayList<UnitContainer>(original.postdominators); dependsOnCutAP = original.dependsOnCutAP; isImplicit = original.isImplicit; } accessPath = p; neighbors = null; currentStmt = null; } public final Abstraction deriveInactiveAbstraction(Unit activationUnit) { if (!flowSensitiveAliasing) { assert this.isAbstractionActive(); return this; } // If this abstraction is already inactive, we keep it if (!this.isAbstractionActive()) return this; Abstraction a = deriveNewAbstractionMutable(accessPath, null); if (a == null) return null; a.postdominators = null; a.activationUnit = activationUnit; a.dependsOnCutAP |= a.getAccessPath().isCutOffApproximation(); return a; } public Abstraction deriveNewAbstraction(AccessPath p, Stmt currentStmt) { return deriveNewAbstraction(p, currentStmt, isImplicit); } public Abstraction deriveNewAbstraction(AccessPath p, Stmt currentStmt, boolean isImplicit) { // If the new abstraction looks exactly like the current one, there is // no need to create a new object if (this.accessPath.equals(p) && this.currentStmt == currentStmt && this.isImplicit == isImplicit) return this; Abstraction abs = deriveNewAbstractionMutable(p, currentStmt); if (abs == null) return null; abs.isImplicit = isImplicit; return abs; } private Abstraction deriveNewAbstractionMutable(AccessPath p, Stmt currentStmt) { // An abstraction needs an access path if (p == null) return null; if (this.accessPath.equals(p) && this.currentStmt == currentStmt) { Abstraction abs = clone(); abs.currentStmt = currentStmt; return abs; } Abstraction abs = new Abstraction(p, this); abs.predecessor = this; abs.currentStmt = currentStmt; if (!abs.getAccessPath().isEmpty()) abs.postdominators = null; if (!abs.isAbstractionActive()) abs.dependsOnCutAP = abs.dependsOnCutAP || p.isCutOffApproximation(); abs.sourceContext = null; return abs; } public final Abstraction deriveNewAbstraction(Value taint, boolean cutFirstField, Stmt currentStmt, Type baseType) { return deriveNewAbstraction(taint, cutFirstField, currentStmt, baseType, getAccessPath().getArrayTaintType()); } public final Abstraction deriveNewAbstraction(Value taint, boolean cutFirstField, Stmt currentStmt, Type baseType, ArrayTaintType arrayTaintType) { assert !this.getAccessPath().isEmpty(); AccessPath newAP = accessPath.copyWithNewValue(taint, baseType, cutFirstField, true, arrayTaintType); if (this.getAccessPath().equals(newAP) && this.currentStmt == currentStmt) return this; return deriveNewAbstractionMutable(newAP, currentStmt); } /** * Derives a new abstraction that models the current local being thrown as * an exception * @param throwStmt The statement at which the exception was thrown * @return The newly derived abstraction */ public final Abstraction deriveNewAbstractionOnThrow(Stmt throwStmt) { assert !this.exceptionThrown; Abstraction abs = clone(); abs.currentStmt = throwStmt; abs.sourceContext = null; abs.exceptionThrown = true; return abs; } /** * Derives a new abstraction that models the current local being caught as * an exception * @param taint The value in which the tainted exception is stored * @return The newly derived abstraction */ public final Abstraction deriveNewAbstractionOnCatch(Value taint) { assert this.exceptionThrown; Abstraction abs = deriveNewAbstractionMutable(AccessPathFactory.v().createAccessPath(taint, true), null); if (abs == null) return null; abs.exceptionThrown = false; return abs; } public boolean isAbstractionActive() { return activationUnit == null; } public boolean isImplicit() { return isImplicit; } @Override public String toString() { return (isAbstractionActive() ? "" : "_") + accessPath.toString() + " | " + (activationUnit == null ? "" : activationUnit.toString()) + ">>"; } public AccessPath getAccessPath() { return accessPath; } public Unit getActivationUnit() { return this.activationUnit; } public Abstraction getActiveCopy() { assert !this.isAbstractionActive(); Abstraction a = clone(); a.sourceContext = null; a.activationUnit = null; return a; } /** * Gets whether this value has been thrown as an exception * @return True if this value has been thrown as an exception, otherwise * false */ public boolean getExceptionThrown() { return this.exceptionThrown; } public final Abstraction deriveConditionalAbstractionEnter(UnitContainer postdom, Stmt conditionalUnit) { assert this.isAbstractionActive(); if (postdominators != null && postdominators.contains(postdom)) return this; Abstraction abs = deriveNewAbstractionMutable(AccessPath.getEmptyAccessPath(), conditionalUnit); if (abs == null) return null; if (abs.postdominators == null) abs.postdominators = Collections.singletonList(postdom); else abs.postdominators.add(0, postdom); return abs; } public final Abstraction deriveConditionalAbstractionCall(Unit conditionalCallSite) { assert this.isAbstractionActive(); assert conditionalCallSite != null; Abstraction abs = deriveNewAbstractionMutable(AccessPath.getEmptyAccessPath(), (Stmt) conditionalCallSite); if (abs == null) return null; // Postdominators are only kept intraprocedurally in order to not // mess up the summary functions with caller-side information abs.postdominators = null; return abs; } public final Abstraction dropTopPostdominator() { if (postdominators == null || postdominators.isEmpty()) return this; Abstraction abs = clone(); abs.sourceContext = null; abs.postdominators.remove(0); return abs; } public UnitContainer getTopPostdominator() { if (postdominators == null || postdominators.isEmpty()) return null; return this.postdominators.get(0); } public boolean isTopPostdominator(Unit u) { UnitContainer uc = getTopPostdominator(); if (uc == null) return false; return uc.getUnit() == u; } public boolean isTopPostdominator(SootMethod sm) { UnitContainer uc = getTopPostdominator(); if (uc == null) return false; return uc.getMethod() == sm; } @Override public Abstraction clone() { Abstraction abs = new Abstraction(accessPath, this); abs.predecessor = this; abs.neighbors = null; abs.currentStmt = null; abs.correspondingCallSite = null; assert abs.equals(this); return abs; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Abstraction other = (Abstraction) obj; // If we have already computed hash codes, we can use them for // comparison if (this.hashCode != 0 && other.hashCode != 0 && this.hashCode != other.hashCode) return false; if (accessPath == null) { if (other.accessPath != null) return false; } else if (!accessPath.equals(other.accessPath)) return false; return localEquals(other); } /** * Checks whether this object locally equals the given object, i.e. the both * are equal modulo the access path * @param other The object to compare this object with * @return True if this object is locally equal to the given one, otherwise * false */ private boolean localEquals(Abstraction other) { // deliberately ignore prevAbs if (sourceContext == null) { if (other.sourceContext != null) return false; } else if (!sourceContext.equals(other.sourceContext)) return false; if (activationUnit == null) { if (other.activationUnit != null) return false; } else if (!activationUnit.equals(other.activationUnit)) return false; if (this.exceptionThrown != other.exceptionThrown) return false; if (postdominators == null) { if (other.postdominators != null) return false; } else if (!postdominators.equals(other.postdominators)) return false; if (this.dependsOnCutAP != other.dependsOnCutAP) return false; if (this.isImplicit != other.isImplicit) return false; return true; } @Override public int hashCode() { if (this.hashCode != 0) return hashCode; final int prime = 31; int result = 1; // deliberately ignore prevAbs result = prime * result + ((sourceContext == null) ? 0 : sourceContext.hashCode()); result = prime * result + ((accessPath == null) ? 0 : accessPath.hashCode()); result = prime * result + ((activationUnit == null) ? 0 : activationUnit.hashCode()); result = prime * result + (exceptionThrown ? 1231 : 1237); result = prime * result + ((postdominators == null) ? 0 : postdominators.hashCode()); result = prime * result + (dependsOnCutAP ? 1231 : 1237); result = prime * result + (isImplicit ? 1231 : 1237); this.hashCode = result; return this.hashCode; } /** * Checks whether this abstraction entails the given abstraction, i.e. this * taint also taints everything that is tainted by the given taint. * @param other The other taint abstraction * @return True if this object at least taints everything that is also tainted * by the given object */ public boolean entails(Abstraction other) { if (accessPath == null) { if (other.accessPath != null) return false; } else if (!accessPath.entails(other.accessPath)) return false; return localEquals(other); } /** * Gets the context of the taint, i.e. the statement and value of the source * @return The statement and value of the source */ public SourceContext getSourceContext() { return sourceContext; } public boolean dependsOnCutAP() { return dependsOnCutAP; } @Override public Abstraction getPredecessor() { return this.predecessor; } public Set<Abstraction> getNeighbors() { return this.neighbors; } public Stmt getCurrentStmt() { return this.currentStmt; } @Override public void addNeighbor(Abstraction originalAbstraction) { // We should not register ourselves as a neighbor if (originalAbstraction == this) return; // We should not add identical nodes as neighbors if (this.predecessor == originalAbstraction.predecessor && this.currentStmt == originalAbstraction.currentStmt && this.predecessor == originalAbstraction.predecessor) return; synchronized (this) { if (neighbors == null) neighbors = Sets.newIdentityHashSet(); else if (InfoflowConfiguration.getMergeNeighbors()) { // Check if we already have an identical neighbor for (Abstraction nb : neighbors) { if (nb == originalAbstraction) return; if (originalAbstraction.predecessor == nb.predecessor && originalAbstraction.currentStmt == nb.currentStmt && originalAbstraction.correspondingCallSite == nb.correspondingCallSite) { return; } } } this.neighbors.add(originalAbstraction); } } public void setCorrespondingCallSite(Stmt callSite) { this.correspondingCallSite = callSite; } public Stmt getCorrespondingCallSite() { return this.correspondingCallSite; } public static Abstraction getZeroAbstraction(boolean flowSensitiveAliasing) { Abstraction zeroValue = new Abstraction( AccessPathFactory.v().createAccessPath(new JimpleLocal("zero", NullType.v()), false), null, false, false); Abstraction.flowSensitiveAliasing = flowSensitiveAliasing; return zeroValue; } @Override public void setPredecessor(Abstraction predecessor) { this.predecessor = predecessor; assert this.predecessor != this; } /** * Only use this method if you really need to fake a source context and know * what you are doing. * @param sourceContext The new source context */ public void setSourceContext(SourceContext sourceContext) { this.sourceContext = sourceContext; } /** * Registers that a worker thread with the given ID has already processed * this abstraction * @param id The ID of the worker thread * @return True if the worker thread with the given ID has not been * registered before, otherwise false */ public boolean registerPathFlag(int id, int maxSize) { if (pathFlags == null) { synchronized (this) { if (pathFlags == null) { // Make sure that the field is set only after the constructor // is done and the object is fully usable AtomicBitSet pf = new AtomicBitSet(maxSize); pathFlags = pf; } } } return pathFlags.set(id); } public Abstraction injectSourceContext(SourceContext sourceContext) { if (this.sourceContext != null && this.sourceContext.equals(sourceContext)) return this; Abstraction abs = clone(); abs.predecessor = null; abs.neighbors = null; abs.sourceContext = sourceContext; abs.currentStmt = this.currentStmt; return abs; } /** * For internal use by memory manager only. Setting a new access path will * not update any dependent data such as cached hashes. Handle with care! */ void setAccessPath(AccessPath accessPath) { this.accessPath = accessPath; } void setCurrentStmt(Stmt currentStmt) { this.currentStmt = currentStmt; } @Override public void setCallingContext(Abstraction callingContext) { } }