454 lines
20 KiB
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(
|
|
|
|
}
|