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 Executor(String name, int timeout, BaseListener listener, Architecture architecture, Device device, boolean needsCleanCodeCache)43 protected Executor(String name, int timeout, BaseListener listener, Architecture architecture, 44 Device device, boolean needsCleanCodeCache) { 45 executeClass = Options.executeClass; 46 47 if (Options.shortTimeouts) { 48 this.timeout = 2; 49 } else { 50 this.timeout = timeout; 51 } 52 53 this.name = name; 54 this.listener = listener; 55 this.architecture = architecture; 56 this.device = device; 57 this.needsCleanCodeCache = needsCleanCodeCache; 58 59 if (Options.executeOnHost) { 60 this.testLocation = System.getProperty("user.dir"); 61 } else { 62 this.testLocation = Options.executeDirectory; 63 } 64 65 outputConsumer = new StreamConsumer(); 66 outputConsumer.start(); 67 errorConsumer = new StreamConsumer(); 68 errorConsumer.start(); 69 } 70 71 /** 72 * Called by subclass Executors in their execute() implementations. 73 */ executeCommandWithTimeout(String command, boolean captureOutput)74 protected ExecutionResult executeCommandWithTimeout(String command, boolean captureOutput) { 75 String timeoutString = "timeout " + timeout + " "; 76 return device.executeCommand(timeoutString + device.getExecutionShellPrefix() + command, 77 captureOutput, outputConsumer, errorConsumer); 78 } 79 80 /** 81 * Call this to make sure the StreamConsumer threads are stopped. 82 */ shutdown()83 public void shutdown() { 84 outputConsumer.shutdown(); 85 errorConsumer.shutdown(); 86 } 87 88 /** 89 * Called by the Fuzzer after each execution has finished, to clear the results. 90 */ reset()91 public void reset() { 92 executionResult = null; 93 } 94 95 /** 96 * Called by the Fuzzer to verify the mutated program using the host-side dex2oat. 97 */ verifyOnHost(String programName)98 public boolean verifyOnHost(String programName) { 99 StringBuilder commandBuilder = new StringBuilder(); 100 commandBuilder.append("dex2oat "); 101 102 commandBuilder.append("--instruction-set=").append(architecture.asString()); 103 commandBuilder.append(" --instruction-set-features=default "); 104 105 // Select the correct boot image. 106 commandBuilder.append("--boot-image=").append(device.getAndroidProductOut()); 107 if (device.noBootImageAvailable()) { 108 commandBuilder.append("/data/art-test/core.art "); 109 } else { 110 commandBuilder.append("/system/framework/boot.art "); 111 } 112 113 commandBuilder.append("--oat-file=output.oat "); 114 commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" "); 115 commandBuilder.append("--runtime-arg -classpath "); 116 commandBuilder.append("--runtime-arg ").append(programName).append(" "); 117 commandBuilder.append("--dex-file=").append(programName).append(" "); 118 commandBuilder.append("--compiler-filter=interpret-only --runtime-arg -Xnorelocate "); 119 120 ExecutionResult verificationResult = device.executeCommand(commandBuilder.toString(), true, 121 outputConsumer, errorConsumer); 122 123 boolean success = true; 124 125 if (verificationResult.isSigabort()) { 126 listener.handleHostVerificationSigabort(verificationResult); 127 success = false; 128 } 129 130 if (success) { 131 // Search for a keyword that indicates verification was not successful. 132 // TODO: Determine if dex2oat crashed? 133 for (String line : verificationResult.error) { 134 if (line.contains("Verification error") 135 || line.contains("Failure to verify dex file")) { 136 success = false; 137 } 138 if (Options.dumpVerify) { 139 // Strip out the start of the log lines. 140 listener.handleDumpVerify(line.replaceFirst(".*(cc|h):\\d+] ", "")); 141 } 142 } 143 } 144 145 if (!success) { 146 listener.handleFailedHostVerification(verificationResult); 147 } 148 149 device.executeCommand("rm output.oat", false); 150 151 return success; 152 } 153 154 /** 155 * Called by the Fuzzer to upload the program to the target device. 156 */ prepareProgramForExecution(String programName)157 public void prepareProgramForExecution(String programName) { 158 if (!Options.executeOnHost) { 159 device.pushProgramToDevice(programName, testLocation); 160 } 161 162 if (needsCleanCodeCache) { 163 // Get the device to clean the code cache 164 device.cleanCodeCache(architecture, testLocation, programName); 165 } 166 } 167 168 /** 169 * Executor subclasses need to override this, to construct their arguments for dalvikvm 170 * invocation correctly. 171 */ execute(String programName)172 public abstract void execute(String programName); 173 174 /** 175 * Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor. 176 */ getArchitecture()177 public Architecture getArchitecture() { 178 return architecture; 179 } 180 181 /** 182 * Used by the Fuzzer to get result of execution. 183 */ getResult()184 public ExecutionResult getResult() { 185 return executionResult; 186 } 187 188 /** 189 * Because dex2oat can accept a program with soft errors on the host, and then fail after 190 * performing hard verification on the target, we need to check if the Executor detected 191 * a target verification failure, before doing anything else with the resulting output. 192 * Used by the Fuzzer. 193 */ didTargetVerify()194 public boolean didTargetVerify() { 195 // TODO: Remove this once host-verification can be forced to always fail? 196 String output = executionResult.getFlattenedAll(); 197 if (output.contains("VerifyError") || output.contains("Verification failed on class")) { 198 return false; 199 } 200 return true; 201 } 202 getName()203 public String getName() { 204 return name; 205 } 206 finishedWithProgramOnDevice()207 public void finishedWithProgramOnDevice() { 208 device.resetProgramPushed(); 209 } 210 } 211