From 851594cff504a628141e442929ce6bc468076d84 Mon Sep 17 00:00:00 2001 From: Tobias Blaschke Date: Sun, 2 Mar 2014 16:46:49 +0100 Subject: [PATCH] Make Intents immutable when attached to Context Oops. Fixes in intent-Resolution --- .../ipa/callgraph/impl/ExplicitCallGraph.java | 2 +- .../stubs/AndroidStartComponentTool.java | 6 ++- .../ipa/callgraph/propagation/cfa/Intent.java | 42 +++++++++++++++++-- .../cfa/IntentContextInterpreter.java | 7 +++- .../cfa/IntentContextSelector.java | 36 +++++++++++----- .../callgraph/propagation/cfa/IntentMap.java | 16 ++++++- 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/ExplicitCallGraph.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/ExplicitCallGraph.java index 80f53c5a5..8932c1d2f 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/ExplicitCallGraph.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/impl/ExplicitCallGraph.java @@ -510,7 +510,7 @@ public class ExplicitCallGraph extends BasicCallGraph imp public IntSet getPossibleTargetNumbers(CGNode node, CallSiteReference site) { if (!containsNode(node)) { - throw new IllegalArgumentException("node not in callgraph " + node); + throw new IllegalArgumentException("node not in callgraph " + node + " Site: " + site); } assert (node instanceof ExplicitNode); ExplicitNode n = (ExplicitNode) node; diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/stubs/AndroidStartComponentTool.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/stubs/AndroidStartComponentTool.java index 1defd8bf2..734f687b1 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/stubs/AndroidStartComponentTool.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/stubs/AndroidStartComponentTool.java @@ -261,7 +261,9 @@ public class AndroidStartComponentTool { logger.debug("Fetching caller context..."); final SSAValue androidContext; if (caller.getName().equals(AndroidTypes.ContextWrapperName)) { - { // Fetch ContextWrapperName.mBase => androidContext + this.callerContext = AndroidTypes.AndroidContextType.USELESS; + return null; + /*{ // Fetch ContextWrapperName.mBase => androidContext androidContext = pm.getUnmanaged(AndroidTypes.Context, "callerContext"); logger.debug("Fetching ContextWrapperName.mBase"); @@ -275,7 +277,7 @@ public class AndroidStartComponentTool { this.callerContext = AndroidTypes.AndroidContextType.CONTEXT_IMPL; logger.info("Caller has android-context type: ContextWrapper(ContextImpl)"); return androidContext; - } + } */ } else if (caller.getName().equals(AndroidTypes.ContextImplName)) { { // self is already the right context androidContext = self; diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java index bae2a4ef2..75f9b97e5 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java @@ -74,7 +74,7 @@ import org.slf4j.LoggerFactory; * @author Tobias Blaschke * @since 2013-10-12 */ -public class Intent implements ContextItem { +public class Intent implements ContextItem, Comparable { private static final Logger logger = LoggerFactory.getLogger(Intent.class); /** @@ -112,6 +112,7 @@ public class Intent implements ContextItem { private IntentType type; private Explicit explicit = Explicit.UNSET; private AndroidComponent targetCompontent; // Activity, Service, ... + private boolean immutable = false; public Intent() { this.action = null; @@ -152,6 +153,19 @@ public class Intent implements ContextItem { return explicit == Explicit.EXPLICIT; } + public void setImmutable() { + this.immutable = true; + } + + public Intent clone() { + final Intent clone = new Intent(); + clone.action = this.action; // OK here? + clone.uri = this.uri; + clone.type = this.type; + clone.explicit = this.explicit; + clone.targetCompontent = this.targetCompontent; + return clone; + } /** @@ -166,6 +180,7 @@ public class Intent implements ContextItem { if (this.action == null) { assert (this.explicit == Explicit.UNSET) : "No Action but Intent is not UNSET - is " + this.explicit; + assert (! immutable) : "Intent was marked immutable - can't change it."; this.action = action; this.explicit = Explicit.EXPLICIT; logger.info("Intent({})", action); @@ -175,6 +190,7 @@ public class Intent implements ContextItem { unbind(); } else if (! isExplicit() ) { logger.warn("Making implicit Intent {} explictit! Target: {}", this, action); + assert (! immutable) : "Intent was marked immutable - can't change it."; this.action = action; this.explicit = Explicit.EXPLICIT; // TODO: Set type? @@ -185,6 +201,7 @@ public class Intent implements ContextItem { public void unbind() { + assert (! immutable) : "Intent was marked immutable - can't change it."; this.action = UNBOUND; this.type = IntentType.UNKNOWN_TARGET; this.explicit = Explicit.MULTI; // XXX shoulb we do this? @@ -197,6 +214,7 @@ public class Intent implements ContextItem { */ public void setAction(Atom action) { if (this.action == null) { + assert (! immutable) : "Intent was marked immutable - can't change it."; this.action = action; logger.info("Intent({})", action); } else if (isExplicit()) { @@ -218,7 +236,9 @@ public class Intent implements ContextItem { if (this.type != null) { return this.type; } else { - if (isStandardAction(this)) { + if (isSystemService(this)) { + this.type = IntentType.SYSTEM_SERVICE; + } else if (isStandardAction(this)) { this.type = IntentType.STANDARD_ACTION; } else if (isInternal(this)) { this.type = IntentType.INTERNAL_TARGET; @@ -245,6 +265,11 @@ public class Intent implements ContextItem { return this.targetCompontent; } + private static boolean isSystemService(Intent intent) { + assert (intent.action != null); + return ((intent.action.getVal(0) != 'L') && (intent.action.rIndex((byte) '/') < 0) && (intent.action.rIndex((byte) '.') < 0)); + } + /** * Fallback: tries to determine on the Intent itself if it's internal. * @@ -417,13 +442,22 @@ public class Intent implements ContextItem { // DO NOT USE TYPE! if (this.uri != null) { - return ( (this.uri.equals(other.uri)) && equalAction(other)); + return ( (this.uri.equals(other.uri)) && equalAction(other) ); // && (this.explicit == other.explicit)); } else { - return ( (other.uri == null) && equalAction(other) ) ; + return ( (other.uri == null) && equalAction(other) ); // && (this.explicit == other.explicit)) ; } } else { System.err.println("WARNING: Can't compare Intent to " + o.getClass()); return false; } } + + public Intent resolve() { + return AndroidEntryPointManager.MANAGER.getIntent(this); + } + + @Override + public int compareTo(Intent other) { + return getAction().toString().compareTo(other.getAction().toString()); + } } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextInterpreter.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextInterpreter.java index d566a50dc..b91b416aa 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextInterpreter.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextInterpreter.java @@ -123,9 +123,14 @@ public class IntentContextInterpreter implements SSAContextInterpreter { /** * Read possible targets of the intents Infos. */ - private AndroidComponent fetchTargetComponent(final Intent intent, final IMethod method) { + private AndroidComponent fetchTargetComponent(final Intent intent, final IMethod method) { + assert (method != null); + assert (intentStarters.getInfo(method.getReference()) != null) : "No IntentStarter for Method " + method + " " + intent; if (intent.getComponent() != null) { return intent.getComponent(); + } else if (intent.getType() == Intent.IntentType.SYSTEM_SERVICE) { + logger.error("Called fetchTargetComponent on a SystemService"); + return null; } else { final Set possibleTargets = intentStarters.getInfo(method.getReference()).getComponentsPossible(); if (possibleTargets.size() == 1) { diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java index 4047d7ed8..7c7017615 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java @@ -126,6 +126,7 @@ public class IntentContextSelector implements ContextSelector { if (this.parent != null) { ctx = parent.getCalleeTarget(caller, site, callee, actualParameters); + assert (ctx.get(Intent.INTENT_KEY) == null) : "Already have Intent: " + ctx + " caller " + caller + " callee " + callee; } if (intentStarters.isStarter(callee.getReference())) { @@ -165,13 +166,17 @@ public class IntentContextSelector implements ContextSelector { // Add the context if (intent != null) { - AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); - return new IntentContext(ctx, intent); + AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); + final Intent iintent = intents.findOrCreateImmutable(intent); + return new IntentContext(ctx, iintent); + //return new IntentContext(iintent); } else { logger.warn("Encountered unresolvable Intent"); intent = new Intent("Unresolvable"); + intent.setImmutable(); AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); return new IntentContext(ctx, intent); + //return new IntentContext(intent); } } else if (callee.getReference().toString().contains("getSystemService")) { assert(actualParameters.length == 2) : "PARAMS LENGTH IS" + actualParameters.length; @@ -202,7 +207,9 @@ public class IntentContextSelector implements ContextSelector { if (intent != null) { AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); logger.info("SystemService {} in {} by {}", intent, site, caller); - return new IntentContext(ctx, intent); + final Intent iintent = intents.findOrCreateImmutable(intent); + return new IntentContext(ctx, iintent); + //return new IntentContext(iintent); } } else if (callee.isInit() && callee.getDeclaringClass().getName().equals(AndroidTypes.IntentName)) { // @@ -302,18 +309,20 @@ public class IntentContextSelector implements ContextSelector { final InstanceKey actionKey = actualParameters[1]; final Intent intent = intents.find(self); - logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); - // Also we _could_ set the new target this is probably a bad idea: If setAction is called - // from a branch in execution the original target could still be called. - // We should implement intents, that can have multiple targets. - intents.setAction(self, actionKey, false); // May unbind internally - //intents.unbind(self); + if (AndroidEntryPointManager.MANAGER.isAllowIntentRerouting()) { + logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); + intents.setAction(self, actionKey, false); // May unbind internally + } else { + intents.unbind(self); + } + logger.info("Encountered Intent.setAction - Intent is now: {}", intent); } else if (callee.getSelector().equals(Selector.make("setComponent(Landroid/content/ComponentName;)Landroid/content/Intent;"))) { // TODO: We can't extract from ComponentName yet. final InstanceKey self = actualParameters[0]; final Intent intent = intents.find(self); logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); + intent.setExplicit(); intents.unbind(self); } else if (callee.getSelector().equals(Selector.make("setClass(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;")) || @@ -323,8 +332,13 @@ public class IntentContextSelector implements ContextSelector { final InstanceKey actionKey = actualParameters[2]; final Intent intent = intents.find(self); - logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); - intents.setAction(self, actionKey, true); + if (AndroidEntryPointManager.MANAGER.isAllowIntentRerouting()) { + logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); + intents.setAction(self, actionKey, true); + } else { + intents.unbind(self); + } + logger.info("Encountered Intent.setClass - Intent is now: {}", intent); } else if (callee.getSelector().equals(Selector.make("fillIn(Landroid/content/Intent;I)I"))) { // See 'setAction' before... TODO logger.warn("Intent.fillIn not implemented - Caller: {}", caller); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentMap.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentMap.java index 7853c49a7..fa9e2e9ef 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentMap.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentMap.java @@ -54,8 +54,22 @@ import org.slf4j.LoggerFactory; /*package*/ class IntentMap { private static final Logger logger = LoggerFactory.getLogger(IntentContextSelector.class); - /* package */ final Map seen = new HashMap(); + private final Map seen = new HashMap(); + private final Map immutables = new HashMap(); + public Intent findOrCreateImmutable(final Intent intent) { + if (immutables.containsKey(intent)) { + final Intent immutable = immutables.get(intent); + assert (immutable.getAction().equals(intent.getAction())); + return immutable; + } else { + final Intent immutable = intent.clone(); + immutable.setImmutable(); + immutables.put(intent, immutable); + logger.debug("Now {} immutables", immutables.size()); + return immutable; + } + } public Intent find(final InstanceKey key) throws IndexOutOfBoundsException { if (key == null) {