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.ArrayList; 28import java.util.List; 29import java.util.Random; 30 31public class ConversionRepeater 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 int conversionInsnIdx; 39 40 @Override 41 public String getString() { 42 return Integer.toString(conversionInsnIdx); 43 } 44 45 @Override 46 public void parseString(String[] elements) { 47 conversionInsnIdx = Integer.parseInt(elements[2]); 48 } 49 } 50 51 // The following two methods are here for the benefit of MutationSerializer, 52 // so it can create a CodeMutator and get the correct associated Mutation, as it 53 // reads in mutations from a dump of mutations. 54 @Override 55 public Mutation getNewMutation() { 56 return new AssociatedMutation(); 57 } 58 59 public ConversionRepeater() { } 60 61 public ConversionRepeater(Random rng, MutationStats stats, List<Mutation> mutations) { 62 super(rng, stats, mutations); 63 likelihood = 50; 64 } 65 66 // A cache that should only exist between generateMutation() and applyMutation(), 67 // or be created at the start of applyMutation(), if we're reading in mutations from 68 // a file. 69 private List<MInsn> conversionInsns = null; 70 71 private void generateCachedConversionInsns(MutatableCode mutatableCode) { 72 if (conversionInsns != null) { 73 return; 74 } 75 76 conversionInsns = new ArrayList<MInsn>(); 77 78 for (MInsn mInsn : mutatableCode.getInstructions()) { 79 if (isConversionInstruction(mInsn)) { 80 conversionInsns.add(mInsn); 81 } 82 } 83 } 84 85 @Override 86 protected boolean canMutate(MutatableCode mutatableCode) { 87 for (MInsn mInsn : mutatableCode.getInstructions()) { 88 if (isConversionInstruction(mInsn)) { 89 return true; 90 } 91 } 92 93 Log.debug("No conversion operations in method, skipping..."); 94 return false; 95 } 96 97 @Override 98 protected Mutation generateMutation(MutatableCode mutatableCode) { 99 generateCachedConversionInsns(mutatableCode); 100 int conversionInsnIdx = rng.nextInt(conversionInsns.size()); 101 AssociatedMutation mutation = new AssociatedMutation(); 102 mutation.setup(this.getClass(), mutatableCode); 103 mutation.conversionInsnIdx = conversionInsnIdx; 104 return mutation; 105 } 106 107 @Override 108 protected void applyMutation(Mutation uncastMutation) { 109 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 110 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 111 MutatableCode mutatableCode = mutation.mutatableCode; 112 113 generateCachedConversionInsns(mutatableCode); 114 115 MInsn originalInsn = conversionInsns.get(mutation.conversionInsnIdx); 116 117 // We want to create two new instructions: 118 // [original conversion] eg float-to-int 119 // NEW: [there] eg int-to-float (with vregs of first inst swapped) 120 // NEW: [back] eg float-to-int 121 122 // Create the "there" instruction. 123 MInsn newInsnThere = originalInsn.clone(); 124 125 // Flip the opcode. 126 Opcode oppositeOpcode = null; 127 switch (newInsnThere.insn.info.opcode) { 128 case INT_TO_LONG: 129 oppositeOpcode = Opcode.LONG_TO_INT; 130 break; 131 case INT_TO_FLOAT: 132 oppositeOpcode = Opcode.FLOAT_TO_INT; 133 break; 134 case INT_TO_DOUBLE: 135 oppositeOpcode = Opcode.DOUBLE_TO_INT; 136 break; 137 case LONG_TO_INT: 138 oppositeOpcode = Opcode.INT_TO_LONG; 139 break; 140 case LONG_TO_FLOAT: 141 oppositeOpcode = Opcode.FLOAT_TO_LONG; 142 break; 143 case LONG_TO_DOUBLE: 144 oppositeOpcode = Opcode.DOUBLE_TO_LONG; 145 break; 146 case FLOAT_TO_INT: 147 oppositeOpcode = Opcode.INT_TO_FLOAT; 148 break; 149 case FLOAT_TO_LONG: 150 oppositeOpcode = Opcode.LONG_TO_FLOAT; 151 break; 152 case FLOAT_TO_DOUBLE: 153 oppositeOpcode = Opcode.DOUBLE_TO_FLOAT; 154 break; 155 case DOUBLE_TO_INT: 156 oppositeOpcode = Opcode.INT_TO_DOUBLE; 157 break; 158 case DOUBLE_TO_LONG: 159 oppositeOpcode = Opcode.LONG_TO_DOUBLE; 160 break; 161 case DOUBLE_TO_FLOAT: 162 oppositeOpcode = Opcode.FLOAT_TO_DOUBLE; 163 break; 164 default: 165 Log.errorAndQuit( 166 "Trying to repeat the conversion in an insn that is not a conversion insn."); 167 } 168 newInsnThere.insn.info = Instruction.getOpcodeInfo(oppositeOpcode); 169 170 // Swap the vregs. 171 long tempReg = newInsnThere.insn.vregA; 172 newInsnThere.insn.vregA = newInsnThere.insn.vregB; 173 newInsnThere.insn.vregB = tempReg; 174 175 // Create the "back" instruction. 176 MInsn newInsnBack = originalInsn.clone(); 177 178 // Get the index into the MutatableCode's mInsns list for this insn. 179 int originalInsnIdx = mutatableCode.getInstructionIndex(originalInsn); 180 181 // Insert the new instructions. 182 mutatableCode.insertInstructionAfter(newInsnThere, originalInsnIdx); 183 mutatableCode.insertInstructionAfter(newInsnBack, originalInsnIdx + 1); 184 185 Log.info("Performing conversion repetition for " + originalInsn); 186 187 stats.incrementStat("Repeating conversion"); 188 189 // Clear the cache. 190 conversionInsns = null; 191 } 192 193 private boolean isConversionInstruction(MInsn mInsn) { 194 Opcode opcode = mInsn.insn.info.opcode; 195 if (Opcode.isBetween(opcode, Opcode.INT_TO_LONG, Opcode.DOUBLE_TO_FLOAT)) { 196 return true; 197 } 198 return false; 199 } 200} 201