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.MTryBlock; 23 import dexfuzz.program.MutatableCode; 24 import dexfuzz.program.Mutation; 25 26 import java.util.List; 27 import java.util.Random; 28 29 public class TryBlockShifter extends CodeMutator { 30 /** 31 * Every CodeMutator has an AssociatedMutation, representing the 32 * mutation that this CodeMutator can perform, to allow separate 33 * generateMutation() and applyMutation() phases, allowing serialization. 34 */ 35 public static class AssociatedMutation extends Mutation { 36 public int tryIdx; 37 public boolean shiftingTryBlock; // false => shifting handler 38 public boolean shiftingStart; // false => shifting end (try block only) 39 public boolean shiftingHandlerCatchall; 40 public int shiftingHandlerIdx; 41 public int newShiftedInsnIdx; 42 43 @Override getString()44 public String getString() { 45 String result = String.format("%d %s %s %s %d %d", 46 tryIdx, 47 (shiftingTryBlock ? "T" : "F"), 48 (shiftingStart ? "T" : "F"), 49 (shiftingHandlerCatchall ? "T" : "F"), 50 shiftingHandlerIdx, 51 newShiftedInsnIdx 52 ); 53 return result; 54 } 55 56 @Override parseString(String[] elements)57 public void parseString(String[] elements) { 58 tryIdx = Integer.parseInt(elements[2]); 59 shiftingTryBlock = elements[3].equals("T"); 60 shiftingStart = elements[4].equals("T"); 61 shiftingHandlerCatchall = elements[5].equals("T"); 62 shiftingHandlerIdx = Integer.parseInt(elements[6]); 63 newShiftedInsnIdx = Integer.parseInt(elements[7]); 64 } 65 } 66 67 // The following two methods are here for the benefit of MutationSerializer, 68 // so it can create a CodeMutator and get the correct associated Mutation, as it 69 // reads in mutations from a dump of mutations. 70 @Override getNewMutation()71 public Mutation getNewMutation() { 72 return new AssociatedMutation(); 73 } 74 TryBlockShifter()75 public TryBlockShifter() { } 76 TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations)77 public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) { 78 super(rng, stats, mutations); 79 likelihood = 40; 80 } 81 82 @Override canMutate(MutatableCode mutatableCode)83 protected boolean canMutate(MutatableCode mutatableCode) { 84 if (mutatableCode.triesSize == 0) { 85 Log.debug("Method contains no tries."); 86 return false; 87 } 88 if (mutatableCode.getInstructionCount() <= 1) { 89 Log.debug("Not enough instructions to shift try block."); 90 return false; 91 } 92 return true; 93 } 94 95 @Override generateMutation(MutatableCode mutatableCode)96 protected Mutation generateMutation(MutatableCode mutatableCode) { 97 // Pick a random try. 98 int tryIdx = rng.nextInt(mutatableCode.triesSize); 99 MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx); 100 101 boolean shiftingTryBlock = rng.nextBoolean(); 102 boolean shiftingStart = false; 103 boolean shiftingHandlerCatchall = false; 104 int shiftingHandlerIdx = -1; 105 if (shiftingTryBlock) { 106 // We're shifting the boundaries of the try block 107 // determine if we shift the start or the end. 108 shiftingStart = rng.nextBoolean(); 109 } else { 110 // We're shifting the start of a handler of the try block. 111 if (tryBlock.handlers.isEmpty()) { 112 // No handlers, so we MUST mutate the catchall 113 shiftingHandlerCatchall = true; 114 } else if (tryBlock.catchAllHandler != null) { 115 // There is a catchall handler, so potentially mutate it. 116 shiftingHandlerCatchall = rng.nextBoolean(); 117 } 118 // If we're not going to shift the catchall handler, then 119 // pick an explicit handler to shift. 120 if (!shiftingHandlerCatchall) { 121 shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size()); 122 } 123 } 124 125 // Get the original instruction wherever we're shifting. 126 MInsn oldInsn = null; 127 if (shiftingTryBlock && shiftingStart) { 128 oldInsn = tryBlock.startInsn; 129 } else if (shiftingTryBlock && !(shiftingStart)) { 130 oldInsn = tryBlock.endInsn; 131 } else if (!(shiftingTryBlock) && shiftingHandlerCatchall) { 132 oldInsn = tryBlock.catchAllHandler; 133 } else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall) 134 && (shiftingHandlerIdx != -1)) { 135 oldInsn = tryBlock.handlers.get(shiftingHandlerIdx); 136 } else { 137 Log.errorAndQuit("Faulty logic in TryBlockShifter!"); 138 } 139 140 // Find the index of this instruction. 141 int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn); 142 143 int newInsnIdx = oldInsnIdx; 144 145 int delta = 0; 146 147 // Keep searching for a new index. 148 while (newInsnIdx == oldInsnIdx) { 149 // Vary by +/- 2 instructions. 150 delta = 0; 151 while (delta == 0) { 152 delta = (rng.nextInt(5) - 2); 153 } 154 155 newInsnIdx = oldInsnIdx + delta; 156 157 // Check the new index is legal. 158 if (newInsnIdx < 0) { 159 newInsnIdx = 0; 160 } else if (newInsnIdx >= mutatableCode.getInstructionCount()) { 161 newInsnIdx = mutatableCode.getInstructionCount() - 1; 162 } 163 } 164 165 AssociatedMutation mutation = new AssociatedMutation(); 166 mutation.setup(this.getClass(), mutatableCode); 167 mutation.tryIdx = tryIdx; 168 mutation.shiftingTryBlock = shiftingTryBlock; 169 mutation.shiftingStart = shiftingStart; 170 mutation.shiftingHandlerCatchall = shiftingHandlerCatchall; 171 mutation.shiftingHandlerIdx = shiftingHandlerIdx; 172 mutation.newShiftedInsnIdx = newInsnIdx; 173 return mutation; 174 } 175 176 @Override applyMutation(Mutation uncastMutation)177 protected void applyMutation(Mutation uncastMutation) { 178 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 179 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 180 MutatableCode mutatableCode = mutation.mutatableCode; 181 182 MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx); 183 184 MInsn newInsn = 185 mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx); 186 187 // Find the right mutatable instruction in try block, and point it at the new instruction. 188 if (mutation.shiftingTryBlock && mutation.shiftingStart) { 189 tryBlock.startInsn = newInsn; 190 Log.info("Shifted the start of try block #" + mutation.tryIdx 191 + " to be at " + newInsn); 192 } else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) { 193 tryBlock.endInsn = newInsn; 194 Log.info("Shifted the end of try block #" + mutation.tryIdx 195 + " to be at " + newInsn); 196 } else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) { 197 tryBlock.catchAllHandler = newInsn; 198 Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx 199 + " to be at " + newInsn); 200 } else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall) 201 && (mutation.shiftingHandlerIdx != -1)) { 202 tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn); 203 Log.info("Shifted handler #" + mutation.shiftingHandlerIdx 204 + " of try block #" + mutation.tryIdx + " to be at " + newInsn); 205 } else { 206 Log.errorAndQuit("faulty logic in TryBlockShifter"); 207 } 208 209 stats.incrementStat("Shifted boundary in a try block"); 210 } 211 } 212