/* * 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) 2009-2012, * * Adam Fuchs <afuchs@cs.umd.edu> * Avik Chaudhuri <avik@cs.umd.edu> * Steve Suh <suhsteve@gmail.com> * * 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 org.scandroid.prefixtransfer; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.propagation.ConstantKey; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.LocalPointerKey; import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; import com.ibm.wala.ipa.callgraph.propagation.ReturnValueKey; import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInvokeInstruction; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.util.intset.OrdinalSet; import com.ibm.wala.util.intset.OrdinalSetMapping; public class StringBuilderUseAnalysis { private static final Logger logger = LoggerFactory.getLogger(StringBuilderUseAnalysis.class); public final Map<ISSABasicBlock, ISSABasicBlock> blockOrdering; private final InstanceKey sbik; private final CGNode node; private final PointerAnalysis<InstanceKey> pa; private final Set<LocalPointerKey> localPointerKeys = new HashSet<LocalPointerKey>(); private final List<SSAInstruction> instructions; public StringBuilderUseAnalysis(final InstanceKey ik, final PointerAnalysis<InstanceKey> pa) throws Exception { assert(ik.getConcreteType().getName().toString().equals("Ljava/lang/StringBuilder")); this.sbik = ik; this.pa = pa; this.node = findCGNode(ik, pa); this.instructions = findInstructions(); final HashSet<ISSABasicBlock> blockSet = new HashSet<ISSABasicBlock>(); for (final SSAInstruction inst : instructions) { blockSet.add(node.getIR().getBasicBlockForInstruction(inst)); } // find the ordering for all of the instructions final BlockSearch blockSearch = new BlockSearch(node.getIR()); final Map<ISSABasicBlock,ISSABasicBlock> blockOrdering = new HashMap<ISSABasicBlock, ISSABasicBlock>(); for (final ISSABasicBlock b : blockSet) { final ISSABasicBlock target = blockSearch.searchFromBlock(b, blockSet); if (target != null) { blockOrdering.put(b, target); } } this.blockOrdering = blockOrdering; } private CGNode findCGNode(final InstanceKey ik, final PointerAnalysis<InstanceKey> pa) throws Exception { CGNode nominatedNode = null; for (final PointerKey pk : pa.getPointerKeys()) { if (pk instanceof LocalPointerKey) { final LocalPointerKey lpk = (LocalPointerKey) pk; if (!lpk.getNode().getMethod().getReference().getDeclaringClass().getClassLoader().equals(ClassLoaderReference.Application)) { continue; } for (final InstanceKey k:pa.getPointsToSet(pk)) { if (k.equals(ik)) { // make sure it's a local pointer key, and make sure that it's in just one cgnode localPointerKeys.add(lpk); if (nominatedNode == null) { nominatedNode = lpk.getNode(); } else if (nominatedNode != lpk.getNode()) { logger.warn("got conflicting nodes: "+nominatedNode+" <> "+lpk.getNode()); return null; } } } } else if (!(pk instanceof ReturnValueKey)) { // if this pointer key points to our instance key then we have to give up -- we can only analyze local pointer keys final OrdinalSet<InstanceKey> pts = pa.getPointsToSet(pk); if (pts.contains(ik)) { logger.warn("Found non LocalPointerKey refering to our ik: " + pk); return null; } } } return nominatedNode; } private List<SSAInstruction> findInstructions() { List<SSAInstruction> instructions = new ArrayList<SSAInstruction>(); if (node != null) { for (SSAInstruction inst: node.getIR().getInstructions()) { if (inst instanceof SSAInvokeInstruction) { if (localPointerKeys.contains(new LocalPointerKey(node,inst.getUse(0)))) { instructions.add(inst); } } } } return instructions; } public Set<InstanceKey> getDeps() { return null; } public class StringBuilderToStringInstanceKeySite extends InstanceKeySite { final ArrayList<Integer> concatenatedInstanceKeys; final int instanceID; StringBuilderToStringInstanceKeySite(final int instanceID, final ArrayList<Integer> concatenatedInstanceKeys) { this.concatenatedInstanceKeys = concatenatedInstanceKeys; this.instanceID = instanceID; } @Override public PrefixVariable propagate(final PrefixVariable input) { // TODO: go through each of the concatenatedInstanceKeys and look up their prefixes in input to figure out the prefix of the string generated by toString() final StringBuffer buf = new StringBuffer(); boolean prefixNew = false; boolean prefixFull = true; for (final Integer i: concatenatedInstanceKeys) { final String prefix = input.getPrefix(i); if (prefix != null) { buf.append(prefix); prefixNew = true; } if (!input.fullPrefixKnown.contains(i)) { prefixFull = false; break; } } final PrefixVariable retVal = new PrefixVariable(); retVal.copyState(input); if (prefixNew) { final String s = buf.toString(); retVal.update(instanceID, s); if (prefixFull) { retVal.include(instanceID); } } return retVal; } public String toString() { return ("StringBuilderToString(instanceID = " + instanceID + "; concatenatedInstanceKeys = " + concatenatedInstanceKeys + ")"); } @Override public int instanceID() { return instanceID; } } public InstanceKeySite getNode(final CallSiteReference csr, final InstanceKey k) { final ISSABasicBlock bbs[] = node.getIR().getBasicBlocksForCall(csr); if (bbs.length != 1) { logger.warn("Got wrong number of basic blocks for call site: " + node.getMethod().getSignature() + " blocks:" + bbs.length); } final OrdinalSetMapping<InstanceKey> mapping = pa.getInstanceKeyMapping(); final HashSet<ISSABasicBlock> blocksSeen = new HashSet<ISSABasicBlock>(); final ArrayList<Integer> concatenatedInstanceKeys = new ArrayList<Integer>(); ISSABasicBlock bPrev = bbs[0]; ISSABasicBlock bNext = blockOrdering.get(bPrev); while (bNext != null) { // detect loops if (blocksSeen.contains(bNext)) { logger.warn("Loop detected in string builder use analysis for " + sbik + "!"); logger.warn("bPrev: " + bPrev); logger.warn("bNext: " + bNext); return null; } blocksSeen.add(bNext); final SSAInvokeInstruction iNext = (SSAInvokeInstruction) bNext.getLastInstruction(); final MethodReference tgt = iNext.getDeclaredTarget(); if ("append".equals(tgt.getName().toString())) { final LocalPointerKey lpk = new LocalPointerKey(node, iNext.getUse(1)); for (final InstanceKey ikey: pa.getPointsToSet(lpk)) { if (isNonNullConstant(ikey)) { concatenatedInstanceKeys.add(0, mapping.getMappedIndex(ikey)); } } } else if (isDefaultConstructor(tgt)) { final LocalPointerKey lpk = new LocalPointerKey(node, iNext.getUse(1)); for (final InstanceKey ikey: pa.getPointsToSet(lpk)) { if (isNonNullConstant(ikey)) { concatenatedInstanceKeys.add(0, mapping.getMappedIndex(ikey)); } } return new StringBuilderToStringInstanceKeySite(mapping.getMappedIndex(k), concatenatedInstanceKeys); } bPrev = bNext; bNext = blockOrdering.get(bNext); } logger.warn("Ran out of parents before getting to <init> on SB: "+ csr + " with builder " + sbik); return null; } private static boolean isDefaultConstructor(final MethodReference mref) { return "<init>".equals(mref.getName().toString()) && mref.getNumberOfParameters() == 0; } private static boolean isNonNullConstant(final InstanceKey ik) { if (ik instanceof ConstantKey<?>) { final ConstantKey<?> ck = (ConstantKey<?>) ik; return ! "null".equals(ck.getValue().toString()); } return false; } }