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.List;
28 import java.util.Random;
29 
30 public class ValuePrinter 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 printedOutputIdx;
38 
39     @Override
getString()40     public String getString() {
41       return Integer.toString(printedOutputIdx);
42     }
43 
44     @Override
parseString(String[] elements)45     public void parseString(String[] elements) {
46       printedOutputIdx = Integer.parseInt(elements[2]);
47     }
48   }
49 
50   // The following two methods are here for the benefit of MutationSerializer,
51   // so it can create a CodeMutator and get the correct associated Mutation, as it
52   // reads in mutations from a dump of mutations.
53   @Override
getNewMutation()54   public Mutation getNewMutation() {
55     return new AssociatedMutation();
56   }
57 
ValuePrinter()58   public ValuePrinter() { }
59 
ValuePrinter(Random rng, MutationStats stats, List<Mutation> mutations)60   public ValuePrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
61     super(rng, stats, mutations);
62     likelihood = 40;
63   }
64 
65   @Override
canMutate(MutatableCode mutatableCode)66   protected boolean canMutate(MutatableCode mutatableCode) {
67     for (MInsn mInsn : mutatableCode.getInstructions()) {
68       if (getInstructionOutputType(mInsn) != OutputType.UNKNOWN) {
69         return true;
70       }
71     }
72 
73     Log.debug("No instructions with legible output in method, skipping.");
74     return false;
75   }
76 
77   @Override
generateMutation(MutatableCode mutatableCode)78   protected Mutation generateMutation(MutatableCode mutatableCode) {
79     // Find an instruction whose output we wish to print.
80     int printedOutputIdx = 0;
81     boolean foundInsn = false;
82 
83     while (!foundInsn) {
84       printedOutputIdx = rng.nextInt(mutatableCode.getInstructionCount());
85       MInsn insnOutputToPrint =
86           mutatableCode.getInstructionAt(printedOutputIdx);
87       foundInsn = true;
88 
89       // Don't want to insert instructions where there are raw instructions for now.
90       if (insnOutputToPrint.insn.justRaw) {
91         foundInsn = false;
92       }
93 
94       if (getInstructionOutputType(insnOutputToPrint) == OutputType.UNKNOWN) {
95         foundInsn = false;
96       }
97     }
98 
99     AssociatedMutation mutation = new AssociatedMutation();
100     mutation.setup(this.getClass(), mutatableCode);
101     mutation.printedOutputIdx = printedOutputIdx;
102     return mutation;
103   }
104 
105   @Override
applyMutation(Mutation uncastMutation)106   protected void applyMutation(Mutation uncastMutation) {
107     // Cast the Mutation to our AssociatedMutation, so we can access its fields.
108     AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
109     MutatableCode mutatableCode = mutation.mutatableCode;
110 
111     MInsn insnOutputToPrint =
112         mutatableCode.getInstructionAt(mutation.printedOutputIdx);
113 
114     int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
115         "Ljava/lang/System;",
116         "Ljava/io/PrintStream;",
117         "out");
118 
119     OutputType outputType = getInstructionOutputType(insnOutputToPrint);
120 
121     if (outputType == OutputType.UNKNOWN) {
122       Log.errorAndQuit("Requested to print output of an instruction, whose output"
123           + " type is unknown.");
124     }
125     int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
126         "Ljava/io/PrintStream;",
127         "print",
128         outputType.getSignatureForPrintln());
129 
130     boolean isWide = false;
131     boolean isRef = false;
132     if (outputType == OutputType.LONG || outputType == OutputType.DOUBLE) {
133       isWide = true;
134     }
135     if (outputType == OutputType.STRING) {
136       isRef = true;
137     }
138 
139     // If we're printing a wide value, we need to allocate 3 registers!
140     if (isWide) {
141       mutatableCode.allocateTemporaryVRegs(3);
142     } else {
143       mutatableCode.allocateTemporaryVRegs(2);
144     }
145 
146     int streamRegister = mutatableCode.getTemporaryVReg(0);
147     int valueRegister = mutatableCode.getTemporaryVReg(1);
148 
149     // Copy the value we want to print to the 2nd temporary register
150     // Then load the out stream
151     // Then call print(out stream, value)
152 
153     MInsn valueCopyInsn = new MInsn();
154     valueCopyInsn.insn = new Instruction();
155     if (isRef) {
156       valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
157     } else if (isWide) {
158       valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
159     } else {
160       valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
161     }
162     valueCopyInsn.insn.vregB = insnOutputToPrint.insn.vregA;
163     valueCopyInsn.insn.vregA = valueRegister;
164 
165     MInsn streamLoadInsn = new MInsn();
166     streamLoadInsn.insn = new Instruction();
167     streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
168     streamLoadInsn.insn.vregB = outFieldIdx;
169     streamLoadInsn.insn.vregA = streamRegister;
170 
171     MInsn invokeInsn = new MInsn();
172     invokeInsn.insn = new Instruction();
173     invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
174     if (isWide) {
175       invokeInsn.insn.vregA = 3;
176     } else {
177       invokeInsn.insn.vregA = 2;
178     }
179     invokeInsn.insn.vregB = printMethodIdx;
180     invokeInsn.insn.vregC = streamRegister;
181 
182     Log.info(String.format("Printing output value of instruction %s", insnOutputToPrint));
183 
184     stats.incrementStat("Printed output value");
185 
186     mutatableCode.insertInstructionAfter(invokeInsn, mutation.printedOutputIdx);
187     mutatableCode.insertInstructionAfter(streamLoadInsn, mutation.printedOutputIdx);
188     mutatableCode.insertInstructionAfter(valueCopyInsn, mutation.printedOutputIdx);
189 
190     mutatableCode.finishedUsingTemporaryVRegs();
191   }
192 
193   private static enum OutputType {
194     STRING("(Ljava/lang/String;)V"),
195     BOOLEAN("(Z)V"),
196     BYTE("(B)V"),
197     CHAR("(C)V"),
198     SHORT("(S)V"),
199     INT("(I)V"),
200     LONG("(J)V"),
201     FLOAT("(F)V"),
202     DOUBLE("(D)V"),
203     UNKNOWN("UNKNOWN");
204 
205     private String printingSignature;
OutputType(String s)206     private OutputType(String s) {
207       printingSignature = s;
208     }
209 
getSignatureForPrintln()210     public String getSignatureForPrintln() {
211       return printingSignature;
212     }
213   }
214 
getInstructionOutputType(MInsn mInsn)215   private OutputType getInstructionOutputType(MInsn mInsn) {
216     Opcode opcode = mInsn.insn.info.opcode;
217     if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
218       return OutputType.STRING;
219     }
220     if (opcode == Opcode.IGET_BOOLEAN || opcode == Opcode.SGET_BOOLEAN) {
221       return OutputType.BOOLEAN;
222     }
223     if (opcode == Opcode.IGET_BYTE || opcode == Opcode.SGET_BYTE
224         || opcode == Opcode.INT_TO_BYTE) {
225       return OutputType.BYTE;
226     }
227     if (opcode == Opcode.IGET_CHAR || opcode == Opcode.SGET_CHAR
228         || opcode == Opcode.INT_TO_CHAR) {
229       return OutputType.CHAR;
230     }
231     if (opcode == Opcode.IGET_SHORT || opcode == Opcode.SGET_SHORT
232         || opcode == Opcode.INT_TO_SHORT) {
233       return OutputType.SHORT;
234     }
235     if (opcode == Opcode.NEG_INT || opcode == Opcode.NOT_INT
236         || opcode == Opcode.LONG_TO_INT || opcode == Opcode.FLOAT_TO_INT
237         || opcode == Opcode.DOUBLE_TO_INT
238         || Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT)
239         || Opcode.isBetween(opcode, Opcode.ADD_INT_2ADDR, Opcode.USHR_INT_2ADDR)
240         || Opcode.isBetween(opcode, Opcode.ADD_INT_LIT16, Opcode.USHR_INT_LIT8)) {
241       return OutputType.INT;
242     }
243     if (opcode == Opcode.NEG_LONG || opcode == Opcode.NOT_LONG
244         || opcode == Opcode.INT_TO_LONG || opcode == Opcode.FLOAT_TO_LONG
245         || opcode == Opcode.DOUBLE_TO_LONG
246         || Opcode.isBetween(opcode, Opcode.ADD_LONG, Opcode.USHR_LONG)
247         || Opcode.isBetween(opcode, Opcode.ADD_LONG_2ADDR, Opcode.USHR_LONG_2ADDR)) {
248       return OutputType.LONG;
249     }
250     if (opcode == Opcode.NEG_FLOAT
251         || opcode == Opcode.INT_TO_FLOAT || opcode == Opcode.LONG_TO_FLOAT
252         || opcode == Opcode.DOUBLE_TO_FLOAT
253         || Opcode.isBetween(opcode, Opcode.ADD_FLOAT, Opcode.REM_FLOAT)
254         || Opcode.isBetween(opcode, Opcode.ADD_FLOAT_2ADDR, Opcode.REM_FLOAT_2ADDR)) {
255       return OutputType.FLOAT;
256     }
257     if (opcode == Opcode.NEG_DOUBLE
258         || opcode == Opcode.INT_TO_DOUBLE || opcode == Opcode.LONG_TO_DOUBLE
259         || opcode == Opcode.FLOAT_TO_DOUBLE
260         || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE, Opcode.REM_DOUBLE)
261         || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE_2ADDR, Opcode.REM_DOUBLE_2ADDR)) {
262       return OutputType.DOUBLE;
263     }
264     return OutputType.UNKNOWN;
265   }
266 }
267