WALA/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java

454 lines
20 KiB
Java

/*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html.
*
* This file is a derivative of code released under the terms listed below.
*
*/
/*
* Copyright (c) 2013,
* Tobias Blaschke <code@tobiasblaschke.de>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The names of the contributors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa;
import java.util.Map;
import java.util.logging.Logger;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters.StartInfo;
import com.ibm.wala.dalvik.util.AndroidEntryPointManager;
import com.ibm.wala.dalvik.util.AndroidTypes;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ipa.callgraph.ContextSelector;
import com.ibm.wala.ipa.callgraph.propagation.ConstantKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.intset.EmptyIntSet;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetUtil;
/**
* Adds Intents to the Context of functions that start Android-Components.
*
* This is done by remembering all new-sites where Intent-Objects are built and the parameters to its
* Constructor. When a function managed by this Selector (see IntentStarters) is encountered the stored
* information is added to its Context.
*
* @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentContextInterpreter
* @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters
*
* @author Tobias Blaschke <code@tobiasblaschke.de>
* @since 2013-10-14
*/
public class IntentContextSelector implements ContextSelector {
private final IntentMap intents = new IntentMap();
private final ContextSelector parent;
private final IntentStarters intentStarters;
private final Map<InstanceKey, AndroidContext> seenContext;
public IntentContextSelector(final IClassHierarchy cha) {
this(null, cha);
}
/**
* @param parent is always asked to build a Context first. Context generated by this class is added then.
*/
public IntentContextSelector(final ContextSelector parent, final IClassHierarchy cha) {
this.parent = parent;
this.seenContext = HashMapFactory.make();
this.intentStarters = new IntentStarters(cha);
}
/**
* Given a calling node and a call site, returns the Context in which the callee should be evaluated.
*
* {@inheritDoc}
*
* @throws IllegalArgumentException if the type of a parameter given as actualParameters does not match an expected one
*/
@Override
public Context getCalleeTarget(CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] actualParameters) {
Context ctx = null;
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())) {
// Handle startActivity(), startActivityForResult(), startService() and such
// Search Android-Context and attach corresponding WALA-Context
/* {
final InstanceKey self = actualParameters[0];
assert (self != null) : "This-Pointer was not marked as relevant!";
if (seenContext.containsKey(self)) {
ctx = new AndroidContext(ctx, seenContext.get(self).getContextType());
} else {
}
} // */
Intent intent = null;
{ // Seach intent
for (int j = 0; j < actualParameters.length; ++j) {
final InstanceKey param = actualParameters[j];
if (param == null) {
continue;
} else if (param.getConcreteType().getName().equals(AndroidTypes.IntentName)) {
if (! intents.contains(param) ) {
break;
} else {
intent = intents.find(param);
break;
}
}
}
}
// Add the context
if (intent != null) {
AndroidEntryPointManager.MANAGER.addCallSeen(site, intent);
final Intent iintent = intents.findOrCreateImmutable(intent);
return new IntentContext(ctx, iintent);
//return new IntentContext(iintent);
} else {
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;
final InstanceKey param = actualParameters[1];
final Intent intent;
{ // Extract target-Service as intent
if (param instanceof ConstantKey) {
final String target = (String) ((ConstantKey)param).getValue();
intent = new Intent(target) {
@Override
public Intent.IntentType getType() {
return Intent.IntentType.SYSTEM_SERVICE;
}
// TODO override equals and hashCode?
};
} else {
intent = null;
if (param == null) {
} else {
}
}
}
// Add the context
if (intent != null) {
AndroidEntryPointManager.MANAGER.addCallSeen(site, 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)) {
//
// Handle the different Constructors of Intent
//
final InstanceKey self = actualParameters[0];
final Selector calleeSel = callee.getSelector();
boolean isExplicit = false;
final InstanceKey uriKey;
final InstanceKey actionKey;
{ // fetch actionKey, uriKey
switch (callee.getNumberOfParameters()) {
case 1:
actionKey = null;
uriKey = null;
break;
case 2:
if (calleeSel.equals(Selector.make("<init>(Ljava/lang/String;)V"))) {
actionKey = actualParameters[1];
} else if (calleeSel.equals(Selector.make("<init>(Landroid/content/Intent;)V"))) {
final InstanceKey inIntent = actualParameters[1];
if (intents.contains(inIntent)) {
intents.put(self, intents.find(inIntent));
} else {
}
actionKey = null;
} else {
actionKey = null;
}
uriKey = null;
break;
case 3:
if (calleeSel.equals(Selector.make("<init>(Ljava/lang/String;Landroid/net/Uri;)V"))) {
// TODO: Use Information of the URI...
actionKey = actualParameters[1];
uriKey = actualParameters[2];
} else if (calleeSel.equals(Selector.make("<init>(Landroid/content/Context;Ljava/lang/Class;)V"))) {
actionKey = actualParameters[2];
uriKey = null;
isExplicit = true;
} else {
actionKey = null;
uriKey = null;
}
break;
case 5:
if (calleeSel.equals(Selector.make("<init>(Ljava/lang/String;Landroid/net/Uri;Landroid/content/Context;Ljava/lang/Class;)V"))) {
actionKey = actualParameters[4];
uriKey = actualParameters[2];
isExplicit = true;
} else {
actionKey = null;
uriKey = null;
}
break;
default:
actionKey = null;
uriKey = null;
}
} // of fetch actionKey
final Intent intent = intents.findOrCreate(self); // Creates Wala-internal Intent
if (actionKey == null) {
if (isExplicit) {
intent.unbind();
}
} else {
intents.setAction(self, actionKey, isExplicit);
}
//final Intent intent = intents.find(self);
//if (isExplicit && (! intent.isExplicit())) { // Has to check if already explicit as we get here multiple times
// intents.setExplicit(self);
//}
// TODO: Evaluate uriKey
} else if (callee.getSelector().equals(Selector.make("setAction(Ljava/lang/String;)Landroid/content/Intent;")) &&
callee.getDeclaringClass().getName().equals(AndroidTypes.IntentName)) {
final InstanceKey self = actualParameters[0];
final InstanceKey actionKey = actualParameters[1];
final Intent intent = intents.find(self);
if (AndroidEntryPointManager.MANAGER.isAllowIntentRerouting()) {
intents.setAction(self, actionKey, false); // May unbind internally
} else {
intents.unbind(self);
}
} 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);
intent.setExplicit();
intents.unbind(self);
} else if (callee.getSelector().equals(Selector.make("setClass(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;")) ||
callee.getSelector().equals(Selector.make("setClassName(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;")) ||
callee.getSelector().equals(Selector.make("setClassName(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;"))) {
final InstanceKey self = actualParameters[0];
final InstanceKey actionKey = actualParameters[2];
final Intent intent = intents.find(self);
if (AndroidEntryPointManager.MANAGER.isAllowIntentRerouting()) {
intents.setAction(self, actionKey, true);
} else {
intents.unbind(self);
}
} else if (callee.getSelector().equals(Selector.make("fillIn(Landroid/content/Intent;I)I"))) {
// See 'setAction' before... TODO
final InstanceKey self = actualParameters[0];
intents.unbind(self);
} else if (callee.isInit() && callee.getDeclaringClass().getName().equals(AndroidTypes.IntentSenderName)) {
// TODO
} /*else if (site.isSpecial() && callee.getDeclaringClass().getName().equals(
AndroidTypes.ContextWrapperName)) {
final InstanceKey baseKey = actualParameters[1];
final InstanceKey wrapperKey = actualParameters[0];
logger.debug("Handling ContextWrapper(Context base)");
if (seenContext.containsKey(baseKey)) {
seenContext.put(wrapperKey, seenContext.get(baseKey));
} else {
if (baseKey == null) {
} else {
}
}
} else if ((site.isSpecial() && callee.getDeclaringClass().getName().equals(
AndroidTypes.ContextImplName))) {
final InstanceKey self = actualParameters[0];
seenContext.put(self, new AndroidContext(ctx, AndroidTypes.AndroidContextType.CONTEXT_IMPL));
} else if (callee.getDeclaringClass().getName().equals(AndroidTypes.ContextWrapperName) &&
callee.getSelector().equals(Selector.make("attachBaseContext(Landroid/content/Context;)V"))) {
final InstanceKey baseKey = actualParameters[1];
final InstanceKey wrapperKey = actualParameters[0];
logger.debug("Handling ContextWrapper.attachBaseContext(base)");
if (seenContext.containsKey(baseKey)) {
seenContext.put(wrapperKey, seenContext.get(baseKey));
} else {
if (baseKey == null) {
} else {
}
}
} */
return ctx;
}
/**
* Given a calling node and a call site, return the set of parameters based on which this selector may choose
* to specialize contexts.
*
* {@inheritDoc}
*/
@Override
public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) {
IntSet ret;
if (this.parent != null) {
ret = this.parent.getRelevantParameters(caller, site);
} else {
ret = EmptyIntSet.instance;
}
final MethodReference target = site.getDeclaredTarget();
if (intentStarters.isStarter(target)) {
final StartInfo info = intentStarters.getInfo(target);
final int[] relevant = info.getRelevant();
if (relevant != null) {
for (int i = 0; i < relevant.length; ++i) {
ret = IntSetUtil.add(ret, relevant[i]);
}
}
} else if (site.isSpecial() && target.getDeclaringClass().getName().equals(
AndroidTypes.IntentName)) {
final MethodReference mRef = site.getDeclaredTarget();
final int numArgs = mRef.getNumberOfParameters();
// Intent()
// Intent(Intent o)
// Intent(String action)
// Intent(String action, Uri uri)
// Intent(Context packageContext, Class<?> cls)
// Intent(String action, Uri uri, Context packageContext, Class<?> cls)
// Select all params;
switch (numArgs) {
case 0:
return EmptyIntSet.instance;
case 1:
return IntSetUtil.make(new int[] { 0, 1 });
case 2:
return IntSetUtil.make(new int[] { 0, 1, 2 });
case 3:
return IntSetUtil.make(new int[] { 0, 1, 2, 3 });
case 4:
return IntSetUtil.make(new int[] { 0, 1, 2, 3, 4 });
default:
return IntSetUtil.make(new int[] { 0, 1, 2, 3, 4, 5 });
}
} else if (site.isSpecial() && target.getDeclaringClass().getName().equals(
AndroidTypes.IntentSenderName)) {
// public IntentSender(IIntentSender target)
// public IntentSender(IBinder target)
return IntSetUtil.make(new int[] { 0, 1 });
} /*else if (site.isSpecial() && target.getDeclaringClass().getName().equals(
AndroidTypes.ContextWrapperName)) {
return IntSetUtil.make(new int[] { 0, 1 });
} else if ((site.isSpecial() && target.getDeclaringClass().getName().equals(
AndroidTypes.ContextImplName))) {
return IntSetUtil.make(new int[] { 0 });
} else if (target.getDeclaringClass().getName().equals(AndroidTypes.ContextWrapperName) &&
target.getSelector().equals(Selector.make("attachBaseContext(Landroid/content/Context;)V"))) {
logger.debug("Encountered ContextWrapper.attachBaseContext()");
return IntSetUtil.make(new int[] { 0, 1 });
}*/ else if (target.getSelector().equals(Selector.make("getSystemService(Ljava/lang/String;)Ljava/lang/Object;"))) {
return IntSetUtil.make(new int[] { 0, 1 });
} else if (target.getSelector().equals(Selector.make("setAction(Ljava/lang/String;)Landroid/content/Intent;"))) {
return IntSetUtil.make(new int[] { 0, 1 });
} else if (target.getSelector().equals(Selector.make("setComponent(Landroid/content/ComponentName;)Landroid/content/Intent;"))) {
return IntSetUtil.make(new int[] { 0 });
} else if (target.getSelector().equals(Selector.make("setClass(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;"))) {
return IntSetUtil.make(new int[] { 0, 2 });
} else if (target.getSelector().equals(Selector.make("setClassName(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;"))) {
return IntSetUtil.make(new int[] { 0, 2 });
} else if (target.getSelector().equals(Selector.make("setClassName(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;"))) {
return IntSetUtil.make(new int[] { 0, 2 });
}
return ret;
} //else if (site.isSpecial() && target.getDeclaringClass().getName().equals(
}