Home | History | Annotate | Download | only in mutators
      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
     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       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
     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
    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