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 CmpBiasChanger 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 cmpBiasInsnIdx;
39 
40     @Override
getString()41     public String getString() {
42       return Integer.toString(cmpBiasInsnIdx);
43     }
44 
45     @Override
parseString(String[] elements)46     public void parseString(String[] elements) {
47       cmpBiasInsnIdx = 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 
CmpBiasChanger()59   public CmpBiasChanger() { }
60 
CmpBiasChanger(Random rng, MutationStats stats, List<Mutation> mutations)61   public CmpBiasChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
62     super(rng, stats, mutations);
63     likelihood = 30;
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> cmpBiasInsns = null;
70 
generateCachedCmpBiasInsns(MutatableCode mutatableCode)71   private void generateCachedCmpBiasInsns(MutatableCode mutatableCode) {
72     if (cmpBiasInsns != null) {
73       return;
74     }
75 
76     cmpBiasInsns = new ArrayList<MInsn>();
77 
78     for (MInsn mInsn : mutatableCode.getInstructions()) {
79       if (isCmpBiasOperation(mInsn)) {
80         cmpBiasInsns.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 (isCmpBiasOperation(mInsn)) {
89         return true;
90       }
91     }
92 
93     Log.debug("No cmp-with-bias operations in method, skipping...");
94     return false;
95   }
96 
97   @Override
generateMutation(MutatableCode mutatableCode)98   protected Mutation generateMutation(MutatableCode mutatableCode) {
99     generateCachedCmpBiasInsns(mutatableCode);
100 
101     int cmpBiasInsnIdx = rng.nextInt(cmpBiasInsns.size());
102 
103     AssociatedMutation mutation = new AssociatedMutation();
104     mutation.setup(this.getClass(), mutatableCode);
105     mutation.cmpBiasInsnIdx = cmpBiasInsnIdx;
106     return mutation;
107   }
108 
109   @Override
applyMutation(Mutation uncastMutation)110   protected void applyMutation(Mutation uncastMutation) {
111     // Cast the Mutation to our AssociatedMutation, so we can access its fields.
112     AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
113     MutatableCode mutatableCode = mutation.mutatableCode;
114 
115     generateCachedCmpBiasInsns(mutatableCode);
116 
117     MInsn cmpBiasInsn = cmpBiasInsns.get(mutation.cmpBiasInsnIdx);
118 
119     String oldInsnString = cmpBiasInsn.toString();
120 
121     Opcode newOpcode = getLegalDifferentOpcode(cmpBiasInsn);
122 
123     cmpBiasInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
124 
125     Log.info("Changed " + oldInsnString + " to " + cmpBiasInsn);
126 
127     stats.incrementStat("Changed comparison bias");
128 
129     // Clear cache.
130     cmpBiasInsns = null;
131   }
132 
getLegalDifferentOpcode(MInsn mInsn)133   private Opcode getLegalDifferentOpcode(MInsn mInsn) {
134     Opcode opcode = mInsn.insn.info.opcode;
135     if (opcode == Opcode.CMPG_DOUBLE) {
136       return Opcode.CMPL_DOUBLE;
137     }
138     if (opcode == Opcode.CMPL_DOUBLE) {
139       return Opcode.CMPG_DOUBLE;
140     }
141     if (opcode == Opcode.CMPG_FLOAT) {
142       return Opcode.CMPL_FLOAT;
143     }
144     return Opcode.CMPG_FLOAT;
145   }
146 
isCmpBiasOperation(MInsn mInsn)147   private boolean isCmpBiasOperation(MInsn mInsn) {
148     Opcode opcode = mInsn.insn.info.opcode;
149     if (Opcode.isBetween(opcode, Opcode.CMPL_FLOAT, Opcode.CMPG_DOUBLE)) {
150       return true;
151     }
152     return false;
153   }
154 }
155