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.MTryBlock; 23import dexfuzz.program.MutatableCode; 24import dexfuzz.program.Mutation; 25 26import java.util.List; 27import java.util.Random; 28 29public 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 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 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 71 public Mutation getNewMutation() { 72 return new AssociatedMutation(); 73 } 74 75 public TryBlockShifter() { } 76 77 public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) { 78 super(rng, stats, mutations); 79 likelihood = 40; 80 } 81 82 @Override 83 protected boolean canMutate(MutatableCode mutatableCode) { 84 if (mutatableCode.triesSize > 0) { 85 return true; 86 } 87 88 Log.debug("Method contains no tries."); 89 return false; 90 } 91 92 @Override 93 protected Mutation generateMutation(MutatableCode mutatableCode) { 94 // Pick a random try. 95 int tryIdx = rng.nextInt(mutatableCode.triesSize); 96 MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx); 97 98 boolean shiftingTryBlock = rng.nextBoolean(); 99 boolean shiftingStart = false; 100 boolean shiftingHandlerCatchall = false; 101 int shiftingHandlerIdx = -1; 102 if (shiftingTryBlock) { 103 // We're shifting the boundaries of the try block 104 // determine if we shift the start or the end. 105 shiftingStart = rng.nextBoolean(); 106 } else { 107 // We're shifting the start of a handler of the try block. 108 if (tryBlock.handlers.isEmpty()) { 109 // No handlers, so we MUST mutate the catchall 110 shiftingHandlerCatchall = true; 111 } else if (tryBlock.catchAllHandler != null) { 112 // There is a catchall handler, so potentially mutate it. 113 shiftingHandlerCatchall = rng.nextBoolean(); 114 } 115 // If we're not going to shift the catchall handler, then 116 // pick an explicit handler to shift. 117 if (!shiftingHandlerCatchall) { 118 shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size()); 119 } 120 } 121 122 // Get the original instruction wherever we're shifting. 123 MInsn oldInsn = null; 124 if (shiftingTryBlock && shiftingStart) { 125 oldInsn = tryBlock.startInsn; 126 } else if (shiftingTryBlock && !(shiftingStart)) { 127 oldInsn = tryBlock.endInsn; 128 } else if (!(shiftingTryBlock) && shiftingHandlerCatchall) { 129 oldInsn = tryBlock.catchAllHandler; 130 } else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall) 131 && (shiftingHandlerIdx != -1)) { 132 oldInsn = tryBlock.handlers.get(shiftingHandlerIdx); 133 } else { 134 Log.errorAndQuit("Faulty logic in TryBlockShifter!"); 135 } 136 137 // Find the index of this instruction. 138 int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn); 139 140 int newInsnIdx = oldInsnIdx; 141 142 int delta = 0; 143 144 // Keep searching for a new index. 145 while (newInsnIdx == oldInsnIdx) { 146 // Vary by +/- 2 instructions. 147 delta = 0; 148 while (delta == 0) { 149 delta = (rng.nextInt(5) - 2); 150 } 151 152 newInsnIdx = oldInsnIdx + delta; 153 154 // Check the new index is legal. 155 if (newInsnIdx < 0) { 156 newInsnIdx = 0; 157 } else if (newInsnIdx >= mutatableCode.getInstructionCount()) { 158 newInsnIdx = mutatableCode.getInstructionCount() - 1; 159 } 160 } 161 162 AssociatedMutation mutation = new AssociatedMutation(); 163 mutation.setup(this.getClass(), mutatableCode); 164 mutation.tryIdx = tryIdx; 165 mutation.shiftingTryBlock = shiftingTryBlock; 166 mutation.shiftingStart = shiftingStart; 167 mutation.shiftingHandlerCatchall = shiftingHandlerCatchall; 168 mutation.shiftingHandlerIdx = shiftingHandlerIdx; 169 mutation.newShiftedInsnIdx = newInsnIdx; 170 return mutation; 171 } 172 173 @Override 174 protected void applyMutation(Mutation uncastMutation) { 175 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 176 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 177 MutatableCode mutatableCode = mutation.mutatableCode; 178 179 MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx); 180 181 MInsn newInsn = 182 mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx); 183 184 // Find the right mutatable instruction in try block, and point it at the new instruction. 185 if (mutation.shiftingTryBlock && mutation.shiftingStart) { 186 tryBlock.startInsn = newInsn; 187 Log.info("Shifted the start of try block #" + mutation.tryIdx 188 + " to be at " + newInsn); 189 } else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) { 190 tryBlock.endInsn = newInsn; 191 Log.info("Shifted the end of try block #" + mutation.tryIdx 192 + " to be at " + newInsn); 193 } else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) { 194 tryBlock.catchAllHandler = newInsn; 195 Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx 196 + " to be at " + newInsn); 197 } else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall) 198 && (mutation.shiftingHandlerIdx != -1)) { 199 tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn); 200 Log.info("Shifted handler #" + mutation.shiftingHandlerIdx 201 + " of try block #" + mutation.tryIdx + " to be at " + newInsn); 202 } else { 203 Log.errorAndQuit("faulty logic in TryBlockShifter"); 204 } 205 206 stats.incrementStat("Shifted boundary in a try block"); 207 } 208} 209