• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.executors;
18 
19 import dexfuzz.ExecutionResult;
20 import dexfuzz.Options;
21 import dexfuzz.StreamConsumer;
22 import dexfuzz.listeners.BaseListener;
23 
24 /**
25  * Base class containing the common methods for executing a particular backend of ART.
26  */
27 public abstract class Executor {
28   private StreamConsumer outputConsumer;
29   private StreamConsumer errorConsumer;
30 
31   protected ExecutionResult executionResult;
32   protected String executeClass;
33 
34   // Set by subclasses.
35   protected String name;
36   protected int timeout;
37   protected BaseListener listener;
38   protected String testLocation;
39   protected Architecture architecture;
40   protected Device device;
41   private boolean needsCleanCodeCache;
42   private boolean isBisectable;
43 
Executor(String name, int timeout, BaseListener listener, Architecture architecture, Device device, boolean needsCleanCodeCache, boolean isBisectable)44   protected Executor(String name, int timeout, BaseListener listener, Architecture architecture,
45       Device device, boolean needsCleanCodeCache, boolean isBisectable) {
46     executeClass = Options.executeClass;
47 
48     if (Options.shortTimeouts) {
49       this.timeout = 2;
50     } else {
51       this.timeout = timeout;
52     }
53 
54     this.name = name;
55     this.listener = listener;
56     this.architecture = architecture;
57     this.device = device;
58     this.needsCleanCodeCache = needsCleanCodeCache;
59     this.isBisectable = isBisectable;
60 
61     if (Options.executeOnHost) {
62       this.testLocation = System.getProperty("user.dir");
63     } else {
64       this.testLocation = Options.executeDirectory;
65     }
66 
67     outputConsumer = new StreamConsumer();
68     outputConsumer.start();
69     errorConsumer = new StreamConsumer();
70     errorConsumer.start();
71   }
72 
73   /**
74    * Called by subclass Executors in their execute() implementations.
75    */
executeCommandWithTimeout(String command, boolean captureOutput)76   protected ExecutionResult executeCommandWithTimeout(String command, boolean captureOutput) {
77     String timeoutString = "timeout " + timeout + " ";
78     return device.executeCommand(timeoutString + device.getExecutionShellPrefix() + command,
79         captureOutput, outputConsumer, errorConsumer);
80   }
81 
82   /**
83    * Call this to make sure the StreamConsumer threads are stopped.
84    */
shutdown()85   public void shutdown() {
86     outputConsumer.shutdown();
87     errorConsumer.shutdown();
88   }
89 
90   /**
91    * Called by the Fuzzer after each execution has finished, to clear the results.
92    */
reset()93   public void reset() {
94     executionResult = null;
95   }
96 
97   /**
98    * Called by the Fuzzer to verify the mutated program using the host-side dex2oat.
99    */
verifyOnHost(String programName)100   public boolean verifyOnHost(String programName) {
101     StringBuilder commandBuilder = new StringBuilder();
102     commandBuilder.append("dex2oat ");
103 
104     commandBuilder.append("--instruction-set=").append(architecture.asString());
105     commandBuilder.append(" --instruction-set-features=default ");
106 
107     // Select the correct boot image.
108     commandBuilder.append("--boot-image=").append(device.getAndroidProductOut());
109     if (device.noBootImageAvailable()) {
110       commandBuilder.append("/data/art-test/core.art ");
111     } else {
112       commandBuilder.append("/system/framework/boot.art ");
113     }
114 
115     commandBuilder.append("--oat-file=output.oat ");
116     commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" ");
117     commandBuilder.append("--runtime-arg -classpath ");
118     commandBuilder.append("--runtime-arg ").append(programName).append(" ");
119     commandBuilder.append("--dex-file=").append(programName).append(" ");
120     commandBuilder.append("--compiler-filter=quicken --runtime-arg -Xnorelocate ");
121 
122     ExecutionResult verificationResult = device.executeCommand(commandBuilder.toString(), true,
123         outputConsumer, errorConsumer);
124 
125     boolean success = true;
126 
127     if (verificationResult.isSigabort()) {
128       listener.handleHostVerificationSigabort(verificationResult);
129       success = false;
130     }
131 
132     if (success) {
133       // Search for a keyword that indicates verification was not successful.
134       // TODO: Determine if dex2oat crashed?
135       for (String line : verificationResult.error) {
136         if (line.contains("Verification error")
137             || line.contains("Failure to verify dex file")) {
138           success = false;
139         }
140         if (Options.dumpVerify) {
141           // Strip out the start of the log lines.
142           listener.handleDumpVerify(line.replaceFirst(".*(cc|h):\\d+] ",  ""));
143         }
144       }
145     }
146 
147     if (!success) {
148       listener.handleFailedHostVerification(verificationResult);
149     }
150 
151     device.executeCommand("rm output.oat", false);
152 
153     return success;
154   }
155 
156   /**
157    * Called by the Fuzzer to upload the program to the target device.
158    */
prepareProgramForExecution(String programName)159   public void prepareProgramForExecution(String programName) {
160     if (!Options.executeOnHost) {
161       device.pushProgramToDevice(programName, testLocation);
162     }
163 
164     if (needsCleanCodeCache) {
165       // Get the device to clean the code cache
166       device.cleanCodeCache(architecture, testLocation, programName);
167     }
168   }
169 
170   /**
171    * Executor subclasses need to override this, to construct their arguments for dalvikvm
172    * invocation correctly.
173    */
constructCommand(String programName)174   protected abstract String constructCommand(String programName);
175 
176   /**
177    * Executes runtime.
178    */
execute(String programName)179   public void execute(String programName) {
180     String command = "";
181     String androidRoot = Options.androidRoot.trim();
182     if (androidRoot.length() != 0) {
183       command = "PATH=" + androidRoot + "/bin ";
184       command += "ANDROID_ROOT=" + androidRoot + " ";
185       command += "LD_LIBRARY_PATH="+ androidRoot + "/lib:" + androidRoot + "/lib64 ";
186     }
187     command += constructCommand(programName);
188     executionResult = executeCommandWithTimeout(command, true);
189   }
190 
191   /**
192    * Runs bisection bug search.
193    */
runBisectionSearch(String programName, String expectedOutputFile, String logFile)194   public ExecutionResult runBisectionSearch(String programName, String expectedOutputFile, String logFile) {
195     assert(isBisectable);
196     String runtimeCommand = constructCommand(programName);
197     StringBuilder commandBuilder = new StringBuilder();
198     commandBuilder.append("bisection_search.py --raw-cmd '").append(runtimeCommand);
199     commandBuilder.append("' --expected-output=").append(expectedOutputFile);
200     commandBuilder.append(" --logfile=").append(logFile);
201     if (!device.isHost()) {
202       commandBuilder.append(" --device");
203       if (device.isUsingSpecificDevice()) {
204         commandBuilder.append(" --specific-device=").append(device.getName());
205       }
206     }
207     return device.executeCommand(commandBuilder.toString(), true, outputConsumer, errorConsumer);
208   }
209 
210   /**
211    * Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor.
212    */
getArchitecture()213   public Architecture getArchitecture() {
214     return architecture;
215   }
216 
217   /**
218    * Used by the Fuzzer to get result of execution.
219    */
getResult()220   public ExecutionResult getResult() {
221     return executionResult;
222   }
223 
224   /**
225    * Because dex2oat can accept a program with soft errors on the host, and then fail after
226    * performing hard verification on the target, we need to check if the Executor detected
227    * a target verification failure, before doing anything else with the resulting output.
228    * Used by the Fuzzer.
229    */
didTargetVerify()230   public boolean didTargetVerify() {
231     // TODO: Remove this once host-verification can be forced to always fail?
232     String output = executionResult.getFlattenedAll();
233     if (output.contains("VerifyError") || output.contains("Verification failed on class")) {
234       return false;
235     }
236     return true;
237   }
238 
getName()239   public String getName() {
240     return name;
241   }
242 
finishedWithProgramOnDevice()243   public void finishedWithProgramOnDevice() {
244     device.resetProgramPushed();
245   }
246 
isBisectable()247   public boolean isBisectable() {
248     return isBisectable;
249   }
250 }
251