All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.infoflow.Infoflow Maven / Gradle / Ivy

/*******************************************************************************
 * 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;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.G;
import soot.MethodOrMethodContext;
import soot.PackManager;
import soot.PatchingChain;
import soot.PointsToAnalysis;
import soot.Scene;
import soot.SootMethod;
import soot.Unit;
import soot.jimple.Stmt;
import soot.jimple.infoflow.InfoflowConfiguration.AccessPathConfiguration;
import soot.jimple.infoflow.InfoflowConfiguration.CallgraphAlgorithm;
import soot.jimple.infoflow.InfoflowConfiguration.CodeEliminationMode;
import soot.jimple.infoflow.InfoflowConfiguration.DataFlowSolver;
import soot.jimple.infoflow.InfoflowConfiguration.PathConfiguration;
import soot.jimple.infoflow.InfoflowConfiguration.SolverConfiguration;
import soot.jimple.infoflow.InfoflowConfiguration.SootIntegrationMode;
import soot.jimple.infoflow.InfoflowConfiguration.StaticFieldTrackingMode;
import soot.jimple.infoflow.aliasing.Aliasing;
import soot.jimple.infoflow.aliasing.FlowSensitiveAliasStrategy;
import soot.jimple.infoflow.aliasing.IAliasingStrategy;
import soot.jimple.infoflow.aliasing.LazyAliasingStrategy;
import soot.jimple.infoflow.aliasing.NullAliasStrategy;
import soot.jimple.infoflow.aliasing.PtsBasedAliasStrategy;
import soot.jimple.infoflow.cfg.BiDirICFGFactory;
import soot.jimple.infoflow.codeOptimization.DeadCodeEliminator;
import soot.jimple.infoflow.codeOptimization.ICodeOptimizer;
import soot.jimple.infoflow.data.Abstraction;
import soot.jimple.infoflow.data.AbstractionAtSink;
import soot.jimple.infoflow.data.AccessPathFactory;
import soot.jimple.infoflow.data.FlowDroidMemoryManager.PathDataErasureMode;
import soot.jimple.infoflow.data.pathBuilders.DefaultPathBuilderFactory;
import soot.jimple.infoflow.data.pathBuilders.IAbstractionPathBuilder;
import soot.jimple.infoflow.data.pathBuilders.IAbstractionPathBuilder.OnPathBuilderResultAvailable;
import soot.jimple.infoflow.entryPointCreators.IEntryPointCreator;
import soot.jimple.infoflow.globalTaints.GlobalTaintManager;
import soot.jimple.infoflow.handlers.PostAnalysisHandler;
import soot.jimple.infoflow.handlers.ResultsAvailableHandler;
import soot.jimple.infoflow.handlers.ResultsAvailableHandler2;
import soot.jimple.infoflow.handlers.TaintPropagationHandler;
import soot.jimple.infoflow.memory.FlowDroidMemoryWatcher;
import soot.jimple.infoflow.memory.FlowDroidTimeoutWatcher;
import soot.jimple.infoflow.memory.IMemoryBoundedSolver;
import soot.jimple.infoflow.memory.ISolverTerminationReason;
import soot.jimple.infoflow.memory.reasons.AbortRequestedReason;
import soot.jimple.infoflow.memory.reasons.OutOfMemoryReason;
import soot.jimple.infoflow.memory.reasons.TimeoutReason;
import soot.jimple.infoflow.problems.AbstractInfoflowProblem;
import soot.jimple.infoflow.problems.BackwardsInfoflowProblem;
import soot.jimple.infoflow.problems.InfoflowProblem;
import soot.jimple.infoflow.problems.TaintPropagationResults;
import soot.jimple.infoflow.problems.TaintPropagationResults.OnTaintPropagationResultAdded;
import soot.jimple.infoflow.problems.rules.DefaultPropagationRuleManagerFactory;
import soot.jimple.infoflow.problems.rules.IPropagationRuleManagerFactory;
import soot.jimple.infoflow.results.InfoflowPerformanceData;
import soot.jimple.infoflow.results.InfoflowResults;
import soot.jimple.infoflow.results.ResultSinkInfo;
import soot.jimple.infoflow.results.ResultSourceInfo;
import soot.jimple.infoflow.solver.IInfoflowSolver;
import soot.jimple.infoflow.solver.PredecessorShorteningMode;
import soot.jimple.infoflow.solver.SolverPeerGroup;
import soot.jimple.infoflow.solver.cfg.BackwardsInfoflowCFG;
import soot.jimple.infoflow.solver.cfg.IInfoflowCFG;
import soot.jimple.infoflow.solver.executors.InterruptableExecutor;
import soot.jimple.infoflow.solver.gcSolver.GCSolverPeerGroup;
import soot.jimple.infoflow.solver.memory.DefaultMemoryManagerFactory;
import soot.jimple.infoflow.solver.memory.IMemoryManager;
import soot.jimple.infoflow.solver.memory.IMemoryManagerFactory;
import soot.jimple.infoflow.sourcesSinks.manager.IOneSourceAtATimeManager;
import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager;
import soot.jimple.infoflow.threading.DefaultExecutorFactory;
import soot.jimple.infoflow.threading.IExecutorFactory;
import soot.jimple.infoflow.util.SootMethodRepresentationParser;
import soot.jimple.infoflow.util.SystemClassHandler;
import soot.jimple.toolkits.callgraph.ReachableMethods;
import soot.jimple.toolkits.pointer.DumbPointerAnalysis;
import soot.options.Options;

/**
 * main infoflow class which triggers the analysis and offers method to
 * customize it.
 *
 */
public class Infoflow extends AbstractInfoflow {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	protected InfoflowResults results = null;
	protected InfoflowManager manager;

	protected Set onResultsAvailable = new HashSet<>();
	protected TaintPropagationHandler taintPropagationHandler = null;
	protected TaintPropagationHandler backwardsPropagationHandler = null;

	protected IMemoryManagerFactory memoryManagerFactory = new DefaultMemoryManagerFactory();
	protected IExecutorFactory executorFactory = new DefaultExecutorFactory();
	protected IPropagationRuleManagerFactory ruleManagerFactory = new DefaultPropagationRuleManagerFactory();

	protected FlowDroidMemoryWatcher memoryWatcher = null;

	protected Set collectedSources = null;
	protected Set collectedSinks = null;

	protected SootMethod dummyMainMethod = null;
	protected Collection additionalEntryPointMethods = null;

	private boolean throwExceptions;

	protected SolverPeerGroup solverPeerGroup;

	/**
	 * Creates a new instance of the InfoFlow class for analyzing plain Java code
	 * without any references to APKs or the Android SDK.
	 */
	public Infoflow() {
		super();
	}

	/**
	 * Creates a new instance of the Infoflow class for analyzing Android APK files.
	 * 
	 * @param androidPath     If forceAndroidJar is false, this is the base
	 *                        directory of the platform files in the Android SDK. If
	 *                        forceAndroidJar is true, this is the full path of a
	 *                        single android.jar file.
	 * @param forceAndroidJar True if a single platform JAR file shall be forced,
	 *                        false if Soot shall pick the appropriate platform
	 *                        version
	 */
	public Infoflow(String androidPath, boolean forceAndroidJar) {
		super(null, androidPath, forceAndroidJar);
	}

	/**
	 * Creates a new instance of the Infoflow class for analyzing Android APK files.
	 * 
	 * @param androidPath     If forceAndroidJar is false, this is the base
	 *                        directory of the platform files in the Android SDK. If
	 *                        forceAndroidJar is true, this is the full path of a
	 *                        single android.jar file.
	 * @param forceAndroidJar True if a single platform JAR file shall be forced,
	 *                        false if Soot shall pick the appropriate platform
	 *                        version
	 * @param icfgFactory     The interprocedural CFG to be used by the
	 *                        InfoFlowProblem
	 */
	public Infoflow(String androidPath, boolean forceAndroidJar, BiDirICFGFactory icfgFactory) {
		super(icfgFactory, androidPath, forceAndroidJar);
	}

	@Override
	public void computeInfoflow(String appPath, String libPath, IEntryPointCreator entryPointCreator,
			ISourceSinkManager sourcesSinks) {
		if (sourcesSinks == null) {
			logger.error("Sources are empty!");
			return;
		}

		if (config.getSootIntegrationMode() != SootIntegrationMode.UseExistingInstance)
			initializeSoot(appPath, libPath, entryPointCreator.getRequiredClasses());

		// entryPoints are the entryPoints required by Soot to calculate Graph -
		// if there is no main method, we have to create a new main method and
		// use it as entryPoint and store our real entryPoints
		this.dummyMainMethod = entryPointCreator.createDummyMain();
		this.additionalEntryPointMethods = entryPointCreator.getAdditionalMethods();
		Scene.v().setEntryPoints(Collections.singletonList(dummyMainMethod));

		// Run the analysis
		runAnalysis(sourcesSinks, null);
	}

	@Override
	public void computeInfoflow(String appPath, String libPath, String entryPoint, ISourceSinkManager sourcesSinks) {
		if (sourcesSinks == null) {
			logger.error("Sources are empty!");
			return;
		}

		initializeSoot(appPath, libPath, SootMethodRepresentationParser.v()
				.parseClassNames(Collections.singletonList(entryPoint), false).keySet(), entryPoint);

		if (!Scene.v().containsMethod(entryPoint)) {
			logger.error("Entry point not found: " + entryPoint);
			return;
		}
		SootMethod ep = Scene.v().getMethod(entryPoint);
		if (ep.isConcrete())
			ep.retrieveActiveBody();
		else {
			logger.debug("Skipping non-concrete method " + ep);
			return;
		}
		this.dummyMainMethod = null;
		Scene.v().setEntryPoints(Collections.singletonList(ep));
		Options.v().set_main_class(ep.getDeclaringClass().getName());

		// Compute the additional seeds if they are specified
		Set seeds = Collections.emptySet();
		if (entryPoint != null && !entryPoint.isEmpty())
			seeds = Collections.singleton(entryPoint);
		ipcManager.updateJimpleForICC();

		// Run the analysis
		runAnalysis(sourcesSinks, seeds);
	}

	/**
	 * Releases the callgraph and all intermediate objects associated with it
	 */
	private void releaseCallgraph() {
		Scene.v().releaseCallGraph();
		Scene.v().releasePointsToAnalysis();
		Scene.v().releaseReachableMethods();
		G.v().resetSpark();
	}

	/**
	 * Conducts a taint analysis on an already initialized callgraph
	 * 
	 * @param sourcesSinks The sources and sinks to be used
	 */
	protected void runAnalysis(final ISourceSinkManager sourcesSinks) {
		runAnalysis(sourcesSinks, null);
	}

	/**
	 * Conducts a taint analysis on an already initialized callgraph
	 * 
	 * @param sourcesSinks    The sources and sinks to be used
	 * @param additionalSeeds Additional seeds at which to create A ZERO fact even
	 *                        if they are not sources
	 */
	private void runAnalysis(final ISourceSinkManager sourcesSinks, final Set additionalSeeds) {
		final InfoflowPerformanceData performanceData = createPerformanceDataClass();
		try {
			// Clear the data from previous runs
			results = new InfoflowResults();
			results.setPerformanceData(performanceData);

			// Print and check our configuration
			checkAndFixConfiguration();
			config.printSummary();

			// Register a memory watcher
			if (memoryWatcher != null) {
				memoryWatcher.clearSolvers();
				memoryWatcher = null;
			}
			memoryWatcher = new FlowDroidMemoryWatcher(results, config.getMemoryThreshold());

			// Initialize the abstraction configuration
			Abstraction.initialize(config);

			// Build the callgraph
			long beforeCallgraph = System.nanoTime();
			constructCallgraph();
			performanceData
					.setCallgraphConstructionSeconds((int) Math.round((System.nanoTime() - beforeCallgraph) / 1E9));
			logger.info(String.format("Callgraph construction took %d seconds",
					performanceData.getCallgraphConstructionSeconds()));

			// Initialize the source sink manager
			if (sourcesSinks != null)
				sourcesSinks.initialize();

			// Perform constant propagation and remove dead code
			if (config.getCodeEliminationMode() != CodeEliminationMode.NoCodeElimination) {
				long currentMillis = System.nanoTime();
				eliminateDeadCode(sourcesSinks);
				logger.info("Dead code elimination took " + (System.nanoTime() - currentMillis) / 1E9 + " seconds");
			}

			// After constant value propagation, we might find more call edges
			// for reflective method calls
			if (config.getEnableReflection()) {
				releaseCallgraph();
				constructCallgraph();
			}

			if (config.getCallgraphAlgorithm() != CallgraphAlgorithm.OnDemand)
				logger.info("Callgraph has {} edges", Scene.v().getCallGraph().size());

			IInfoflowCFG iCfg = icfgFactory.buildBiDirICFG(config.getCallgraphAlgorithm(),
					config.getEnableExceptionTracking());

			if (config.isTaintAnalysisEnabled())
				runTaintAnalysis(sourcesSinks, additionalSeeds, iCfg, performanceData);

			// Gather performance data
			performanceData.setTotalRuntimeSeconds((int) Math.round((System.nanoTime() - beforeCallgraph) / 1E9));
			performanceData.updateMaxMemoryConsumption(getUsedMemory());
			logger.info(String.format("Data flow solver took %d seconds. Maximum memory consumption: %d MB",
					performanceData.getTotalRuntimeSeconds(), performanceData.getMaxMemoryConsumption()));

			// Provide the handler with the final results
			for (ResultsAvailableHandler handler : onResultsAvailable)
				handler.onResultsAvailable(iCfg, results);

			// Write the Jimple files to disk if requested
			if (config.getWriteOutputFiles())
				PackManager.v().writeOutput();
		} catch (Exception ex) {
			StringWriter stacktrace = new StringWriter();
			PrintWriter pw = new PrintWriter(stacktrace);
			ex.printStackTrace(pw);
			results.addException(ex.getClass().getName() + ": " + ex.getMessage() + "\n" + stacktrace.toString());
			logger.error("Exception during data flow analysis", ex);
			if (throwExceptions)
				throw ex;
		}
	}

	private void runTaintAnalysis(final ISourceSinkManager sourcesSinks, final Set additionalSeeds,
			IInfoflowCFG iCfg, InfoflowPerformanceData performanceData) {
		logger.info("Starting Taint Analysis");

		// Make sure that we have a path builder factory
		if (pathBuilderFactory == null)
			pathBuilderFactory = new DefaultPathBuilderFactory(config.getPathConfiguration());

		// Check whether we need to run with one source at a time
		IOneSourceAtATimeManager oneSourceAtATime = config.getOneSourceAtATime() && sourcesSinks != null
				&& sourcesSinks instanceof IOneSourceAtATimeManager ? (IOneSourceAtATimeManager) sourcesSinks : null;

		// Reset the current source
		if (oneSourceAtATime != null)
			oneSourceAtATime.resetCurrentSource();
		boolean hasMoreSources = oneSourceAtATime == null || oneSourceAtATime.hasNextSource();

		while (hasMoreSources) {
			// Fetch the next source
			if (oneSourceAtATime != null)
				oneSourceAtATime.nextSource();

			// Create the executor that takes care of the workers
			int numThreads = Runtime.getRuntime().availableProcessors();
			InterruptableExecutor executor = executorFactory.createExecutor(numThreads, true, config);
			executor.setThreadFactory(new ThreadFactory() {

				@Override
				public Thread newThread(Runnable r) {
					Thread thrIFDS = new Thread(r);
					thrIFDS.setDaemon(true);
					thrIFDS.setName("FlowDroid");
					return thrIFDS;
				}

			});

			// Initialize the memory manager
			IMemoryManager memoryManager = createMemoryManager();

			// Initialize our infrastructure for global taints
			final Set solvers = new HashSet<>();
			GlobalTaintManager globalTaintManager = new GlobalTaintManager(solvers);

			// Initialize the data flow manager
			manager = initializeInfoflowManager(sourcesSinks, iCfg, globalTaintManager);

			// Create the solver peer group
			solverPeerGroup = new GCSolverPeerGroup();

			// Initialize the alias analysis
			Abstraction zeroValue = null;
			IAliasingStrategy aliasingStrategy = createAliasAnalysis(sourcesSinks, iCfg, executor, memoryManager);
			IInfoflowSolver backwardSolver = aliasingStrategy.getSolver();
			if (backwardSolver != null) {
				zeroValue = backwardSolver.getTabulationProblem().createZeroValue();
				solvers.add(backwardSolver);
			}

			// Initialize the aliasing infrastructure
			Aliasing aliasing = createAliasController(aliasingStrategy);
			if (dummyMainMethod != null)
				aliasing.excludeMethodFromMustAlias(dummyMainMethod);
			manager.setAliasing(aliasing);

			// Initialize the data flow problem
			InfoflowProblem forwardProblem = new InfoflowProblem(manager, zeroValue, ruleManagerFactory);

			// We need to create the right data flow solver
			IInfoflowSolver forwardSolver = createForwardSolver(executor, forwardProblem);

			// Set the options
			manager.setForwardSolver(forwardSolver);
			if (aliasingStrategy.getSolver() != null)
				aliasingStrategy.getSolver().getTabulationProblem().getManager().setForwardSolver(forwardSolver);
			solvers.add(forwardSolver);

			memoryWatcher.addSolver((IMemoryBoundedSolver) forwardSolver);

			forwardSolver.setMemoryManager(memoryManager);
			// forwardSolver.setEnableMergePointChecking(true);

			forwardProblem.setTaintPropagationHandler(taintPropagationHandler);
			forwardProblem.setTaintWrapper(taintWrapper);
			if (nativeCallHandler != null)
				forwardProblem.setNativeCallHandler(nativeCallHandler);

			if (aliasingStrategy.getSolver() != null) {
				aliasingStrategy.getSolver().getTabulationProblem().setActivationUnitsToCallSites(forwardProblem);
			}

			// Start a thread for enforcing the timeout
			FlowDroidTimeoutWatcher timeoutWatcher = null;
			FlowDroidTimeoutWatcher pathTimeoutWatcher = null;
			if (config.getDataFlowTimeout() > 0) {
				timeoutWatcher = new FlowDroidTimeoutWatcher(config.getDataFlowTimeout(), results);
				timeoutWatcher.addSolver((IMemoryBoundedSolver) forwardSolver);
				if (aliasingStrategy.getSolver() != null)
					timeoutWatcher.addSolver((IMemoryBoundedSolver) aliasingStrategy.getSolver());
				timeoutWatcher.start();
			}

			InterruptableExecutor resultExecutor = null;
			long beforePathReconstruction = 0;
			try {
				// Print our configuration
				if (config.getFlowSensitiveAliasing() && !aliasingStrategy.isFlowSensitive())
					logger.warn("Trying to use a flow-sensitive aliasing with an "
							+ "aliasing strategy that does not support this feature");
				if (config.getFlowSensitiveAliasing()
						&& config.getSolverConfiguration().getMaxJoinPointAbstractions() > 0)
					logger.warn("Running with limited join point abstractions can break context-"
							+ "sensitive path builders");

				// We have to look through the complete program to find
				// sources which are then taken as seeds.
				int sinkCount = 0;
				logger.info("Looking for sources and sinks...");

				for (SootMethod sm : getMethodsForSeeds(iCfg))
					sinkCount += scanMethodForSourcesSinks(sourcesSinks, forwardProblem, sm);

				// We optionally also allow additional seeds to be specified
				if (additionalSeeds != null)
					for (String meth : additionalSeeds) {
						SootMethod m = Scene.v().getMethod(meth);
						if (!m.hasActiveBody()) {
							logger.warn("Seed method {} has no active body", m);
							continue;
						}
						forwardProblem.addInitialSeeds(m.getActiveBody().getUnits().getFirst(),
								Collections.singleton(forwardProblem.zeroValue()));
					}

				// Report on the sources and sinks we have found
				if (!forwardProblem.hasInitialSeeds()) {
					logger.error("No sources found, aborting analysis");
					continue;
				}
				if (sinkCount == 0) {
					logger.error("No sinks found, aborting analysis");
					continue;
				}
				logger.info("Source lookup done, found {} sources and {} sinks.",
						forwardProblem.getInitialSeeds().size(), sinkCount);

				// Update the performance statistics
				performanceData.setSourceCount(forwardProblem.getInitialSeeds().size());
				performanceData.setSinkCount(sinkCount);

				// Initialize the taint wrapper if we have one
				if (taintWrapper != null)
					taintWrapper.initialize(manager);
				if (nativeCallHandler != null)
					nativeCallHandler.initialize(manager);

				// Register the handler for interim results
				TaintPropagationResults propagationResults = forwardProblem.getResults();
				resultExecutor = executorFactory.createExecutor(numThreads, false, config);
				resultExecutor.setThreadFactory(new ThreadFactory() {

					@Override
					public Thread newThread(Runnable r) {
						Thread thrPath = new Thread(r);
						thrPath.setDaemon(true);
						thrPath.setName("FlowDroid Path Reconstruction");
						return thrPath;
					}
				});

				// Create the path builder
				final IAbstractionPathBuilder builder = createPathBuilder(resultExecutor);
//							final IAbstractionPathBuilder builder = new DebuggingPathBuilder(pathBuilderFactory, manager);

				// If we want incremental result reporting, we have to
				// initialize it before we start the taint tracking
				if (config.getIncrementalResultReporting())
					initializeIncrementalResultReporting(propagationResults, builder);

				// Initialize the performance data
				if (performanceData.getTaintPropagationSeconds() < 0)
					performanceData.setTaintPropagationSeconds(0);
				long beforeTaintPropagation = System.nanoTime();

				onBeforeTaintPropagation(forwardSolver, backwardSolver);
				forwardSolver.solve();

				// Not really nice, but sometimes Heros returns before all
				// executor tasks are actually done. This way, we give it a
				// chance to terminate gracefully before moving on.
				int terminateTries = 0;
				while (terminateTries < 10) {
					if (executor.getActiveCount() != 0 || !executor.isTerminated()) {
						terminateTries++;
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							logger.error("Could not wait for executor termination", e);
						}
					} else
						break;
				}
				if (executor.getActiveCount() != 0 || !executor.isTerminated())
					logger.error("Executor did not terminate gracefully");
				if (executor.getException() != null) {
					throw new RuntimeException("An exception has occurred in an executor", executor.getException());
				}

				// Update performance statistics
				performanceData.updateMaxMemoryConsumption(getUsedMemory());
				int taintPropagationSeconds = (int) Math.round((System.nanoTime() - beforeTaintPropagation) / 1E9);
				performanceData.addTaintPropagationSeconds(taintPropagationSeconds);
				performanceData.addEdgePropagationCount(forwardSolver.getPropagationCount());
				if (backwardSolver != null)
					performanceData.addEdgePropagationCount(backwardSolver.getPropagationCount());

				// Print taint wrapper statistics
				if (taintWrapper != null) {
					logger.info("Taint wrapper hits: " + taintWrapper.getWrapperHits());
					logger.info("Taint wrapper misses: " + taintWrapper.getWrapperMisses());
				}

				// Give derived classes a chance to do whatever they need before we remove stuff
				// from memory
				onTaintPropagationCompleted(forwardSolver, backwardSolver);

				// Get the result abstractions
				Set res = propagationResults.getResults();
				propagationResults = null;

				// We need to prune access paths that are entailed by
				// another one
				removeEntailedAbstractions(res);

				// Shut down the native call handler
				if (nativeCallHandler != null)
					nativeCallHandler.shutdown();

				logger.info(
						"IFDS problem with {} forward and {} backward edges solved in {} seconds, processing {} results...",
						forwardSolver.getPropagationCount(),
						aliasingStrategy.getSolver() == null ? 0 : aliasingStrategy.getSolver().getPropagationCount(),
						taintPropagationSeconds, res == null ? 0 : res.size());

				// Update the statistics
				{
					ISolverTerminationReason reason = ((IMemoryBoundedSolver) forwardSolver).getTerminationReason();
					if (reason != null) {
						if (reason instanceof OutOfMemoryReason)
							results.setTerminationState(
									results.getTerminationState() | InfoflowResults.TERMINATION_DATA_FLOW_OOM);
						else if (reason instanceof TimeoutReason)
							results.setTerminationState(
									results.getTerminationState() | InfoflowResults.TERMINATION_DATA_FLOW_TIMEOUT);
					}
				}

				// Force a cleanup. Everything we need is reachable through
				// the results set, the other abstractions can be killed
				// now.
				performanceData.updateMaxMemoryConsumption(getUsedMemory());
				logger.info(String.format("Current memory consumption: %d MB", getUsedMemory()));

				if (timeoutWatcher != null)
					timeoutWatcher.stop();
				memoryWatcher.removeSolver((IMemoryBoundedSolver) forwardSolver);
				forwardSolver.cleanup();
				forwardSolver = null;
				forwardProblem = null;

				solverPeerGroup = null;

				// Remove the alias analysis from memory
				aliasing = null;
				if (aliasingStrategy.getSolver() != null) {
					aliasingStrategy.getSolver().terminate();
					memoryWatcher.removeSolver((IMemoryBoundedSolver) aliasingStrategy.getSolver());
				}
				aliasingStrategy.cleanup();
				aliasingStrategy = null;

				if (config.getIncrementalResultReporting())
					res = null;
				iCfg.purge();

				// Clean up the manager. Make sure to free objects, even if
				// the manager is still held by other objects
				if (manager != null)
					manager.cleanup();
				manager = null;

				// Report the remaining memory consumption
				Runtime.getRuntime().gc();
				performanceData.updateMaxMemoryConsumption(getUsedMemory());
				logger.info(String.format("Memory consumption after cleanup: %d MB", getUsedMemory()));

				// Apply the timeout to path reconstruction
				if (config.getPathConfiguration().getPathReconstructionTimeout() > 0) {
					pathTimeoutWatcher = new FlowDroidTimeoutWatcher(
							config.getPathConfiguration().getPathReconstructionTimeout(), results);
					pathTimeoutWatcher.addSolver(builder);
					pathTimeoutWatcher.start();
				}
				beforePathReconstruction = System.nanoTime();

				// Do the normal result computation in the end unless we
				// have used incremental path building
				if (config.getIncrementalResultReporting()) {
					// After the last intermediate result has been computed,
					// we need to re-process those abstractions that
					// received new neighbors in the meantime
					builder.runIncrementalPathCompuation();

					try {
						resultExecutor.awaitCompletion();
					} catch (InterruptedException e) {
						logger.error("Could not wait for executor termination", e);
					}
				} else {
					memoryWatcher.addSolver(builder);
					builder.computeTaintPaths(res);
					res = null;

					// Wait for the path builders to terminate
					try {
						// The path reconstruction should stop on time anyway. In case it doesn't, we
						// make sure that we don't get stuck.
						long pathTimeout = config.getPathConfiguration().getPathReconstructionTimeout();
						if (pathTimeout > 0)
							resultExecutor.awaitCompletion(pathTimeout + 20, TimeUnit.SECONDS);
						else
							resultExecutor.awaitCompletion();
					} catch (InterruptedException e) {
						logger.error("Could not wait for executor termination", e);
					}

					// Update the statistics
					{
						ISolverTerminationReason reason = builder.getTerminationReason();
						if (reason != null) {
							if (reason instanceof OutOfMemoryReason)
								results.setTerminationState(results.getTerminationState()
										| InfoflowResults.TERMINATION_PATH_RECONSTRUCTION_OOM);
							else if (reason instanceof TimeoutReason)
								results.setTerminationState(results.getTerminationState()
										| InfoflowResults.TERMINATION_PATH_RECONSTRUCTION_TIMEOUT);
						}
					}

					// Get the results once the path builder is done
					this.results.addAll(builder.getResults());
				}
				resultExecutor.shutdown();

				// If the path builder was aborted, we warn the user
				if (builder.isKilled())
					logger.warn("Path reconstruction aborted. The reported results may be incomplete. "
							+ "You might want to try again with sequential path processing enabled.");
			} finally {
				// Terminate the executor
				if (resultExecutor != null)
					resultExecutor.shutdown();

				// Make sure to stop the watcher thread
				if (timeoutWatcher != null)
					timeoutWatcher.stop();
				if (pathTimeoutWatcher != null)
					pathTimeoutWatcher.stop();

				if (aliasingStrategy != null) {
					IInfoflowSolver solver = aliasingStrategy.getSolver();
					if (solver != null)
						solver.terminate();
				}

				// Do we have any more sources?
				hasMoreSources = oneSourceAtATime != null && oneSourceAtATime.hasNextSource();

				// Shut down the memory watcher
				memoryWatcher.close();

				// Get rid of all the stuff that's still floating around in
				// memory
				forwardProblem = null;
				forwardSolver = null;
				if (manager != null)
					manager.cleanup();
				manager = null;
			}

			// Make sure that we are in a sensible state even if we ran out
			// of memory before
			Runtime.getRuntime().gc();
			performanceData.updateMaxMemoryConsumption((int) getUsedMemory());
			performanceData.setPathReconstructionSeconds(
					(int) Math.round((System.nanoTime() - beforePathReconstruction) / 1E9));

			logger.info(String.format("Memory consumption after path building: %d MB", getUsedMemory()));
			logger.info(String.format("Path reconstruction took %d seconds",
					performanceData.getPathReconstructionSeconds()));
		}

		// Execute the post-processors
		for (PostAnalysisHandler handler : this.postProcessors)
			results = handler.onResultsAvailable(results, iCfg);

		if (results == null || results.isEmpty())
			logger.warn("No results found.");
		else if (logger.isInfoEnabled()) {
			for (ResultSinkInfo sink : results.getResults().keySet()) {
				logger.info("The sink {} in method {} was called with values from the following sources:", sink,
						iCfg.getMethodOf(sink.getStmt()).getSignature());
				for (ResultSourceInfo source : results.getResults().get(sink)) {
					logger.info("- {} in method {}", source, iCfg.getMethodOf(source.getStmt()).getSignature());
					if (source.getPath() != null) {
						logger.info("\ton Path: ");
						for (Unit p : source.getPath()) {
							logger.info("\t -> " + iCfg.getMethodOf(p));
							logger.info("\t\t -> " + p);
						}
					}
				}
			}
		}
	}

	/**
	 * Creates the path builder that shall be used for path reconstruction
	 * 
	 * @param executor The execute in which to run the parallel path reconstruction
	 *                 tasks
	 * @return The path builder implementation
	 */
	protected IAbstractionPathBuilder createPathBuilder(InterruptableExecutor executor) {
		return pathBuilderFactory.createPathBuilder(manager, executor);
	}

	/**
	 * Factory method for creating the data object that will receive the data flow
	 * solver's performance data
	 * 
	 * @return The data object for the performance data
	 */
	protected InfoflowPerformanceData createPerformanceDataClass() {
		return new InfoflowPerformanceData();
	}

	/**
	 * Creates the controller object that handles aliasing operations. Derived
	 * classes can override this method to supply custom aliasing implementations.
	 * 
	 * @param aliasingStrategy The aliasing strategy to use
	 * @return The new alias controller object
	 */
	protected Aliasing createAliasController(IAliasingStrategy aliasingStrategy) {
		return new Aliasing(aliasingStrategy, manager);
	}

	/**
	 * Callback that is invoked when the main taint propagation is about to start
	 * 
	 * @param forwardSolver  The forward data flow solver
	 * @param backwardSolver The backward data flow solver
	 */
	protected void onBeforeTaintPropagation(IInfoflowSolver forwardSolver, IInfoflowSolver backwardSolver) {
		//
	}

	/**
	 * Callback that is invoked when the main taint propagation has completed. This
	 * method is called before memory cleanup happens.
	 * 
	 * @param forwardSolver  The forward data flow solver
	 * @param backwardSolver The backward data flow solver
	 */
	protected void onTaintPropagationCompleted(IInfoflowSolver forwardSolver, IInfoflowSolver backwardSolver) {
		//
	}

	/**
	 * Initializes the data flow manager with which propagation rules can interact
	 * with the data flow engine
	 * 
	 * @param sourcesSinks       The source/sink definitions
	 * @param iCfg               The interprocedural control flow graph
	 * @param globalTaintManager The manager object for storing and processing
	 *                           global taints
	 * @return The data flow manager
	 */
	protected InfoflowManager initializeInfoflowManager(final ISourceSinkManager sourcesSinks, IInfoflowCFG iCfg,
			GlobalTaintManager globalTaintManager) {
		return new InfoflowManager(config, null, iCfg, sourcesSinks, taintWrapper, hierarchy,
				new AccessPathFactory(config), globalTaintManager);
	}

	/**
	 * Initializes the mechanism for incremental result reporting
	 * 
	 * @param propagationResults A reference to the result object of the forward
	 *                           data flow solver
	 * @param builder            The path builder to use for reconstructing the
	 *                           taint propagation paths
	 */
	private void initializeIncrementalResultReporting(TaintPropagationResults propagationResults,
			final IAbstractionPathBuilder builder) {
		// Create the path builder
		memoryWatcher.addSolver(builder);
		this.results = new InfoflowResults();
		propagationResults.addResultAvailableHandler(new OnTaintPropagationResultAdded() {

			@Override
			public boolean onResultAvailable(AbstractionAtSink abs) {
				builder.addResultAvailableHandler(new OnPathBuilderResultAvailable() {

					@Override
					public void onResultAvailable(ResultSourceInfo source, ResultSinkInfo sink) {
						// Notify our external handlers
						for (ResultsAvailableHandler handler : onResultsAvailable) {
							if (handler instanceof ResultsAvailableHandler2) {
								ResultsAvailableHandler2 handler2 = (ResultsAvailableHandler2) handler;
								handler2.onSingleResultAvailable(source, sink);
							}
						}
						results.addResult(sink, source);
					}

				});

				// Compute the result paths
				builder.computeTaintPaths(Collections.singleton(abs));
				return true;
			}

		});
	}

	/**
	 * Checks the configuration of the data flow solver for errors and automatically
	 * fixes some common issues
	 */
	private void checkAndFixConfiguration() {
		final AccessPathConfiguration accessPathConfig = config.getAccessPathConfiguration();
		if (config.getStaticFieldTrackingMode() != StaticFieldTrackingMode.None
				&& accessPathConfig.getAccessPathLength() == 0)
			throw new RuntimeException("Static field tracking must be disabled if the access path length is zero");
		if (config.getSolverConfiguration().getDataFlowSolver() == DataFlowSolver.FlowInsensitive) {
			config.setFlowSensitiveAliasing(false);
			config.setEnableTypeChecking(false);
			logger.warn("Disabled flow-sensitive aliasing because we are running with "
					+ "a flow-insensitive data flow solver");
		}
	}

	/**
	 * Removes all abstractions from the given set that arrive at the same sink
	 * statement as another abstraction, but cover less tainted variables. If, e.g.,
	 * a.b.* and a.* arrive at the same sink, a.b.* is already covered by a.* and
	 * can thus safely be removed.
	 * 
	 * @param res The result set from which to remove all entailed abstractions
	 */
	private void removeEntailedAbstractions(Set res) {
		for (Iterator absAtSinkIt = res.iterator(); absAtSinkIt.hasNext();) {
			AbstractionAtSink curAbs = absAtSinkIt.next();
			for (AbstractionAtSink checkAbs : res) {
				if (checkAbs != curAbs && checkAbs.getSinkStmt() == curAbs.getSinkStmt()
						&& checkAbs.getAbstraction().isImplicit() == curAbs.getAbstraction().isImplicit()
						&& checkAbs.getAbstraction().getSourceContext() == curAbs.getAbstraction().getSourceContext()) {
					if (checkAbs.getAbstraction().getAccessPath().entails(curAbs.getAbstraction().getAccessPath())) {
						absAtSinkIt.remove();
						break;
					}
				}
			}
		}
	}

	/**
	 * Initializes the alias analysis
	 * 
	 * @param sourcesSinks  The set of sources and sinks
	 * @param iCfg          The interprocedural control flow graph
	 * @param executor      The executor in which to run concurrent tasks
	 * @param memoryManager The memory manager for rducing the memory load during
	 *                      IFDS propagation
	 * @return The alias analysis implementation to use for the data flow analysis
	 */
	private IAliasingStrategy createAliasAnalysis(final ISourceSinkManager sourcesSinks, IInfoflowCFG iCfg,
			InterruptableExecutor executor, IMemoryManager memoryManager) {
		IAliasingStrategy aliasingStrategy;
		IInfoflowSolver backSolver = null;
		BackwardsInfoflowProblem backProblem = null;
		InfoflowManager backwardsManager = null;
		switch (getConfig().getAliasingAlgorithm()) {
		case FlowSensitive:
			backwardsManager = new InfoflowManager(config, null, new BackwardsInfoflowCFG(iCfg), sourcesSinks,
					taintWrapper, hierarchy, manager.getAccessPathFactory(), manager.getGlobalTaintManager());
			backProblem = new BackwardsInfoflowProblem(backwardsManager);

			// We need to create the right data flow solver
			SolverConfiguration solverConfig = config.getSolverConfiguration();
			backSolver = createDataFlowSolver(executor, backProblem, solverConfig);

			backSolver.setMemoryManager(memoryManager);
			backSolver.setPredecessorShorteningMode(
					pathConfigToShorteningMode(manager.getConfig().getPathConfiguration()));
			// backSolver.setEnableMergePointChecking(true);
			backSolver.setMaxJoinPointAbstractions(solverConfig.getMaxJoinPointAbstractions());
			backSolver.setMaxCalleesPerCallSite(solverConfig.getMaxCalleesPerCallSite());
			backSolver.setMaxAbstractionPathLength(solverConfig.getMaxAbstractionPathLength());
			backSolver.setSolverId(false);
			backProblem.setTaintPropagationHandler(backwardsPropagationHandler);
			backProblem.setTaintWrapper(taintWrapper);
			if (nativeCallHandler != null)
				backProblem.setNativeCallHandler(nativeCallHandler);

			memoryWatcher.addSolver((IMemoryBoundedSolver) backSolver);

			aliasingStrategy = new FlowSensitiveAliasStrategy(manager, backSolver);
			break;
		case PtsBased:
			backProblem = null;
			backSolver = null;
			aliasingStrategy = new PtsBasedAliasStrategy(manager);
			break;
		case None:
			backProblem = null;
			backSolver = null;
			aliasingStrategy = new NullAliasStrategy();
			break;
		case Lazy:
			backProblem = null;
			backSolver = null;
			aliasingStrategy = new LazyAliasingStrategy(manager);
			break;
		default:
			throw new RuntimeException("Unsupported aliasing algorithm");
		}
		return aliasingStrategy;
	}

	/**
	 * Gets the path shortening mode that shall be applied given a certain path
	 * reconstruction configuration. This method computes the most aggressive path
	 * shortening that is possible without eliminating data that is necessary for
	 * the requested path reconstruction.
	 * 
	 * @param pathConfiguration The path reconstruction configuration
	 * @return The computed path shortening mode
	 */
	private PredecessorShorteningMode pathConfigToShorteningMode(PathConfiguration pathConfiguration) {
		if (pathBuilderFactory.supportsPathReconstruction()) {
			switch (pathConfiguration.getPathReconstructionMode()) {
			case Fast:
				return PredecessorShorteningMode.ShortenIfEqual;
			case NoPaths:
				return PredecessorShorteningMode.AlwaysShorten;
			case Precise:
				return PredecessorShorteningMode.NeverShorten;
			default:
				throw new RuntimeException("Unknown path reconstruction mode");
			}
		} else
			return PredecessorShorteningMode.AlwaysShorten;
	}

	/**
	 * Creates the memory manager that helps reduce the memory consumption of the
	 * data flow analysis
	 * 
	 * @return The memory manager object
	 */
	private IMemoryManager createMemoryManager() {
		if (memoryManagerFactory == null)
			return null;

		PathDataErasureMode erasureMode;
		if (config.getPathConfiguration().mustKeepStatements())
			erasureMode = PathDataErasureMode.EraseNothing;
		else if (pathBuilderFactory.supportsPathReconstruction())
			erasureMode = PathDataErasureMode.EraseNothing;
		else if (pathBuilderFactory.isContextSensitive())
			erasureMode = PathDataErasureMode.KeepOnlyContextData;
		else
			erasureMode = PathDataErasureMode.EraseAll;
		IMemoryManager memoryManager = memoryManagerFactory.getMemoryManager(false, erasureMode);
		return memoryManager;
	}

	/**
	 * Creates the IFDS solver for the forward data flow problem
	 * 
	 * @param executor       The executor in which to run the tasks or propagating
	 *                       IFDS edges
	 * @param forwardProblem The implementation of the forward problem
	 * @return The solver that solves the forward taint analysis problem
	 */
	private IInfoflowSolver createForwardSolver(InterruptableExecutor executor, InfoflowProblem forwardProblem) {
		// Depending on the configured solver algorithm, we have to create a
		// different solver object
		IInfoflowSolver forwardSolver;
		SolverConfiguration solverConfig = config.getSolverConfiguration();
		forwardSolver = createDataFlowSolver(executor, forwardProblem, solverConfig);

		// Configure the solver
		forwardSolver.setSolverId(true);
		forwardSolver
				.setPredecessorShorteningMode(pathConfigToShorteningMode(manager.getConfig().getPathConfiguration()));
		forwardSolver.setMaxJoinPointAbstractions(solverConfig.getMaxJoinPointAbstractions());
		forwardSolver.setMaxCalleesPerCallSite(solverConfig.getMaxCalleesPerCallSite());
		forwardSolver.setMaxAbstractionPathLength(solverConfig.getMaxAbstractionPathLength());

		return forwardSolver;
	}

	/**
	 * Creates the instance of the data flow solver
	 * 
	 * @param executor     The executor on which the solver shall run its tasks
	 * @param problem      The problem to be solved by the new solver
	 * @param solverConfig The solver configuration
	 * @return The new data flow solver
	 */
	protected IInfoflowSolver createDataFlowSolver(InterruptableExecutor executor, AbstractInfoflowProblem problem,
			SolverConfiguration solverConfig) {
		switch (solverConfig.getDataFlowSolver()) {
		case ContextFlowSensitive:
			logger.info("Using context- and flow-sensitive solver");
			return new soot.jimple.infoflow.solver.fastSolver.InfoflowSolver(problem, executor);
		case FlowInsensitive:
			logger.info("Using context-sensitive, but flow-insensitive solver");
			return new soot.jimple.infoflow.solver.fastSolver.flowInsensitive.InfoflowSolver(problem, executor);
		case GarbageCollecting:
			logger.info("Using garbage-collecting solver");
			IInfoflowSolver solver = new soot.jimple.infoflow.solver.gcSolver.InfoflowSolver(problem, executor);
			solverPeerGroup.addSolver(solver);
			solver.setPeerGroup(solverPeerGroup);
			return solver;
		default:
			throw new RuntimeException("Unsupported data flow solver");
		}
	}

	/**
	 * Gets the memory used by FlowDroid at the moment
	 * 
	 * @return FlowDroid's current memory consumption in megabytes
	 */
	private int getUsedMemory() {
		Runtime runtime = Runtime.getRuntime();
		return (int) Math.round((runtime.totalMemory() - runtime.freeMemory()) / 1E6);
	}

	/**
	 * Runs all code optimizers
	 * 
	 * @param sourcesSinks The SourceSinkManager
	 */
	protected void eliminateDeadCode(ISourceSinkManager sourcesSinks) {
		InfoflowManager dceManager = new InfoflowManager(config, null,
				icfgFactory.buildBiDirICFG(config.getCallgraphAlgorithm(), config.getEnableExceptionTracking()), null,
				null, null, new AccessPathFactory(config), null);

		// Dead code elimination may drop the points-to analysis. We need to restore it.
		final Scene scene = Scene.v();
		PointsToAnalysis pta = scene.getPointsToAnalysis();

		// We need to exclude the dummy main method and all other artificial methods
		// that the entry point creator may have generated as well
		Set excludedMethods = new HashSet<>();
		if (additionalEntryPointMethods != null)
			excludedMethods.addAll(additionalEntryPointMethods);
		excludedMethods.addAll(Scene.v().getEntryPoints());

		ICodeOptimizer dce = new DeadCodeEliminator();
		dce.initialize(config);
		dce.run(dceManager, excludedMethods, sourcesSinks, taintWrapper);

		// Restore the points-to analysis. This may restore a PAG that contains outdated
		// methods, but it's still better than running the entire callgraph algorithm
		// again. Continuing with the DumbPointerAnalysis is not a viable solution
		// either, since it heavily over-approximates.
		if (pta != null && !(pta instanceof DumbPointerAnalysis)) {
			PointsToAnalysis newPta = scene.getPointsToAnalysis();
			if (newPta == null || newPta instanceof DumbPointerAnalysis)
				scene.setPointsToAnalysis(pta);
		}
	}

	protected Collection getMethodsForSeeds(IInfoflowCFG icfg) {
		List seeds = new LinkedList();
		// If we have a callgraph, we retrieve the reachable methods. Otherwise,
		// we have no choice but take all application methods as an
		// approximation
		if (Scene.v().hasCallGraph()) {
			ReachableMethods reachableMethods = Scene.v().getReachableMethods();
			reachableMethods.update();
			for (Iterator iter = reachableMethods.listener(); iter.hasNext();) {
				SootMethod sm = iter.next().method();
				if (isValidSeedMethod(sm))
					seeds.add(sm);
			}
		} else {
			long beforeSeedMethods = System.nanoTime();
			Set doneSet = new HashSet();
			for (SootMethod sm : Scene.v().getEntryPoints())
				getMethodsForSeedsIncremental(sm, doneSet, seeds, icfg);
			logger.info("Collecting seed methods took {} seconds", (System.nanoTime() - beforeSeedMethods) / 1E9);
		}
		return seeds;
	}

	private void getMethodsForSeedsIncremental(SootMethod sm, Set doneSet, List seeds,
			IInfoflowCFG icfg) {
		assert Scene.v().hasFastHierarchy();
		if (!sm.isConcrete() || !sm.getDeclaringClass().isApplicationClass() || !doneSet.add(sm))
			return;
		seeds.add(sm);
		for (Unit u : sm.retrieveActiveBody().getUnits()) {
			Stmt stmt = (Stmt) u;
			if (stmt.containsInvokeExpr()) {
				for (SootMethod callee : icfg.getCalleesOfCallAt(stmt)) {
					if (isValidSeedMethod(callee))
						getMethodsForSeedsIncremental(callee, doneSet, seeds, icfg);
				}
			}
		}
	}

	/**
	 * Gets whether the given method is a valid seen when scanning for sources and
	 * sinks. A method is a valid seed it it (or one of its transitive callees) can
	 * contain calls to source or sink methods.
	 * 
	 * @param sm The method to check
	 * @return True if this method or one of its transitive callees can contain
	 *         sources or sinks, otherwise false
	 */
	protected boolean isValidSeedMethod(SootMethod sm) {
		if (sm == dummyMainMethod)
			return false;
		if (dummyMainMethod != null && sm.getDeclaringClass() == dummyMainMethod.getDeclaringClass())
			return false;

		// Exclude system classes
		final String className = sm.getDeclaringClass().getName();
		if (config.getIgnoreFlowsInSystemPackages() && SystemClassHandler.v().isClassInSystemPackage(className)
				&& !isUserCodeClass(className))
			return false;

		// Exclude library classes
		if (config.getExcludeSootLibraryClasses() && sm.getDeclaringClass().isLibraryClass())
			return false;

		return true;
	}

	/**
	 * Checks whether the given class is user code and should not be filtered out.
	 * By default, this method assumes that all code is potentially user code.
	 * 
	 * @param className The name of the class to check
	 * @return True if the given class is user code, false otherwise
	 */
	protected boolean isUserCodeClass(String className) {
		return true;
	}

	/**
	 * Scans the given method for sources and sinks contained in it. Sinks are just
	 * counted, sources are added to the InfoflowProblem as seeds.
	 * 
	 * @param sourcesSinks   The SourceSinkManager to be used for identifying
	 *                       sources and sinks
	 * @param forwardProblem The InfoflowProblem in which to register the sources as
	 *                       seeds
	 * @param m              The method to scan for sources and sinks
	 * @return The number of sinks found in this method
	 */
	private int scanMethodForSourcesSinks(final ISourceSinkManager sourcesSinks, InfoflowProblem forwardProblem,
			SootMethod m) {
		if (getConfig().getLogSourcesAndSinks() && collectedSources == null) {
			collectedSources = new HashSet<>();
			collectedSinks = new HashSet<>();
		}

		int sinkCount = 0;
		if (m.hasActiveBody()) {
			// Check whether this is a system class we need to ignore
			if (!isValidSeedMethod(m))
				return sinkCount;

			// Look for a source in the method. Also look for sinks. If we
			// have no sink in the program, we don't need to perform any
			// analysis
			PatchingChain units = m.getActiveBody().getUnits();
			for (Unit u : units) {
				Stmt s = (Stmt) u;
				if (sourcesSinks.getSourceInfo(s, manager) != null) {
					forwardProblem.addInitialSeeds(u, Collections.singleton(forwardProblem.zeroValue()));
					if (getConfig().getLogSourcesAndSinks())
						collectedSources.add(s);
					logger.debug("Source found: {} in {}", u, m.getSignature());
				}
				if (sourcesSinks.getSinkInfo(s, manager, null) != null) {
					sinkCount++;
					if (getConfig().getLogSourcesAndSinks())
						collectedSinks.add(s);
					logger.debug("Sink found: {} in {}", u, m.getSignature());
				}
			}

		}
		return sinkCount;
	}

	@Override
	public InfoflowResults getResults() {
		return results;
	}

	@Override
	public boolean isResultAvailable() {
		if (results == null) {
			return false;
		}
		return true;
	}

	@Override
	public void addResultsAvailableHandler(ResultsAvailableHandler handler) {
		this.onResultsAvailable.add(handler);
	}

	@Override
	public void setTaintPropagationHandler(TaintPropagationHandler handler) {
		this.taintPropagationHandler = handler;
	}

	@Override
	public void setBackwardsPropagationHandler(TaintPropagationHandler handler) {
		this.backwardsPropagationHandler = handler;
	}

	@Override
	public void removeResultsAvailableHandler(ResultsAvailableHandler handler) {
		onResultsAvailable.remove(handler);
	}

	@Override
	public Set getCollectedSources() {
		return this.collectedSources;
	}

	@Override
	public Set getCollectedSinks() {
		return this.collectedSinks;
	}

	@Override
	public void setMemoryManagerFactory(IMemoryManagerFactory factory) {
		this.memoryManagerFactory = factory;
	}

	@Override
	public void setExecutorFactory(IExecutorFactory executorFactory) {
		this.executorFactory = executorFactory;
	}

	@Override
	public void setPropagationRuleManagerFactory(IPropagationRuleManagerFactory ruleManagerFactory) {
		this.ruleManagerFactory = ruleManagerFactory;
	}

	@Override
	public void abortAnalysis() {
		ISolverTerminationReason reason = new AbortRequestedReason();

		if (manager != null) {
			// Stop the forward taint analysis
			if (manager.getForwardSolver() instanceof IMemoryBoundedSolver) {
				IMemoryBoundedSolver boundedSolver = (IMemoryBoundedSolver) manager.getForwardSolver();
				boundedSolver.forceTerminate(reason);
			}
		}

		if (memoryWatcher != null) {
			// Stop all registered solvers
			memoryWatcher.forceTerminate(reason);
		}
	}

	public void setThrowExceptions(boolean b) {
		this.throwExceptions = b;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy