/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2010, Benedikt Huber <benedikt.huber@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.dfa.analyses; import com.jopdesign.common.AppInfo; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.code.CallString; import com.jopdesign.common.misc.AppInfoError; import com.jopdesign.common.misc.MethodNotFoundException; import com.jopdesign.common.misc.MiscUtils.Query; import com.jopdesign.dfa.DFATool; import com.jopdesign.dfa.framework.Analysis; import com.jopdesign.dfa.framework.AnalysisResultSerialization; import com.jopdesign.dfa.framework.BoundedSetFactory; import com.jopdesign.dfa.framework.BoundedSetFactory.BoundedSet; import com.jopdesign.dfa.framework.Context; import com.jopdesign.dfa.framework.ContextMap; import com.jopdesign.dfa.framework.FlowEdge; import com.jopdesign.dfa.framework.Interpreter; import com.jopdesign.dfa.framework.MethodHelper; import org.apache.bcel.Constants; import org.apache.bcel.classfile.LineNumberTable; import org.apache.bcel.generic.AASTORE; import org.apache.bcel.generic.ARRAYLENGTH; import org.apache.bcel.generic.ArrayInstruction; import org.apache.bcel.generic.FieldInstruction; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.GETSTATIC; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InvokeInstruction; import org.apache.bcel.generic.LDC; import org.apache.bcel.generic.LoadInstruction; import org.apache.bcel.generic.PUTFIELD; import org.apache.bcel.generic.PUTSTATIC; import org.apache.bcel.generic.ReferenceType; import org.apache.bcel.generic.StoreInstruction; import org.apache.bcel.generic.Type; import org.apache.log4j.Logger; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; public class SymbolicPointsTo implements Analysis<CallString, SymbolicAddressMap> { private static final boolean DEBUG_PRINT = false; // Set this to true to see how good one could get //private static final boolean ASSUME_NO_ALIASING = true; // Set this to true to see how good one could get private static final boolean ASSUME_NO_CONC = true; public static final String NAME = "SymbolicPointsTo"; private static final Logger logger = Logger.getLogger(DFATool.LOG_DFA_ANALYSES+"."+NAME); private BoundedSetFactory<SymbolicAddress> bsFactory; private final int callStringLength; private MethodInfo entryMethod; private HashMap<InstructionHandle, ContextMap<CallString, BoundedSet<SymbolicAddress>>> usedRefs = new HashMap<InstructionHandle, ContextMap<CallString,BoundedSet<SymbolicAddress>>>(); private Query<InstructionHandle> executedOnce; private CallString initialCallString; // optional extra info: Max flow for each instruction handle public SymbolicPointsTo(int maxSetSize, int callStringLength) { bsFactory = new BoundedSetFactory<SymbolicAddress>(maxSetSize); this.callStringLength = callStringLength; executedOnce = new Query<InstructionHandle>() { public boolean query(InstructionHandle a) { return false; } }; } public String getId() { return NAME + "-" + callStringLength; } public SymbolicPointsTo(int maxSetSize, int callStringLength, Query<InstructionHandle> eoAna) { bsFactory = new BoundedSetFactory<SymbolicAddress>(maxSetSize); this.callStringLength = callStringLength; executedOnce = eoAna; } public ContextMap<CallString, SymbolicAddressMap> bottom() { return null; } /* A `compare` B <=> contexts are equals and A subseteq B */ public boolean compare(ContextMap<CallString, SymbolicAddressMap> s1, ContextMap<CallString, SymbolicAddressMap> s2) { /* If either is undefined, order is not defined */ if (s1 == null || s2 == null) return false; /* Get context */ Context context = s1.getContext(); /* If context do not match, partial order is not defined */ if (!context.equals(s2.getContext())) return false; SymbolicAddressMap a = s1.get(context.callString); SymbolicAddressMap b = s2.get(context.callString); /* If either is undefined, partial order is not defined */ if (a == null || b == null) return false; /* The actual comparison */ return a.isSubset(b); } public ContextMap<CallString, SymbolicAddressMap> initial(InstructionHandle stmt) { ContextMap<CallString, SymbolicAddressMap> retval = new ContextMap<CallString, SymbolicAddressMap>( new Context(), new HashMap<CallString, SymbolicAddressMap>()); SymbolicAddressMap init = new SymbolicAddressMap(bsFactory); // Add symbolic stack names int stackPtr = 0; if(! entryMethod.isStatic()) { init.putStack(stackPtr++, bsFactory.singleton(SymbolicAddress.rootAddress("$this"))); } String[] args = entryMethod.getArgumentNames(); for(int i = 0; i < args.length; i++) { Type ty = entryMethod.getArgumentType(i); if(ty instanceof ReferenceType) { init.putStack(stackPtr, bsFactory.singleton(SymbolicAddress.rootAddress("$"+args[i]))); } stackPtr += ty.getSize(); } retval.put(initialCallString, init); return retval; } public void initialize(MethodInfo mi, Context context) { entryMethod = mi; initialCallString = context.callString; } public ContextMap<CallString, SymbolicAddressMap> join( ContextMap<CallString, SymbolicAddressMap> s1, ContextMap<CallString, SymbolicAddressMap> s2) { if (s1 == null) { return new ContextMap<CallString, SymbolicAddressMap>(s2); } if (s2 == null) { return new ContextMap<CallString, SymbolicAddressMap>(s1); } // create empty context map, with s1's context ContextMap<CallString, SymbolicAddressMap> result = new ContextMap<CallString, SymbolicAddressMap>(new Context(s1.getContext()), new HashMap<CallString, SymbolicAddressMap>()); // add both context maps. Note that entries from s2 overwrite those from the s1 result.putAll(s1); result.putAll(s2); // a and b are the DF results for the respectively active contexts SymbolicAddressMap a = s1.get(s1.getContext().callString); SymbolicAddressMap b = s2.get(s2.getContext().callString); SymbolicAddressMap merged = a.clone(); merged.join(b); result.put(s2.getContext().callString, merged); if (result.getContext().stackPtr < 0) { result.getContext().stackPtr = s2.getContext().stackPtr; } if (result.getContext().syncLevel < 0) { result.getContext().syncLevel = s2.getContext().syncLevel; } result.getContext().threaded = Context.isThreaded(); // System.out.println("R: "+result); return result; } public ContextMap<CallString, SymbolicAddressMap> transfer( InstructionHandle stmt, FlowEdge edge, ContextMap<CallString, SymbolicAddressMap> input, Interpreter<CallString, SymbolicAddressMap> interpreter, Map<InstructionHandle, ContextMap<CallString, SymbolicAddressMap>> state) { Context context = new Context(input.getContext()); if(DEBUG_PRINT) { System.out.println("[S] "+context.callString.toStringList()+": "+context.method()+" / "+stmt); } SymbolicAddressMap in = input.get(context.callString); ContextMap<CallString, SymbolicAddressMap> retval = new ContextMap<CallString, SymbolicAddressMap>(context, input); Instruction instruction = stmt.getInstruction(); int newStackPtr = context.stackPtr + instruction.produceStack(context.constPool()) - instruction.consumeStack(context.constPool()); int opcode = instruction.getOpcode(); switch (opcode) { /* Constants (boring) */ case Constants.DCONST_0: case Constants.DCONST_1: case Constants.LCONST_0: case Constants.LCONST_1 : /* Instructions above need two stack slots */ case Constants.FCONST_0: case Constants.FCONST_1: case Constants.FCONST_2: case Constants.ICONST_M1: case Constants.ICONST_0: case Constants.ICONST_1: case Constants.ICONST_2: case Constants.ICONST_3: case Constants.ICONST_4: case Constants.ICONST_5: case Constants.BIPUSH: case Constants.SIPUSH: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.ACONST_NULL: { // Null -> No reference SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); result.putStack(newStackPtr-1, bsFactory.empty()); retval.put(context.callString, result); } break; /* Long/Double Constants */ case Constants.LDC2_W: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; /* Int/Float/String Constants */ case Constants.LDC: case Constants.LDC_W: { LDC ldc = (LDC) instruction; Type t = ldc.getType(context.constPool()); if(t instanceof ReferenceType) { SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); /* FIXME: This is overly conservative, but class pointer not available here */ String classContext = context.getMethodInfo().getMemberID().toString(); SymbolicAddress addr = SymbolicAddress.stringLiteral(classContext,ldc.getIndex()); result.putStack(newStackPtr-1, bsFactory.singleton(addr )); retval.put(context.callString, result); } else { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } } break; case Constants.DSTORE: case Constants.DSTORE_0: case Constants.DSTORE_1: case Constants.DSTORE_2: case Constants.DSTORE_3: case Constants.LSTORE: case Constants.LSTORE_0: case Constants.LSTORE_1: case Constants.LSTORE_2: case Constants.LSTORE_3: case Constants.FSTORE: case Constants.FSTORE_0: case Constants.FSTORE_1: case Constants.FSTORE_2: case Constants.FSTORE_3: case Constants.ISTORE_0: case Constants.ISTORE_1: case Constants.ISTORE_2: case Constants.ISTORE_3: case Constants.ISTORE: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; // case Constants.ASTORE_0: case Constants.ASTORE_1: case Constants.ASTORE_2: case Constants.ASTORE_3: case Constants.ASTORE: { // copy value to local variable StoreInstruction instr = (StoreInstruction)instruction; SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); if(DEBUG_PRINT) { System.out.println(String.format("[DD] Copy: stack[%d] <- stack[%d]", instr.getIndex(), context.stackPtr-1)); } result.copyStack(in, instr.getIndex(), context.stackPtr-1); retval.put(context.callString, result); } break; /* Load variables (boring) */ case Constants.DLOAD_0: case Constants.DLOAD_1: case Constants.DLOAD_2: case Constants.DLOAD_3: case Constants.DLOAD: case Constants.LLOAD_0: case Constants.LLOAD_1: case Constants.LLOAD_2: case Constants.LLOAD_3: case Constants.LLOAD: /* Instructions above need two stack slots */ case Constants.FLOAD_0: case Constants.FLOAD_1: case Constants.FLOAD_2: case Constants.FLOAD_3: case Constants.FLOAD: case Constants.ILOAD_0: case Constants.ILOAD_1: case Constants.ILOAD_2: case Constants.ILOAD_3: case Constants.ILOAD: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; /* Floating Point Comparison (boring) */ case Constants.DCMPG: case Constants.DCMPL: case Constants.LCMP: /* Instructions above need two stack slots */ case Constants.FCMPG: case Constants.FCMPL:{ retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.ALOAD_0: case Constants.ALOAD_1: case Constants.ALOAD_2: case Constants.ALOAD_3: case Constants.ALOAD: { LoadInstruction instr = (LoadInstruction)instruction; // copy value from local variable SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); result.copyStack(in, context.stackPtr, instr.getIndex()); retval.put(context.callString, result); } break; // Access Object Handle (area), top of stack case Constants.ARRAYLENGTH: { putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-1)); retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; // Access Object Handle, second on stack case Constants.PUTFIELD: { PUTFIELD instr = (PUTFIELD)instruction; putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-1-instr.getType(context.constPool()).getSize())); SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); // Change alias information if(instr.getFieldType(context.constPool()) instanceof ReferenceType) { String ty = instr.getFieldType(context.constPool()).getSignature(); result.addAlias(ty, in.getStack(context.stackPtr-1)); } retval.put(context.callString, result); } break; // Access Object Handle, top of stack case Constants.GETFIELD: { putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-1)); GETFIELD instr = (GETFIELD)instruction; SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); if(instr.getFieldType(context.constPool()) instanceof ReferenceType) { BoundedSet<SymbolicAddress> newMapping = SymbolicAddress.fieldAccess(bsFactory, in.getStack(context.stackPtr-1), instr.getFieldName(context.constPool())); newMapping.addAll(in.getAliases(instr.getFieldType(context.constPool()).getSignature())); result.putStack(context.stackPtr-1, newMapping); } retval.put(context.callString, result); } break; // Put object is on top of stack // Handles the same way as MOV case Constants.PUTSTATIC: { PUTSTATIC instr = (PUTSTATIC)instruction; SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); if(instr.getFieldType(context.constPool()) instanceof ReferenceType) { BoundedSet<SymbolicAddress> pointers = in.getStack(context.stackPtr - 1); result.putHeap(fieldSignature(context, instr), pointers); } retval.put(context.callString, result); } break; // Assign TOS the field value // Handled the same as MOV case Constants.GETSTATIC: { GETSTATIC instr = (GETSTATIC)instruction; SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); if(instr.getFieldType(context.constPool()) instanceof ReferenceType) { result.putStack(context.stackPtr, in.getHeap(fieldSignature(context, instr))); } retval.put(context.callString, result); } break; case Constants.LASTORE: case Constants.DASTORE: case Constants.IASTORE: case Constants.FASTORE: case Constants.CASTORE: case Constants.SASTORE: case Constants.BASTORE: { int offset = 3; if(opcode == Constants.LASTORE || opcode == Constants.DASTORE) offset=4; putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-offset)); retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; // changing the heap -> TOP case Constants.AASTORE: { putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-3)); AASTORE instr = (AASTORE) stmt.getInstruction(); SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); // Change alias information if(instr.getType(context.constPool()) instanceof ReferenceType) { String ty = instr.getType(context.constPool()).getSignature(); result.addAlias(ty, in.getStack(context.stackPtr-1)); } retval.put(context.callString, result); } break; case Constants.DALOAD: case Constants.LALOAD: case Constants.IALOAD: case Constants.FALOAD: case Constants.CALOAD: case Constants.SALOAD: case Constants.BALOAD: { putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-2)); retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; // AALOAD objectref, index -> objectref // TODO: Use index info case Constants.AALOAD: { putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-2)); //AALOAD instr = (AALOAD)instruction; SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); BoundedSet<SymbolicAddress> objectMapping = in.getStack(context.stackPtr-2); BoundedSet<SymbolicAddress> newMapping; LoopBounds bounds = interpreter.getDFATool().getLoopBounds(); if(executedOnce.query(stmt)) { newMapping = bsFactory.singleton(SymbolicAddress.newName()); } else if(objectMapping.isSaturated() || bounds == null) { newMapping = bsFactory.top(); } else { Interval interval = bounds.getArrayIndices(stmt, context.callString); if(interval.hasLb() && interval.hasUb()) { newMapping = bsFactory.empty(); for(SymbolicAddress addr: objectMapping.getSet()) { for(int i = interval.getLb(); i <= interval.getUb(); i++) { newMapping.add(addr.accessArray(i)); } } } else { newMapping = bsFactory.top(); } } // Doesn't work, but is probably stupid anyway :( // LoopBounds bounds = interpreter.getDFATool().getLoopBounds(); // bounds.getArraySizes().get(stmt).get(context.callString); // newMapping = bsFactory.empty(); // Interval[] sizeBounds = { new Interval(2,3) }; // for(Interval i : sizeBounds) { // if(! i.hasUb()) { // newMapping = bsFactory.top(); // } // int ub = i.getUb(); // for(int j = 0; j <= ub; j++) { // for(SymbolicAddress addr: objectMapping.getSet()) { // newMapping.add(addr.accessArray(j)); // } // } // } // } result.putStack(context.stackPtr-2, newMapping); retval.put(context.callString, result); } break; case Constants.DUP: { // copy value on stack SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); result.copyStack(in, context.stackPtr, context.stackPtr-1); retval.put(context.callString, result); } break; case Constants.DUP_X1: { // copy value on stack SymbolicAddressMap result = in.cloneFilterStack(context.stackPtr-2); result.copyStack(in, context.stackPtr-2, context.stackPtr-1); result.copyStack(in, context.stackPtr-1, context.stackPtr-2); result.copyStack(in, context.stackPtr, context.stackPtr-1); retval.put(context.callString, result); } break; case Constants.DUP_X2: { // copy value on stack SymbolicAddressMap result = in.cloneFilterStack(context.stackPtr-3); result.copyStack(in, context.stackPtr-3, context.stackPtr-1); result.copyStack(in, context.stackPtr-2, context.stackPtr-3); result.copyStack(in, context.stackPtr-1, context.stackPtr-2); result.copyStack(in, context.stackPtr, context.stackPtr-1); retval.put(context.callString, result); } break; case Constants.DUP2: { // v1,v2 -> v1,v2,v1,v2 SymbolicAddressMap result = in.cloneFilterStack(context.stackPtr); result.copyStack(in, context.stackPtr, context.stackPtr-2); result.copyStack(in, context.stackPtr+1, context.stackPtr-1); retval.put(context.callString, result); } break; case Constants.POP: case Constants.POP2: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.IINC: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.IADD: case Constants.ISUB: case Constants.INEG: case Constants.IUSHR: case Constants.ISHR: case Constants.IAND: case Constants.IOR: case Constants.IXOR: case Constants.IMUL: case Constants.IDIV: case Constants.IREM: case Constants.ISHL: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; /* Long,Float and Double operations (boring) */ case Constants.DADD: case Constants.DSUB: case Constants.DMUL: case Constants.DDIV: case Constants.DREM: case Constants.LADD: case Constants.LSUB: case Constants.LUSHR: case Constants.LSHR: case Constants.LAND: case Constants.LOR: case Constants.LXOR: case Constants.LMUL: case Constants.LDIV: case Constants.LREM: case Constants.LSHL: /* Instructions above need two stack slots */ case Constants.DNEG: case Constants.LNEG: case Constants.FADD: case Constants.FSUB: case Constants.FNEG: case Constants.FMUL: case Constants.FDIV: case Constants.FREM: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; /* Conversion of primitives (boring) */ case Constants.D2F: case Constants.D2I: /* Instructions above need two stack slots */ case Constants.D2L: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.L2F: case Constants.L2I: /* Instructions above need two stack slots */ case Constants.L2D: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.F2D: case Constants.I2D: case Constants.F2L: case Constants.I2L: /* Instructions above need one stack slot less */ case Constants.F2I: case Constants.I2B: case Constants.I2C: case Constants.I2F: case Constants.I2S:{ retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.MONITORENTER: // not supported yet if(ASSUME_NO_CONC) retval.put(context.callString, in.cloneFilterStack(newStackPtr)); else retval.put(context.callString, SymbolicAddressMap.top()); break; case Constants.MONITOREXIT: // not supported yet retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; case Constants.CHECKCAST: retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; case Constants.INSTANCEOF: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.NEW: case Constants.NEWARRAY: case Constants.ANEWARRAY: { SymbolicAddressMap result = in.cloneFilterStack(newStackPtr); BoundedSet<SymbolicAddress> newMapping; if(executedOnce.query(stmt)) { newMapping = bsFactory.singleton(SymbolicAddress.newName()); } else { newMapping = bsFactory.top(); } int objPtr = (instruction.getOpcode() == Constants.NEW) ? context.stackPtr: (context.stackPtr-1); result.putStack(objPtr, newMapping); retval.put(context.callString, result); } break; case Constants.MULTIANEWARRAY: { // not supported yet retval.put(context.callString, SymbolicAddressMap.top()); } break; case Constants.GOTO: retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; case Constants.IFNULL: case Constants.IFNONNULL: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.IF_ACMPEQ: case Constants.IF_ACMPNE: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; case Constants.IFEQ: case Constants.IFNE: case Constants.IFLT: case Constants.IFGE: case Constants.IFLE: case Constants.IFGT: retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; case Constants.IF_ICMPEQ: case Constants.IF_ICMPNE: case Constants.IF_ICMPLT: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT: case Constants.IF_ICMPLE: retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; case Constants.LOOKUPSWITCH: case Constants.TABLESWITCH: retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; case Constants.INVOKEVIRTUAL: case Constants.INVOKEINTERFACE: case Constants.INVOKESTATIC: case Constants.INVOKESPECIAL: { DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); retval.put(context.callString, new SymbolicAddressMap(bsFactory)); if (receivers == null || receivers.size() == 0) { String errMsg = String.format("%s : invoke %s: %s receivers", context.method(), instruction.toString(context.constPool().getConstantPool()), (receivers == null ? "Unknown" : "No")); // Get static receivers (FIXME: this just workarounds DFA bugs) if(opcode == Constants.INVOKESTATIC) { receivers = new HashSet<String>(); InvokeInstruction invInstruction = (InvokeInstruction) instruction; String klass = invInstruction.getClassName(context.constPool()); String name = invInstruction.getMethodName(context.constPool()); String sig = invInstruction.getSignature(context.constPool()); String recv = klass+"."+name+sig; Logger.getLogger(this.getClass()).info("Using static receiver: "+recv); receivers.add(recv); } else { Logger.getLogger(this.getClass()).error(errMsg); throw new AssertionError(errMsg); } } if( instruction.getOpcode() == Constants.INVOKEVIRTUAL || instruction.getOpcode() == Constants.INVOKEINTERFACE) { MethodInfo mi = p.getMethod(receivers.iterator().next()); int refPos = MethodHelper.getArgSize(mi); // System.out.println(String.format("%s: args+1: %d; stack[%d] %s", // mi.methodId,refPos,context.stackPtr,input.get(context.callString))); try { putResult(stmt, context, input.get(context.callString).getStack(context.stackPtr-refPos)); } catch(Error e) { System.err.println("We have problems with method "+mi); System.err.println(e.getMessage()); } } for (String methodName : receivers) { doInvoke(methodName, stmt, context, input, interpreter, state, retval); } } break; case Constants.ARETURN: { SymbolicAddressMap result = in.cloneFilterStack(0); // store results result.copyStack(in, 0, context.stackPtr-1); retval.put(context.callString, result); } break; /* The values of other return statements are not of interest here */ case Constants.DRETURN: case Constants.LRETURN: case Constants.FRETURN: case Constants.IRETURN: case Constants.RETURN: { retval.put(context.callString, in.cloneFilterStack(newStackPtr)); } break; default: System.err.println("unknown instruction: "+stmt+" in method "+context.method()); retval.put(context.callString, in.cloneFilterStack(newStackPtr)); break; } // DEBUGGING if(DEBUG_PRINT) { System.out.println("[F] "+context.method()+" / "+stmt); System.out.println(" Stackptr: "+context.stackPtr + " -> "+newStackPtr); System.out.println(" Before: "); input.get(context.callString).print(System.out, 4); System.out.println(" After: "); retval.get(context.callString).print(System.out, 4); } context.stackPtr = newStackPtr; return retval; } private String fieldSignature(Context context, FieldInstruction instr) { return instr.getClassName(context.constPool()) + instr.getFieldName(context.constPool()); } /** Save used object handle */ private void putResult(InstructionHandle stmt, Context ctx, BoundedSet<SymbolicAddress> stackEntry) { if(! usedRefs.containsKey(stmt)) { usedRefs.put(stmt, new ContextMap<CallString, BoundedSet<SymbolicAddress>>(ctx, new HashMap<CallString, BoundedSet<SymbolicAddress>>())); } usedRefs.get(stmt).put(ctx.callString,stackEntry); } private void doInvoke(String methodName, InstructionHandle stmt, Context context, ContextMap<CallString,SymbolicAddressMap> input, Interpreter<CallString,SymbolicAddressMap> interpreter, Map<InstructionHandle, ContextMap<CallString, SymbolicAddressMap>> state, ContextMap<CallString, SymbolicAddressMap> retval) { DFATool p = interpreter.getDFATool(); MethodInfo method = p.getMethod(methodName); //methodName = method.getMemberID().toString(); // System.out.println(stmt+" invokes method: "+methodName); if (method.isNative()) { handleNative(method, context, input, retval); } else { // set up new context int varPtr = context.stackPtr - MethodHelper.getArgSize(method); Context c = new Context(context); c.stackPtr = method.getCode().getMaxLocals(); if (method.isSynchronized()) { c.syncLevel = context.syncLevel+1; } c.setMethodInfo(method); c.callString = c.callString.push(method, stmt, callStringLength); // carry only minimal information with call SymbolicAddressMap in = input.get(context.callString); SymbolicAddressMap out = in.cloneInvoke(varPtr); HashMap<CallString, SymbolicAddressMap> initialMap = new HashMap<CallString, SymbolicAddressMap>(); ContextMap<CallString, SymbolicAddressMap> tmpresult = new ContextMap<CallString, SymbolicAddressMap>(c, initialMap); tmpresult.put(c.callString, out); InstructionHandle entry = p.getEntryHandle(method); state.put(entry, join(state.get(entry), tmpresult)); if(DEBUG_PRINT) { System.out.println("[I] Invoke: "+method.getMemberID()); System.out.println(String.format(" StackPtr: %d, framePtr: %d, args: %d", context.stackPtr, varPtr, MethodHelper.getArgSize(method))); } // interpret method Map<InstructionHandle, ContextMap<CallString, SymbolicAddressMap>> r = interpreter.interpret(c, entry, state, false); SymbolicAddressMap ctxInfo = retval.get(context.callString); // pull out relevant information from call InstructionHandle exit = p.getExitHandle(method); if(r.get(exit) != null) { SymbolicAddressMap returned = r.get(exit).get(c.callString); if (returned != null) { ctxInfo.joinReturned(returned, varPtr); } else { System.err.println("doInvoke(): No exit information for callstring ?"); } } else { System.err.println("Symbolic Points To[doInvoke()]: No exit information from "+methodName+"?"); } // add relevant information to result ctxInfo.addStackUpto(in, context.stackPtr - MethodHelper.getArgSize(method)); if(DEBUG_PRINT) { System.out.println("[R] Invoke: "+method.getMemberID()); System.out.println(String.format(" StackPtr: %d, framePtr: %d, args: %d", context.stackPtr, varPtr, MethodHelper.getArgSize(method))); } } } @SuppressWarnings({"LiteralAsArgToStringEquals"}) private Map<CallString, SymbolicAddressMap> handleNative(MethodInfo method, Context context, ContextMap<CallString,SymbolicAddressMap> input, ContextMap<CallString,SymbolicAddressMap> retval) { String methodId = method.getMemberID().toString(false); SymbolicAddressMap in = input.get(context.callString); SymbolicAddressMap out; int nextStackPtr = context.stackPtr-1; if (methodId.equals("com.jopdesign.sys.Native.rd(I)I") || methodId.equals("com.jopdesign.sys.Native.rdMem(I)I") || methodId.equals("com.jopdesign.sys.Native.rdIntMem(I)I")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.wr(II)V") || methodId.equals("com.jopdesign.sys.Native.wrMem(II)V") || methodId.equals("com.jopdesign.sys.Native.wrIntMem(II)V")) { out = in.cloneFilterStack(nextStackPtr-2); } else if (methodId.equals("com.jopdesign.sys.Native.toInt(Ljava/lang/Object;)I")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.toInt(F)I")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.toInt(Ljava/lang/Object;)I")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.toInt(Ljava/lang/Object;)I")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.toLong(D)J")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.toDouble(J)D")) { out = in.cloneFilterStack(nextStackPtr); } else if (methodId.equals("com.jopdesign.sys.Native.toObject(I)Ljava/lang/Object;") || methodId.equals("com.jopdesign.sys.Native.toIntArray(I)[I")) { out = in.cloneFilterStack(nextStackPtr); out.putStack(context.stackPtr - 1, bsFactory.top()); } else if (methodId.equals("com.jopdesign.sys.Native.getSP()I")) { out = in.cloneFilterStack(nextStackPtr+1); } else if (methodId.equals("com.jopdesign.sys.Native.getField(II)I")) { out = in.cloneFilterStack(nextStackPtr-1); } else if(methodId.equals("com.jopdesign.sys.Native.putField(III)V")) { out = in.cloneFilterStack(nextStackPtr-3); } else if (methodId.equals("com.jopdesign.sys.Native.condMove(IIZ)I")) { out = in.cloneFilterStack(nextStackPtr-2); } else if(methodId.equals("com.jopdesign.sys.Native.arrayLoad(II)I")) { out = in.cloneFilterStack(nextStackPtr-1); } else if(methodId.equals("com.jopdesign.sys.Native.arrayStore(III)V")) { out = in.cloneFilterStack(nextStackPtr-3); } else if(methodId.equals("com.jopdesign.sys.Native.condMoveRef(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;")) { out = in.cloneFilterStack(nextStackPtr-2); BoundedSet<SymbolicAddress> joined = in.getStack(context.stackPtr-3).join(in.getStack(context.stackPtr-2)); out.putStack(context.stackPtr-3, joined); } else { out = null; RuntimeException ex = new RuntimeException("Unknown native method: " + methodId); Logger.getLogger(this.getClass()).error(ex); throw ex; } retval.put(context.callString, out); return retval; } public void printResult(DFATool program) { Map<String,String> getFields = new TreeMap<String,String>(); for(InstructionHandle instr : usedRefs.keySet()) { ContextMap<CallString, BoundedSet<SymbolicAddress>> r = usedRefs.get(instr); Context c = r.getContext(); MethodInfo method = c.getMethodInfo(); if(method == null) { throw new AssertionError("Internal Error: No method '"+c.method()+"'"); } LineNumberTable lines = method.getCode().getLineNumberTable(); int sourceLine = lines.getSourceLine(instr.getPosition()); for (CallString callString : r.keySet()) { System.out.println(c.method() + ":" + sourceLine + ":" + callString + ": " + instr); BoundedSet<SymbolicAddress> symAddr = r.get(callString); String infoStr; if (instr.getInstruction() instanceof GETFIELD) { GETFIELD gfInstr = (GETFIELD) instr.getInstruction(); infoStr = String.format("GETFIELD %s %s %s", symAddr.toString(), gfInstr.getFieldName(c.constPool()), gfInstr.getFieldType(c.constPool())); } else if (instr.getInstruction() instanceof ARRAYLENGTH) { infoStr = String.format("ARRAYLENGTH %s", symAddr.toString()); } else if (instr.getInstruction() instanceof ArrayInstruction) { ArrayInstruction aInstr = (ArrayInstruction) instr.getInstruction(); infoStr = String.format("%s %s %s[]", aInstr.getName().toUpperCase(), symAddr.toString(), aInstr.getType(c.constPool())); } else { infoStr = String.format("%s %s", instr.getInstruction().getName().toUpperCase(), symAddr.toString()); } if (infoStr != null) { String infoKey = String.format("%s:%04d:%s", c.method(), sourceLine, callString); while (getFields.containsKey(infoKey)) infoKey += "'"; getFields.put(infoKey, infoStr); } } } for(Entry<String, String> entry : getFields.entrySet()) { System.out.println(entry.getKey()); System.out.println(" "+entry.getValue()); } } /** Return symbolic object names used at instructions which use a handle * <ul> * <li/> getfield (top of stack) * <li/> putfield (second on stack) * <li/> arraylen (top of stack, handle area) * <li/> a*load (second on stack) * <li/> a*store (third on stack) * </ul> */ public HashMap<InstructionHandle, ContextMap<CallString, BoundedSet<SymbolicAddress>>> getResult() { return usedRefs; } @Override public void serializeResult(File cacheFile) throws IOException { AnalysisResultSerialization.fromContextMapResult(getResult()).serialize(cacheFile); } @Override public Map deSerializeResult(AppInfo appInfo, File cacheFile) throws IOException, ClassNotFoundException, MethodNotFoundException { return AnalysisResultSerialization.fromSerialization(cacheFile).toContextMapResult(appInfo, null); } @Override public void copyResults(MethodInfo newContainer, Map<InstructionHandle, InstructionHandle> newHandles) { throw new AppInfoError("Not yet implemented!"); } }