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.Instruction.InvokeFormatInfo;
26 import dexfuzz.rawdex.Opcode;
27 
28 import java.util.List;
29 import java.util.Random;
30 
31 public 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
getString()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
parseString(String[] elements)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
getNewMutation()93   public Mutation getNewMutation() {
94     return new AssociatedMutation();
95   }
96 
NewMethodCaller()97   public NewMethodCaller() { }
98 
NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations)99   public NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations) {
100     super(rng, stats, mutations);
101     likelihood = 10;
102   }
103 
104   @Override
generateMutation(MutatableCode mutatableCode)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
applyMutation(Mutation uncastMutation)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