/* SAAF: A static analyzer for APK files.
* Copyright (C) 2013 syssec.rub.de
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.rub.syssec.saaf.analysis.steps.slicing;
import java.util.HashSet;
import java.util.LinkedList;
import de.rub.syssec.saaf.model.application.BasicBlockInterface;
public class BBList {
private LinkedList<BasicBlockInterface> path = new LinkedList<BasicBlockInterface>();
/**
* Holds information about the current path, eg, what register is tracked where in the path
*/
private LinkedList<byte[]> pathState = new LinkedList<byte[]>();
private LinkedList<BasicBlockInterface> previousPath = null;
private HashSet<BasicBlockInterface> visited = new HashSet<BasicBlockInterface>();
private boolean firstTime = true;
private final boolean isBackwardSearch;
/**
* Create a new list of BBs which automatically handles the path for each BB which is found by a depth-first search.
*
* @param firstBB the first BB of the new path, it will be returned on the first call of getNextBB()
* @param pathState Can be used to denote the tracked register for the current BB, it can be overridden if it changes while handling new BBs
* @param isBackwardSearch are BBs searched forward (next) of backwards (previous)
*/
public BBList(BasicBlockInterface firstBB, byte[] pathState, boolean isBackwardSearch) {
path.add(firstBB);
this.pathState.add(pathState);
this.isBackwardSearch = isBackwardSearch;
}
/**
* Create a new list of BBs which automatically handles the path for each BB which is found by a depth-first search. This method will
* store the previous path and will search for other BBs from the last BB in the path.
*
* @param previousPath the actual BB is the last BB in the given path, only the last BB will be returned on the first call to getNextBB().
* @param pathState Can be used to denote the tracked register for the current BB, it can be overridden if it changes while handling new BBs
* @param isBackwardSearch are BBs searched forward (next) or backwards (previous)
*/
public BBList(LinkedList<BasicBlockInterface> previousPath, byte[] pathState, boolean isBackwardSearch) {
this.previousPath = previousPath;
this.isBackwardSearch = isBackwardSearch;
// the last one is the starting BB
BasicBlockInterface last = this.previousPath.removeLast();
path.add(last);
this.pathState.add(pathState);
/*
* Avoid loops.
* This next command is not ok as the previous path might be a->b->c and from c it
* goes back to b where something of interest can be found:
* if (isBackwardSearch) visited.addAll(this.previousPath);
* But we have to add 'last' to the visited list as a BB might reference itself.
*/
visited.add(last);
}
/**
* Create a new list of BBs which automatically handles the path for each BB which is found by a depth-first search.
* The search is conducted backwards.
*
* @param firstBB the first BB of the new path, it will be returned on the first call of getNextBB()
* @param pathState Can be used to denote the tracked register for the current BB, it can be overridden if it changes while handling new BBs
*/
public BBList(BasicBlockInterface firstBB, byte[] pathState) {
this(firstBB, pathState, true);
}
/**
* Create a new list of BBs which automatically handles the path for each BB which is found by a depth-first search. This method will
* store the previous path and will search for other BBs from the last BB in the path. The search is conducted backwards.
*
* @param previousPath the actual BB is the last BB in the given path, only the last BB will be returned on the first call to getNextBB().
* @param pathState Can be used to denote the tracked register for the current BB, it can be overridden if it changes while handling new BBs
*/
public BBList(LinkedList<BasicBlockInterface> previousPath, byte[] pathState) {
this(previousPath, pathState, true);
}
/**
* Returns BBs which are found during the DFS until no new BBs can be found. The first BB will always be
* returned on the first call, see constructor. The path is automatically stored and the current one can be
* retrieved with the getPathForLastBB() method.
*
* @return the next BB according to the DFS, no BB will be returned twice
*/
public BasicBlockInterface getNextBb() {
// return the first BB which was supplied in the constructor for the first call
if (firstTime) {
firstTime = false;
return path.getFirst(); // there is only one
}
BasicBlockInterface current;
BasicBlockInterface retBB = null; // the bb to be returned
while (!path.isEmpty() && retBB == null) { // search until retBB is found or no more BBs are available
current = path.getLast();
LinkedList<BasicBlockInterface> blocks;
if (isBackwardSearch) blocks = current.getPreviousBB();
else blocks = current.getNextBB();
for (BasicBlockInterface bb : blocks) { // search next successor
if (!visited.contains(bb)) {
retBB = bb; // this is the next in the DFS and will be returned
visited.add(bb);
path.add(bb); // add the current BB to the path
pathState.add(pathState.getLast()); // save the state
break;
}
}
if (retBB == null) { // no successor found, go backwards in path
// remove the last and search for a new one in the last BB
removeLastBBFromList();
}
}
return retBB;
}
/**
* Remove the last BB from the current path and search for the next BB according to the DFS.
*/
public void removeLastBBFromList() {
if (!path.isEmpty()) {
path.removeLast();
pathState.removeLast();
}
}
/**
* Add a new state to the current BB in the path, it will be propagated to the next (previous) BBs in the path.
* @param state
*/
public void setNewStateforCurrentBB(byte[] state) {
pathState.pop();
pathState.addLast(state);
}
/**
* Get the saved state for the current BB.
*/
public byte[] getState() {
return pathState.getLast();
}
/**
* Get the path, which is always a new object.
* @return the path
*/
public LinkedList<BasicBlockInterface> getPathForLastBB() {
LinkedList<BasicBlockInterface> ret = new LinkedList<BasicBlockInterface>();
if (previousPath != null) ret.addAll(previousPath);
if (!firstTime) { // as long as getNextBB() is not called, there is no path, but the initial BB is already added
ret.addAll(path);
}
return ret;
}
/**
* Prints the path in a semi readable manner :)
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PATH: ");
for (BasicBlockInterface bb : path) {
sb.append(bb.getLabel());
sb.append("\t");
}
sb.append("\nSTATE: ");
for (byte[] bb : pathState) {
sb.append(new String(bb));
sb.append("\t");
}
sb.append("\nPREV PATH: ");
for (BasicBlockInterface bb : previousPath) {
sb.append(bb.getLabel());
sb.append("\t");
}
sb.append("\nisBackwards: ");
sb.append(isBackwardSearch);
sb.append("\npathState: ");
return sb.toString();
}
}