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.Instruction;
25import dexfuzz.rawdex.Instruction.InvokeFormatInfo;
26import dexfuzz.rawdex.Opcode;
27
28import java.util.List;
29import java.util.Random;
30
31public class NewMethodCaller 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 enum InvokeType {
39      VIRTUAL,
40      DIRECT,
41      SUPER,
42      STATIC,
43      INTERFACE
44    }
45
46    public int insertionIdx;
47    public InvokeType invokeType;
48    public String className;
49    public String methodName;
50    public String signature;
51    public int numArgs;
52    public int[] args;
53
54    @Override
55    public String getString() {
56      StringBuilder argsString = new StringBuilder();
57      for (int i = 0; i < numArgs; i++) {
58        argsString.append(args[i]);
59        if (i < (numArgs - 1)) {
60          argsString.append(" ");
61        }
62      }
63      String result = String.format("%d %d %s %s %s %d %s",
64          insertionIdx,
65          invokeType.ordinal(),
66          className,
67          methodName,
68          signature,
69          numArgs,
70          argsString);
71      return result;
72    }
73
74    @Override
75    public void parseString(String[] elements) {
76      insertionIdx = Integer.parseInt(elements[2]);
77      invokeType = InvokeType.values()[Integer.parseInt(elements[3])];
78      className = elements[4];
79      methodName = elements[5];
80      signature = elements[6];
81      numArgs = Integer.parseInt(elements[7]);
82      args = new int[numArgs];
83      for (int i = 0; i < numArgs; i++) {
84        args[i] = Integer.parseInt(elements[8 + i]);
85      }
86    }
87  }
88
89  // The following two methods are here for the benefit of MutationSerializer,
90  // so it can create a CodeMutator and get the correct associated Mutation, as it
91  // reads in mutations from a dump of mutations.
92  @Override
93  public Mutation getNewMutation() {
94    return new AssociatedMutation();
95  }
96
97  public NewMethodCaller() { }
98
99  public NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations) {
100    super(rng, stats, mutations);
101    likelihood = 10;
102  }
103
104  @Override
105  protected Mutation generateMutation(MutatableCode mutatableCode) {
106    // Find the insertion point.
107    int insertionIdx = 0;
108    boolean foundInsn = false;
109
110    while (!foundInsn) {
111      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
112      MInsn insertionPoint =
113          mutatableCode.getInstructionAt(insertionIdx);
114      foundInsn = true;
115
116      // Don't want to insert instructions where there are raw instructions for now.
117      if (insertionPoint.insn.justRaw) {
118        foundInsn = false;
119      }
120    }
121
122    AssociatedMutation mutation = new AssociatedMutation();
123    mutation.setup(this.getClass(), mutatableCode);
124    mutation.insertionIdx = insertionIdx;
125
126    // TODO: Right now this mutator can only insert calls to System.gc() Add more!
127
128    mutation.invokeType = AssociatedMutation.InvokeType.STATIC;
129    mutation.className = "Ljava/lang/System;";
130    mutation.methodName = "gc";
131    mutation.signature = "()V";
132    mutation.numArgs = 0;
133
134    return mutation;
135  }
136
137  @Override
138  protected void applyMutation(Mutation uncastMutation) {
139    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
140    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
141    MutatableCode mutatableCode = mutation.mutatableCode;
142
143    MInsn newInsn = new MInsn();
144    newInsn.insn = new Instruction();
145
146    switch (mutation.invokeType) {
147      case VIRTUAL:
148        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL);
149        break;
150      case DIRECT:
151        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_DIRECT);
152        break;
153      case SUPER:
154        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_SUPER);
155        break;
156      case STATIC:
157        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_STATIC);
158        break;
159      case INTERFACE:
160        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_INTERFACE);
161        break;
162      default:
163    }
164
165    // TODO: Handle more than just static invokes.
166
167    int methodIdx = mutatableCode.program.getNewItemCreator()
168        .findOrCreateMethodId(mutation.className,
169            mutation.methodName, mutation.signature);
170
171    newInsn.insn.vregB = methodIdx;
172    newInsn.insn.invokeFormatInfo = new InvokeFormatInfo();
173
174    // TODO: More field population, when we call methods that take arguments.
175
176    MInsn insertionPoint =
177        mutatableCode.getInstructionAt(mutation.insertionIdx);
178
179    Log.info(String.format("Called new method %s %s %s, inserting at %s",
180        mutation.className, mutation.methodName, mutation.signature, insertionPoint));
181
182    stats.incrementStat("Called new method");
183
184    mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
185  }
186}
187