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 17package dexfuzz.program.mutators; 18 19import dexfuzz.Log; 20import dexfuzz.MutationStats; 21import dexfuzz.program.MInsn; 22import dexfuzz.program.MutatableCode; 23import dexfuzz.program.Mutation; 24import dexfuzz.rawdex.Instruction; 25import dexfuzz.rawdex.Opcode; 26 27import java.util.List; 28import java.util.Random; 29 30public class ValuePrinter extends CodeMutator { 31 /** 32 * Every CodeMutator has an AssociatedMutation, representing the 33 * mutation that this CodeMutator can perform, to allow separate 34 * generateMutation() and applyMutation() phases, allowing serialization. 35 */ 36 public static class AssociatedMutation extends Mutation { 37 public int printedOutputIdx; 38 39 @Override 40 public String getString() { 41 return Integer.toString(printedOutputIdx); 42 } 43 44 @Override 45 public void parseString(String[] elements) { 46 printedOutputIdx = Integer.parseInt(elements[2]); 47 } 48 } 49 50 // The following two methods are here for the benefit of MutationSerializer, 51 // so it can create a CodeMutator and get the correct associated Mutation, as it 52 // reads in mutations from a dump of mutations. 53 @Override 54 public Mutation getNewMutation() { 55 return new AssociatedMutation(); 56 } 57 58 public ValuePrinter() { } 59 60 public ValuePrinter(Random rng, MutationStats stats, List<Mutation> mutations) { 61 super(rng, stats, mutations); 62 likelihood = 40; 63 } 64 65 @Override 66 protected boolean canMutate(MutatableCode mutatableCode) { 67 for (MInsn mInsn : mutatableCode.getInstructions()) { 68 if (getInstructionOutputType(mInsn) != OutputType.UNKNOWN) { 69 return true; 70 } 71 } 72 73 Log.debug("No instructions with legible output in method, skipping."); 74 return false; 75 } 76 77 @Override 78 protected Mutation generateMutation(MutatableCode mutatableCode) { 79 // Find an instruction whose output we wish to print. 80 int printedOutputIdx = 0; 81 boolean foundInsn = false; 82 83 while (!foundInsn) { 84 printedOutputIdx = rng.nextInt(mutatableCode.getInstructionCount()); 85 MInsn insnOutputToPrint = 86 mutatableCode.getInstructionAt(printedOutputIdx); 87 foundInsn = true; 88 89 // Don't want to insert instructions where there are raw instructions for now. 90 if (insnOutputToPrint.insn.justRaw) { 91 foundInsn = false; 92 } 93 94 if (getInstructionOutputType(insnOutputToPrint) == OutputType.UNKNOWN) { 95 foundInsn = false; 96 } 97 } 98 99 AssociatedMutation mutation = new AssociatedMutation(); 100 mutation.setup(this.getClass(), mutatableCode); 101 mutation.printedOutputIdx = printedOutputIdx; 102 return mutation; 103 } 104 105 @Override 106 protected void applyMutation(Mutation uncastMutation) { 107 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 108 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 109 MutatableCode mutatableCode = mutation.mutatableCode; 110 111 MInsn insnOutputToPrint = 112 mutatableCode.getInstructionAt(mutation.printedOutputIdx); 113 114 int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId( 115 "Ljava/lang/System;", 116 "Ljava/io/PrintStream;", 117 "out"); 118 119 OutputType outputType = getInstructionOutputType(insnOutputToPrint); 120 121 if (outputType == OutputType.UNKNOWN) { 122 Log.errorAndQuit("Requested to print output of an instruction, whose output" 123 + " type is unknown."); 124 } 125 int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId( 126 "Ljava/io/PrintStream;", 127 "print", 128 outputType.getSignatureForPrintln()); 129 130 boolean isWide = false; 131 boolean isRef = false; 132 if (outputType == OutputType.LONG || outputType == OutputType.DOUBLE) { 133 isWide = true; 134 } 135 if (outputType == OutputType.STRING) { 136 isRef = true; 137 } 138 139 // If we're printing a wide value, we need to allocate 3 registers! 140 if (isWide) { 141 mutatableCode.allocateTemporaryVRegs(3); 142 } else { 143 mutatableCode.allocateTemporaryVRegs(2); 144 } 145 146 int streamRegister = mutatableCode.getTemporaryVReg(0); 147 int valueRegister = mutatableCode.getTemporaryVReg(1); 148 149 // Copy the value we want to print to the 2nd temporary register 150 // Then load the out stream 151 // Then call print(out stream, value) 152 153 MInsn valueCopyInsn = new MInsn(); 154 valueCopyInsn.insn = new Instruction(); 155 if (isRef) { 156 valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16); 157 } else if (isWide) { 158 valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16); 159 } else { 160 valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16); 161 } 162 valueCopyInsn.insn.vregB = insnOutputToPrint.insn.vregA; 163 valueCopyInsn.insn.vregA = valueRegister; 164 165 MInsn streamLoadInsn = new MInsn(); 166 streamLoadInsn.insn = new Instruction(); 167 streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT); 168 streamLoadInsn.insn.vregB = outFieldIdx; 169 streamLoadInsn.insn.vregA = streamRegister; 170 171 MInsn invokeInsn = new MInsn(); 172 invokeInsn.insn = new Instruction(); 173 invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE); 174 if (isWide) { 175 invokeInsn.insn.vregA = 3; 176 } else { 177 invokeInsn.insn.vregA = 2; 178 } 179 invokeInsn.insn.vregB = printMethodIdx; 180 invokeInsn.insn.vregC = streamRegister; 181 182 Log.info(String.format("Printing output value of instruction %s", insnOutputToPrint)); 183 184 stats.incrementStat("Printed output value"); 185 186 mutatableCode.insertInstructionAfter(invokeInsn, mutation.printedOutputIdx); 187 mutatableCode.insertInstructionAfter(streamLoadInsn, mutation.printedOutputIdx); 188 mutatableCode.insertInstructionAfter(valueCopyInsn, mutation.printedOutputIdx); 189 190 mutatableCode.finishedUsingTemporaryVRegs(); 191 } 192 193 private static enum OutputType { 194 STRING("(Ljava/lang/String;)V"), 195 BOOLEAN("(Z)V"), 196 BYTE("(B)V"), 197 CHAR("(C)V"), 198 SHORT("(S)V"), 199 INT("(I)V"), 200 LONG("(J)V"), 201 FLOAT("(F)V"), 202 DOUBLE("(D)V"), 203 UNKNOWN("UNKNOWN"); 204 205 private String printingSignature; 206 private OutputType(String s) { 207 printingSignature = s; 208 } 209 210 public String getSignatureForPrintln() { 211 return printingSignature; 212 } 213 } 214 215 private OutputType getInstructionOutputType(MInsn mInsn) { 216 Opcode opcode = mInsn.insn.info.opcode; 217 if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) { 218 return OutputType.STRING; 219 } 220 if (opcode == Opcode.IGET_BOOLEAN || opcode == Opcode.SGET_BOOLEAN) { 221 return OutputType.BOOLEAN; 222 } 223 if (opcode == Opcode.IGET_BYTE || opcode == Opcode.SGET_BYTE 224 || opcode == Opcode.INT_TO_BYTE) { 225 return OutputType.BYTE; 226 } 227 if (opcode == Opcode.IGET_CHAR || opcode == Opcode.SGET_CHAR 228 || opcode == Opcode.INT_TO_CHAR) { 229 return OutputType.CHAR; 230 } 231 if (opcode == Opcode.IGET_SHORT || opcode == Opcode.SGET_SHORT 232 || opcode == Opcode.INT_TO_SHORT) { 233 return OutputType.SHORT; 234 } 235 if (opcode == Opcode.NEG_INT || opcode == Opcode.NOT_INT 236 || opcode == Opcode.LONG_TO_INT || opcode == Opcode.FLOAT_TO_INT 237 || opcode == Opcode.DOUBLE_TO_INT 238 || Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT) 239 || Opcode.isBetween(opcode, Opcode.ADD_INT_2ADDR, Opcode.USHR_INT_2ADDR) 240 || Opcode.isBetween(opcode, Opcode.ADD_INT_LIT16, Opcode.USHR_INT_LIT8)) { 241 return OutputType.INT; 242 } 243 if (opcode == Opcode.NEG_LONG || opcode == Opcode.NOT_LONG 244 || opcode == Opcode.INT_TO_LONG || opcode == Opcode.FLOAT_TO_LONG 245 || opcode == Opcode.DOUBLE_TO_LONG 246 || Opcode.isBetween(opcode, Opcode.ADD_LONG, Opcode.USHR_LONG) 247 || Opcode.isBetween(opcode, Opcode.ADD_LONG_2ADDR, Opcode.USHR_LONG_2ADDR)) { 248 return OutputType.LONG; 249 } 250 if (opcode == Opcode.NEG_FLOAT 251 || opcode == Opcode.INT_TO_FLOAT || opcode == Opcode.LONG_TO_FLOAT 252 || opcode == Opcode.DOUBLE_TO_FLOAT 253 || Opcode.isBetween(opcode, Opcode.ADD_FLOAT, Opcode.REM_FLOAT) 254 || Opcode.isBetween(opcode, Opcode.ADD_FLOAT_2ADDR, Opcode.REM_FLOAT_2ADDR)) { 255 return OutputType.FLOAT; 256 } 257 if (opcode == Opcode.NEG_DOUBLE 258 || opcode == Opcode.INT_TO_DOUBLE || opcode == Opcode.LONG_TO_DOUBLE 259 || opcode == Opcode.FLOAT_TO_DOUBLE 260 || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE, Opcode.REM_DOUBLE) 261 || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE_2ADDR, Opcode.REM_DOUBLE_2ADDR)) { 262 return OutputType.DOUBLE; 263 } 264 return OutputType.UNKNOWN; 265 } 266} 267