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.MutatableCode; 23 import dexfuzz.program.Mutation; 24 import dexfuzz.rawdex.EncodedField; 25 import dexfuzz.rawdex.Instruction; 26 import dexfuzz.rawdex.Opcode; 27 import dexfuzz.rawdex.formats.ContainsPoolIndex; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Random; 32 33 public class FieldFlagChanger extends CodeMutator { 34 /** 35 * Every CodeMutator has an AssociatedMutation, representing the 36 * mutation that this CodeMutator can perform, to allow separate 37 * generateMutation() and applyMutation() phases, allowing serialization. 38 */ 39 public static class AssociatedMutation extends Mutation { 40 public int fieldInsnIdx; 41 public boolean setVolatile; 42 43 @Override 44 public String getString() { 45 StringBuilder builder = new StringBuilder(); 46 builder.append(fieldInsnIdx).append(" "); 47 builder.append(setVolatile); 48 return builder.toString(); 49 } 50 51 @Override 52 public void parseString(String[] elements) { 53 fieldInsnIdx = Integer.parseInt(elements[2]); 54 setVolatile = Boolean.parseBoolean(elements[3]); 55 } 56 } 57 58 // The following two methods are here for the benefit of MutationSerializer, 59 // so it can create a CodeMutator and get the correct associated Mutation, as it 60 // reads in mutations from a dump of mutations. 61 @Override 62 public Mutation getNewMutation() { 63 return new AssociatedMutation(); 64 } 65 66 public FieldFlagChanger() { } 67 68 public FieldFlagChanger(Random rng, MutationStats stats, List<Mutation> mutations) { 69 super(rng, stats, mutations); 70 likelihood = 40; 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> fieldInsns = null; 77 78 private void generateCachedFieldInsns(MutatableCode mutatableCode) { 79 if (fieldInsns != null) { 80 return; 81 } 82 83 fieldInsns = new ArrayList<MInsn>(); 84 85 for (MInsn mInsn : mutatableCode.getInstructions()) { 86 if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) { 87 fieldInsns.add(mInsn); 88 } 89 } 90 } 91 92 @Override 93 protected boolean canMutate(MutatableCode mutatableCode) { 94 for (MInsn mInsn : mutatableCode.getInstructions()) { 95 if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) { 96 return true; 97 } 98 } 99 100 Log.debug("No field instructions in method, skipping..."); 101 return false; 102 } 103 104 @Override 105 protected Mutation generateMutation(MutatableCode mutatableCode) { 106 generateCachedFieldInsns(mutatableCode); 107 108 int fieldInsnIdx = rng.nextInt(fieldInsns.size()); 109 110 Instruction insn = fieldInsns.get(fieldInsnIdx).insn; 111 ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format; 112 int fieldIdx = containsPoolIndex.getPoolIndex(insn); 113 EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx); 114 115 boolean setVolatile = false; 116 if (!encodedField.isVolatile()) { 117 setVolatile = true; 118 } 119 // TODO: Flip more flags? 120 121 AssociatedMutation mutation = new AssociatedMutation(); 122 mutation.setup(this.getClass(), mutatableCode); 123 mutation.fieldInsnIdx = fieldInsnIdx; 124 mutation.setVolatile = setVolatile; 125 return mutation; 126 } 127 128 @Override 129 protected void applyMutation(Mutation uncastMutation) { 130 // Cast the Mutation to our AssociatedMutation, so we can access its fields. 131 AssociatedMutation mutation = (AssociatedMutation) uncastMutation; 132 MutatableCode mutatableCode = mutation.mutatableCode; 133 134 generateCachedFieldInsns(mutatableCode); 135 136 Instruction insn = fieldInsns.get(mutation.fieldInsnIdx).insn; 137 ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format; 138 int fieldIdx = containsPoolIndex.getPoolIndex(insn); 139 EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx); 140 141 if (mutation.setVolatile) { 142 encodedField.setVolatile(true); 143 Log.info("Set field idx " + fieldIdx + " as volatile"); 144 } else { 145 encodedField.setVolatile(false); 146 Log.info("Set field idx " + fieldIdx + " as not volatile"); 147 } 148 149 stats.incrementStat("Changed volatility of field"); 150 151 // Clear cache. 152 fieldInsns = null; 153 } 154 155 private boolean isFileDefinedFieldInstruction(MInsn mInsn, MutatableCode mutatableCode) { 156 Opcode opcode = mInsn.insn.info.opcode; 157 if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.SPUT_SHORT)) { 158 Instruction insn = mInsn.insn; 159 ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format; 160 int fieldIdx = containsPoolIndex.getPoolIndex(insn); 161 if (mutatableCode.program.getEncodedField(fieldIdx) != null) { 162 return true; 163 } 164 } 165 return false; 166 } 167 } 168