1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dexfuzz.program.mutators; 18 19 import dexfuzz.Log; 20 import dexfuzz.MutationStats; 21 import dexfuzz.program.MInsn; 22 import dexfuzz.program.MutatableCode; 23 import dexfuzz.program.Mutation; 24 import dexfuzz.rawdex.Instruction; 25 import dexfuzz.rawdex.Instruction.InvokeFormatInfo; 26 import dexfuzz.rawdex.Opcode; 27 28 import java.util.List; 29 import java.util.Random; 30 31 public class NewMethodCaller extends CodeMutator { 32 /** 33 * Every CodeMutator has an AssociatedMutation, representing the 34 * mutation that this CodeMutator can perform, to allow separate 35 * generateMutation() and applyMutation() phases, allowing serialization. 36 */ 37 public static class AssociatedMutation extends Mutation { 38 public enum InvokeType { 39 VIRTUAL, 40 DIRECT, 41 SUPER, 42 STATIC, 43 INTERFACE 44 } 45 46 public int insertionIdx; 47 public InvokeType invokeType; 48 public String className; 49 public String methodName; 50 public String signature; 51 public int numArgs; 52 public int[] args; 53 54 @Override getString()55 public String getString() { 56 StringBuilder argsString = new StringBuilder(); 57 for (int i = 0; i < numArgs; i++) { 58 argsString.append(args[i]); 59 if (i < (numArgs - 1)) { 60 argsString.append(" "); 61 } 62 } 63 String result = String.format("%d %d %s %s %s %d %s", 64 insertionIdx, 65 invokeType.ordinal(), 66 className, 67 methodName, 68 signature, 69 numArgs, 70 argsString); 71 return result; 72 } 73 74 @Override parseString(String[] elements)75 public void parseString(String[] elements) { 76 insertionIdx = Integer.parseInt(elements[2]); 77 invokeType = InvokeType.values()[Integer.parseInt(elements[3])]; 78 className = elements[4]; 79 methodName = elements[5]; 80 signature = elements[6]; 81 numArgs = Integer.parseInt(elements[7]); 82 args = new int[numArgs]; 83 for (int i = 0; i < numArgs; i++) { 84 args[i] = Integer.parseInt(elements[8 + i]); 85 } 86 } 87 } 88 89 // The following two methods are here for the benefit of MutationSerializer, 90 // so it can create a CodeMutator and get the correct associated Mutation, as it 91 // reads in mutations from a dump of mutations. 92 @Override getNewMutation()93 public Mutation getNewMutation() { 94 return new AssociatedMutation(); 95 } 96 NewMethodCaller()97 public NewMethodCaller() { } 98 NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations)99 public NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations) { 100 super(rng, stats, mutations); 101 likelihood = 10; 102 } 103 104 @Override generateMutation(MutatableCode mutatableCode)105 protected Mutation generateMutation(MutatableCode mutatableCode) { 106 // Find the insertion point. 107 int insertionIdx = 0; 108 boolean foundInsn = false; 109 110 while (!foundInsn) { 111 insertionIdx = rng.nextInt(mutatableCode.getInstructionCount()); 112 MInsn insertionPoint = 113 mutatableCode.getInstructionAt(insertionIdx); 114 foundInsn = true; 115 116 // Don't want to insert instructions where there are raw instructions for now. 117 if (insertionPoint.insn.justRaw) { 118 foundInsn = false; 119 } 120 } 121 122 AssociatedMutation mutation = new AssociatedMutation(); 123 mutation.setup(this.getClass(), mutatableCode); 124 mutation.insertionIdx = insertionIdx; 125 126 // TODO: Right now this mutator can only insert calls to System.gc() Add more! 127 128 mutation.invokeType = AssociatedMutation.InvokeType.STATIC; 129 mutation.className = "Ljava/lang/System;"; 130 mutation.methodName = "gc"; 131 mutation.signature = "()V"; 132 mutation.numArgs = 0; 133 134 return mutation; 135 } 136 137 @Override applyMutation(Mutation uncastMutation)138 protected void applyMutation(Mutation uncastMutation) { 139 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 140 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 141 MutatableCode mutatableCode = mutation.mutatableCode; 142 143 MInsn newInsn = new MInsn(); 144 newInsn.insn = new Instruction(); 145 146 switch (mutation.invokeType) { 147 case VIRTUAL: 148 newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL); 149 break; 150 case DIRECT: 151 newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_DIRECT); 152 break; 153 case SUPER: 154 newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_SUPER); 155 break; 156 case STATIC: 157 newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_STATIC); 158 break; 159 case INTERFACE: 160 newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_INTERFACE); 161 break; 162 default: 163 } 164 165 // TODO: Handle more than just static invokes. 166 167 int methodIdx = mutatableCode.program.getNewItemCreator() 168 .findOrCreateMethodId(mutation.className, 169 mutation.methodName, mutation.signature); 170 171 newInsn.insn.vregB = methodIdx; 172 newInsn.insn.invokeFormatInfo = new InvokeFormatInfo(); 173 174 // TODO: More field population, when we call methods that take arguments. 175 176 MInsn insertionPoint = 177 mutatableCode.getInstructionAt(mutation.insertionIdx); 178 179 Log.info(String.format("Called new method %s %s %s, inserting at %s", 180 mutation.className, mutation.methodName, mutation.signature, insertionPoint)); 181 182 stats.incrementStat("Called new method"); 183 184 mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx); 185 } 186 } 187