blob: 55e3e603126d7004d271cc5922c12e7cb01a1b91 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dexfuzz.program.mutators;
import dexfuzz.Log;
import dexfuzz.MutationStats;
import dexfuzz.program.MInsn;
import dexfuzz.program.MTryBlock;
import dexfuzz.program.MutatableCode;
import dexfuzz.program.Mutation;
import java.util.List;
import java.util.Random;
public class TryBlockShifter extends CodeMutator {
/**
* Every CodeMutator has an AssociatedMutation, representing the
* mutation that this CodeMutator can perform, to allow separate
* generateMutation() and applyMutation() phases, allowing serialization.
*/
public static class AssociatedMutation extends Mutation {
public int tryIdx;
public boolean shiftingTryBlock; // false => shifting handler
public boolean shiftingStart; // false => shifting end (try block only)
public boolean shiftingHandlerCatchall;
public int shiftingHandlerIdx;
public int newShiftedInsnIdx;
@Override
public String getString() {
String result = String.format("%d %s %s %s %d %d",
tryIdx,
(shiftingTryBlock ? "T" : "F"),
(shiftingStart ? "T" : "F"),
(shiftingHandlerCatchall ? "T" : "F"),
shiftingHandlerIdx,
newShiftedInsnIdx
);
return result;
}
@Override
public void parseString(String[] elements) {
tryIdx = Integer.parseInt(elements[2]);
shiftingTryBlock = elements[3].equals("T");
shiftingStart = elements[4].equals("T");
shiftingHandlerCatchall = elements[5].equals("T");
shiftingHandlerIdx = Integer.parseInt(elements[6]);
newShiftedInsnIdx = Integer.parseInt(elements[7]);
}
}
// The following two methods are here for the benefit of MutationSerializer,
// so it can create a CodeMutator and get the correct associated Mutation, as it
// reads in mutations from a dump of mutations.
@Override
public Mutation getNewMutation() {
return new AssociatedMutation();
}
public TryBlockShifter() { }
public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
super(rng, stats, mutations);
likelihood = 40;
}
@Override
protected boolean canMutate(MutatableCode mutatableCode) {
if (mutatableCode.triesSize == 0) {
Log.debug("Method contains no tries.");
return false;
}
if (mutatableCode.getInstructionCount() <= 1) {
Log.debug("Not enough instructions to shift try block.");
return false;
}
return true;
}
@Override
protected Mutation generateMutation(MutatableCode mutatableCode) {
// Pick a random try.
int tryIdx = rng.nextInt(mutatableCode.triesSize);
MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx);
boolean shiftingTryBlock = rng.nextBoolean();
boolean shiftingStart = false;
boolean shiftingHandlerCatchall = false;
int shiftingHandlerIdx = -1;
if (shiftingTryBlock) {
// We're shifting the boundaries of the try block
// determine if we shift the start or the end.
shiftingStart = rng.nextBoolean();
} else {
// We're shifting the start of a handler of the try block.
if (tryBlock.handlers.isEmpty()) {
// No handlers, so we MUST mutate the catchall
shiftingHandlerCatchall = true;
} else if (tryBlock.catchAllHandler != null) {
// There is a catchall handler, so potentially mutate it.
shiftingHandlerCatchall = rng.nextBoolean();
}
// If we're not going to shift the catchall handler, then
// pick an explicit handler to shift.
if (!shiftingHandlerCatchall) {
shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size());
}
}
// Get the original instruction wherever we're shifting.
MInsn oldInsn = null;
if (shiftingTryBlock && shiftingStart) {
oldInsn = tryBlock.startInsn;
} else if (shiftingTryBlock && !(shiftingStart)) {
oldInsn = tryBlock.endInsn;
} else if (!(shiftingTryBlock) && shiftingHandlerCatchall) {
oldInsn = tryBlock.catchAllHandler;
} else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall)
&& (shiftingHandlerIdx != -1)) {
oldInsn = tryBlock.handlers.get(shiftingHandlerIdx);
} else {
Log.errorAndQuit("Faulty logic in TryBlockShifter!");
}
// Find the index of this instruction.
int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn);
int newInsnIdx = oldInsnIdx;
int delta = 0;
// Keep searching for a new index.
while (newInsnIdx == oldInsnIdx) {
// Vary by +/- 2 instructions.
delta = 0;
while (delta == 0) {
delta = (rng.nextInt(5) - 2);
}
newInsnIdx = oldInsnIdx + delta;
// Check the new index is legal.
if (newInsnIdx < 0) {
newInsnIdx = 0;
} else if (newInsnIdx >= mutatableCode.getInstructionCount()) {
newInsnIdx = mutatableCode.getInstructionCount() - 1;
}
}
AssociatedMutation mutation = new AssociatedMutation();
mutation.setup(this.getClass(), mutatableCode);
mutation.tryIdx = tryIdx;
mutation.shiftingTryBlock = shiftingTryBlock;
mutation.shiftingStart = shiftingStart;
mutation.shiftingHandlerCatchall = shiftingHandlerCatchall;
mutation.shiftingHandlerIdx = shiftingHandlerIdx;
mutation.newShiftedInsnIdx = newInsnIdx;
return mutation;
}
@Override
protected void applyMutation(Mutation uncastMutation) {
// Cast the Mutation to our AssociatedMutation, so we can access its fields.
AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
MutatableCode mutatableCode = mutation.mutatableCode;
MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx);
MInsn newInsn =
mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx);
// Find the right mutatable instruction in try block, and point it at the new instruction.
if (mutation.shiftingTryBlock && mutation.shiftingStart) {
tryBlock.startInsn = newInsn;
Log.info("Shifted the start of try block #" + mutation.tryIdx
+ " to be at " + newInsn);
} else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) {
tryBlock.endInsn = newInsn;
Log.info("Shifted the end of try block #" + mutation.tryIdx
+ " to be at " + newInsn);
} else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) {
tryBlock.catchAllHandler = newInsn;
Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx
+ " to be at " + newInsn);
} else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall)
&& (mutation.shiftingHandlerIdx != -1)) {
tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn);
Log.info("Shifted handler #" + mutation.shiftingHandlerIdx
+ " of try block #" + mutation.tryIdx + " to be at " + newInsn);
} else {
Log.errorAndQuit("faulty logic in TryBlockShifter");
}
stats.incrementStat("Shifted boundary in a try block");
}
}