1/* 2 * Copyright (C) 2017 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 31/** 32 * This class mutates the comparison operator of the if 33 * statements by taking in a random instruction, checking whether 34 * it is an if statement and, if so, changing the comparison 35 * operator. The inheriting classes implement the way comparison 36 * operator changes. For example, by choosing the opposite 37 * comparison operator or by choosing a random comparison operator. 38 */ 39public abstract class IfBranchChanger extends CodeMutator { 40 /** 41 * Every CodeMutator has an AssociatedMutation, representing the 42 * mutation that this CodeMutator can perform, to allow separate 43 * generateMutation() and applyMutation() phases, allowing serialization. 44 */ 45 public static class AssociatedMutation extends Mutation { 46 public int ifBranchInsnIdx; 47 48 @Override 49 public String getString() { 50 return Integer.toString(ifBranchInsnIdx); 51 } 52 53 @Override 54 public void parseString(String[] elements) { 55 ifBranchInsnIdx = Integer.parseInt(elements[2]); 56 } 57 } 58 59 // The following two methods are here for the benefit of MutationSerializer, 60 // so it can create a CodeMutator and get the correct associated Mutation, as it 61 // reads in mutations from a dump of mutations. 62 @Override 63 public Mutation getNewMutation() { 64 return new AssociatedMutation(); 65 } 66 67 public IfBranchChanger() { } 68 69 public IfBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) { 70 super(rng, stats, mutations); 71 } 72 73 // A cache that should only exist between generateMutation() and applyMutation(), 74 // or be created at the start of applyMutation(), if we're reading in mutations from 75 // a file. 76 private List<MInsn> ifBranchInsns = null; 77 78 private void generateCachedifBranchInsns(MutatableCode mutatableCode) { 79 if (ifBranchInsns != null) { 80 return; 81 } 82 83 ifBranchInsns = new ArrayList<MInsn>(); 84 85 for (MInsn mInsn : mutatableCode.getInstructions()) { 86 if (isIfBranchOperation(mInsn)) { 87 ifBranchInsns.add(mInsn); 88 } 89 } 90 } 91 92 @Override 93 protected boolean canMutate(MutatableCode mutatableCode) { 94 for (MInsn mInsn : mutatableCode.getInstructions()) { 95 if (isIfBranchOperation(mInsn)) { 96 return true; 97 } 98 } 99 100 Log.debug("No if branch operation, skipping..."); 101 return false; 102 } 103 104 @Override 105 protected Mutation generateMutation(MutatableCode mutatableCode) { 106 generateCachedifBranchInsns(mutatableCode); 107 108 int ifBranchInsnIdx = rng.nextInt(ifBranchInsns.size()); 109 110 AssociatedMutation mutation = new AssociatedMutation(); 111 mutation.setup(this.getClass(), mutatableCode); 112 mutation.ifBranchInsnIdx = ifBranchInsnIdx; 113 return mutation; 114 } 115 116 @Override 117 protected void applyMutation(Mutation uncastMutation) { 118 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 119 MutatableCode mutatableCode = mutation.mutatableCode; 120 121 generateCachedifBranchInsns(mutatableCode); 122 123 MInsn ifBranchInsn = ifBranchInsns.get(mutation.ifBranchInsnIdx); 124 125 String oldInsnString = ifBranchInsn.toString(); 126 127 Opcode newOpcode = getModifiedOpcode(ifBranchInsn); 128 129 ifBranchInsn.insn.info = Instruction.getOpcodeInfo(newOpcode); 130 131 Log.info("Changed " + oldInsnString + " to " + ifBranchInsn); 132 133 stats.incrementStat("Changed if branch operator to " + getMutationTag() + " operator"); 134 135 // Clear cache. 136 ifBranchInsns = null; 137 } 138 139 /** 140 * Get a different if branch instruction. 141 * @return opcode of the new comparison operator. 142 */ 143 protected abstract Opcode getModifiedOpcode(MInsn mInsn); 144 145 /** 146 * Get the tag of the mutation that fired. 147 * @return string tag of the type of mutation used 148 */ 149 protected abstract String getMutationTag(); 150 151 private boolean isIfBranchOperation(MInsn mInsn) { 152 Opcode opcode = mInsn.insn.info.opcode; 153 if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)) { 154 return true; 155 } 156 return false; 157 } 158}