• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Instruction;
25 import dexfuzz.rawdex.Opcode;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Random;
30 
31 public class ConversionRepeater extends CodeMutator {
32   /**
33    * Every CodeMutator has an AssociatedMutation, representing the
34    * mutation that this CodeMutator can perform, to allow separate
35    * generateMutation() and applyMutation() phases, allowing serialization.
36    */
37   public static class AssociatedMutation extends Mutation {
38     public int conversionInsnIdx;
39 
40     @Override
getString()41     public String getString() {
42       return Integer.toString(conversionInsnIdx);
43     }
44 
45     @Override
parseString(String[] elements)46     public void parseString(String[] elements) {
47       conversionInsnIdx = Integer.parseInt(elements[2]);
48     }
49   }
50 
51   // The following two methods are here for the benefit of MutationSerializer,
52   // so it can create a CodeMutator and get the correct associated Mutation, as it
53   // reads in mutations from a dump of mutations.
54   @Override
getNewMutation()55   public Mutation getNewMutation() {
56     return new AssociatedMutation();
57   }
58 
ConversionRepeater()59   public ConversionRepeater() { }
60 
ConversionRepeater(Random rng, MutationStats stats, List<Mutation> mutations)61   public ConversionRepeater(Random rng, MutationStats stats, List<Mutation> mutations) {
62     super(rng, stats, mutations);
63     likelihood = 50;
64   }
65 
66   // A cache that should only exist between generateMutation() and applyMutation(),
67   // or be created at the start of applyMutation(), if we're reading in mutations from
68   // a file.
69   private List<MInsn> conversionInsns = null;
70 
generateCachedConversionInsns(MutatableCode mutatableCode)71   private void generateCachedConversionInsns(MutatableCode mutatableCode) {
72     if (conversionInsns != null) {
73       return;
74     }
75 
76     conversionInsns = new ArrayList<MInsn>();
77 
78     for (MInsn mInsn : mutatableCode.getInstructions()) {
79       if (isConversionInstruction(mInsn)) {
80         conversionInsns.add(mInsn);
81       }
82     }
83   }
84 
85   @Override
canMutate(MutatableCode mutatableCode)86   protected boolean canMutate(MutatableCode mutatableCode) {
87     for (MInsn mInsn : mutatableCode.getInstructions()) {
88       if (isConversionInstruction(mInsn)) {
89         return true;
90       }
91     }
92 
93     Log.debug("No conversion operations in method, skipping...");
94     return false;
95   }
96 
97   @Override
generateMutation(MutatableCode mutatableCode)98   protected Mutation generateMutation(MutatableCode mutatableCode) {
99     generateCachedConversionInsns(mutatableCode);
100     int conversionInsnIdx = rng.nextInt(conversionInsns.size());
101     AssociatedMutation mutation = new AssociatedMutation();
102     mutation.setup(this.getClass(), mutatableCode);
103     mutation.conversionInsnIdx = conversionInsnIdx;
104     return mutation;
105   }
106 
107   @Override
applyMutation(Mutation uncastMutation)108   protected void applyMutation(Mutation uncastMutation) {
109     // Cast the Mutation to our AssociatedMutation, so we can access its fields.
110     AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
111     MutatableCode mutatableCode = mutation.mutatableCode;
112 
113     generateCachedConversionInsns(mutatableCode);
114 
115     MInsn originalInsn = conversionInsns.get(mutation.conversionInsnIdx);
116 
117     // We want to create two new instructions:
118     // [original conversion] eg float-to-int
119     // NEW: [there] eg int-to-float (with vregs of first inst swapped)
120     // NEW: [back] eg float-to-int
121 
122     // Create the "there" instruction.
123     MInsn newInsnThere = originalInsn.clone();
124 
125     // Flip the opcode.
126     Opcode oppositeOpcode = null;
127     switch (newInsnThere.insn.info.opcode) {
128       case INT_TO_LONG:
129         oppositeOpcode = Opcode.LONG_TO_INT;
130         break;
131       case INT_TO_FLOAT:
132         oppositeOpcode = Opcode.FLOAT_TO_INT;
133         break;
134       case INT_TO_DOUBLE:
135         oppositeOpcode = Opcode.DOUBLE_TO_INT;
136         break;
137       case LONG_TO_INT:
138         oppositeOpcode = Opcode.INT_TO_LONG;
139         break;
140       case LONG_TO_FLOAT:
141         oppositeOpcode = Opcode.FLOAT_TO_LONG;
142         break;
143       case LONG_TO_DOUBLE:
144         oppositeOpcode = Opcode.DOUBLE_TO_LONG;
145         break;
146       case FLOAT_TO_INT:
147         oppositeOpcode = Opcode.INT_TO_FLOAT;
148         break;
149       case FLOAT_TO_LONG:
150         oppositeOpcode = Opcode.LONG_TO_FLOAT;
151         break;
152       case FLOAT_TO_DOUBLE:
153         oppositeOpcode = Opcode.DOUBLE_TO_FLOAT;
154         break;
155       case DOUBLE_TO_INT:
156         oppositeOpcode = Opcode.INT_TO_DOUBLE;
157         break;
158       case DOUBLE_TO_LONG:
159         oppositeOpcode = Opcode.LONG_TO_DOUBLE;
160         break;
161       case DOUBLE_TO_FLOAT:
162         oppositeOpcode = Opcode.FLOAT_TO_DOUBLE;
163         break;
164       default:
165         Log.errorAndQuit(
166             "Trying to repeat the conversion in an insn that is not a conversion insn.");
167     }
168     newInsnThere.insn.info = Instruction.getOpcodeInfo(oppositeOpcode);
169 
170     // Swap the vregs.
171     long tempReg = newInsnThere.insn.vregA;
172     newInsnThere.insn.vregA = newInsnThere.insn.vregB;
173     newInsnThere.insn.vregB = tempReg;
174 
175     // Create the "back" instruction.
176     MInsn newInsnBack = originalInsn.clone();
177 
178     // Get the index into the MutatableCode's mInsns list for this insn.
179     int originalInsnIdx = mutatableCode.getInstructionIndex(originalInsn);
180 
181     // Insert the new instructions.
182     mutatableCode.insertInstructionAfter(newInsnThere, originalInsnIdx);
183     mutatableCode.insertInstructionAfter(newInsnBack, originalInsnIdx + 1);
184 
185     Log.info("Performing conversion repetition for " + originalInsn);
186 
187     stats.incrementStat("Repeating conversion");
188 
189     // Clear the cache.
190     conversionInsns = null;
191   }
192 
isConversionInstruction(MInsn mInsn)193   private boolean isConversionInstruction(MInsn mInsn) {
194     Opcode opcode = mInsn.insn.info.opcode;
195     if (Opcode.isBetween(opcode, Opcode.INT_TO_LONG, Opcode.DOUBLE_TO_FLOAT)) {
196       return true;
197     }
198     return false;
199   }
200 }
201