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
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.formats.ContainsVRegs;
25
26import java.util.ArrayList;
27import java.util.List;
28import java.util.Random;
29
30public class VRegChanger extends CodeMutator {
31  /**
32   * Every CodeMutator has an AssociatedMutation, representing the
33   * mutation that this CodeMutator can perform, to allow separate
34   * generateMutation() and applyMutation() phases, allowing serialization.
35   */
36  public static class AssociatedMutation extends Mutation {
37    public int vregInsnIdx;
38    public int mutatingVreg;
39    public int newVregValue;
40
41    @Override
42    public String getString() {
43      StringBuilder builder = new StringBuilder();
44      builder.append(vregInsnIdx).append(" ");
45      builder.append(mutatingVreg).append(" ");
46      builder.append(newVregValue);
47      return builder.toString();
48    }
49
50    @Override
51    public void parseString(String[] elements) {
52      vregInsnIdx = Integer.parseInt(elements[2]);
53      mutatingVreg = Integer.parseInt(elements[3]);
54      newVregValue = Integer.parseInt(elements[4]);
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 VRegChanger() { }
67
68  public VRegChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
69    super(rng, stats, mutations);
70    likelihood = 60;
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> vregInsns = null;
77
78  private void generateCachedVRegInsns(MutatableCode mutatableCode) {
79    if (vregInsns != null) {
80      return;
81    }
82
83    vregInsns = new ArrayList<MInsn>();
84    for (MInsn mInsn : mutatableCode.getInstructions()) {
85      if (mInsn.insn.info.format instanceof ContainsVRegs) {
86        vregInsns.add(mInsn);
87      }
88    }
89  }
90
91  @Override
92  protected boolean canMutate(MutatableCode mutatableCode) {
93    if (mutatableCode.registersSize < 2) {
94      Log.debug("Impossible to change vregs in a method with fewer than 2 registers.");
95      return false;
96    }
97
98    for (MInsn mInsn : mutatableCode.getInstructions()) {
99      if (mInsn.insn.info.format instanceof ContainsVRegs) {
100        return true;
101      }
102    }
103
104    return false;
105  }
106
107  @Override
108  protected Mutation generateMutation(MutatableCode mutatableCode) {
109    generateCachedVRegInsns(mutatableCode);
110
111    // Pick a random vreg instruction.
112    int vregInsnIdx = rng.nextInt(vregInsns.size());
113    MInsn vregInsn = vregInsns.get(vregInsnIdx);
114
115    // Get the number of VRegs this instruction uses.
116    int numVregs = ((ContainsVRegs)vregInsn.insn.info.format).getVRegCount();
117
118    // Pick which vreg to mutate.
119    int mutatingVreg = rng.nextInt(numVregs);
120
121    // Find the old index.
122    int oldVregValue = 0;
123
124    switch (mutatingVreg) {
125      case 0:
126        oldVregValue = (int) vregInsn.insn.vregA;
127        break;
128      case 1:
129        oldVregValue = (int) vregInsn.insn.vregB;
130        break;
131      case 2:
132        oldVregValue = (int) vregInsn.insn.vregC;
133        break;
134      default:
135        Log.errorAndQuit("Invalid number of vregs reported by a Format.");
136    }
137
138    // Search for a new vreg value.
139    int newVregValue = oldVregValue;
140    while (newVregValue == oldVregValue) {
141      newVregValue = rng.nextInt(mutatableCode.registersSize);
142    }
143
144    AssociatedMutation mutation = new AssociatedMutation();
145    mutation.setup(this.getClass(), mutatableCode);
146    mutation.vregInsnIdx = vregInsnIdx;
147    mutation.mutatingVreg = mutatingVreg;
148    mutation.newVregValue = newVregValue;
149    return mutation;
150  }
151
152  @Override
153  protected void applyMutation(Mutation uncastMutation) {
154    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
155    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
156    MutatableCode mutatableCode = mutation.mutatableCode;
157
158    generateCachedVRegInsns(mutatableCode);
159
160    MInsn vregInsn = vregInsns.get(mutation.vregInsnIdx);
161
162    // Remember what the instruction used to look like.
163    String oldInsnString = vregInsn.toString();
164
165    int oldVregValue = 0;
166
167    String vregId = "A";
168    switch (mutation.mutatingVreg) {
169      case 0:
170        oldVregValue = (int) vregInsn.insn.vregA;
171        vregInsn.insn.vregA = (long) mutation.newVregValue;
172        break;
173      case 1:
174        oldVregValue = (int) vregInsn.insn.vregB;
175        vregInsn.insn.vregB = (long) mutation.newVregValue;
176        vregId = "B";
177        break;
178      case 2:
179        oldVregValue = (int) vregInsn.insn.vregC;
180        vregInsn.insn.vregC = (long) mutation.newVregValue;
181        vregId = "C";
182        break;
183      default:
184        Log.errorAndQuit("Invalid number of vregs specified in a VRegChanger mutation.");
185    }
186
187    Log.info("In " + oldInsnString + " changed v" + vregId + ": v" + oldVregValue
188        + " to v" + mutation.newVregValue);
189
190    stats.incrementStat("Changed a virtual register");
191
192    // Clear cache.
193    vregInsns = null;
194  }
195}
196