1 /* 2 * Copyright (C) 2016 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.listeners; 18 19 import dexfuzz.ExecutionResult; 20 import dexfuzz.executors.Executor; 21 import dexfuzz.Log; 22 23 import java.io.File; 24 import java.io.IOException; 25 import java.io.PrintWriter; 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.Map; 29 30 /** 31 * Runs bisection search for divergent programs. 32 */ 33 public class BisectionSearchListener extends BaseListener { 34 35 /** 36 * Used to remember the seed used to fuzz the fuzzed file, so we can save it with this 37 * seed as a name, if we find a divergence. 38 */ 39 private long currentSeed; 40 41 /** 42 * Used to remember the name of the file we've fuzzed, so we can save it if we 43 * find a divergence. 44 */ 45 private String fuzzedFile; 46 47 @Override handleSeed(long seed)48 public void handleSeed(long seed) { 49 currentSeed = seed; 50 } 51 52 @Override handleSuccessfullyFuzzedFile(String programName)53 public void handleSuccessfullyFuzzedFile(String programName) { 54 fuzzedFile = programName; 55 } 56 writeToFile(String file, String toWrite)57 private void writeToFile(String file, String toWrite) throws IOException { 58 PrintWriter writer = new PrintWriter(file); 59 writer.write(toWrite); 60 writer.close(); 61 } 62 extractExpectedOutput(ExecutionResult result)63 private String extractExpectedOutput(ExecutionResult result) { 64 StringBuilder builder = new StringBuilder(); 65 // Skip last, artificial output line with return code. 66 for (int i = 0; i < result.output.size() - 1; i++) { 67 builder.append(result.output.get(i)).append("\n"); 68 } 69 return builder.toString(); 70 } 71 72 @Override handleDivergences(Map<String, List<Executor>> outputMap)73 public void handleDivergences(Map<String, List<Executor>> outputMap) { 74 if (outputMap.size() != 2) { 75 // It's unclear which output should be considered reference output. 76 return; 77 } 78 try { 79 File expected_output_file = File.createTempFile("expected_output", ".txt"); 80 String outputFile = String.format("bisection_outputs/%d_out.txt", currentSeed); 81 String logFile = String.format("bisection_outputs/%d_log.txt", currentSeed); 82 List<List<Executor>> executorsGroupedByOutput = 83 new ArrayList<List<Executor>>(outputMap.values()); 84 List<String> outputs = new ArrayList<String>(); 85 for (List<Executor> executors : executorsGroupedByOutput) { 86 outputs.add(extractExpectedOutput(executors.get(0).getResult())); 87 } 88 for (int i = 0; i < 2; i++) { 89 String output = outputs.get(i); 90 String otherOutput = outputs.get(1 - i); 91 List<Executor> executors = executorsGroupedByOutput.get(i); 92 for (Executor executor : executors) { 93 if (executor.isBisectable()) { 94 writeToFile(expected_output_file.getAbsolutePath(), otherOutput); 95 ExecutionResult result = executor.runBisectionSearch(fuzzedFile, 96 expected_output_file.getAbsolutePath(), logFile); 97 writeToFile(outputFile, result.getFlattenedAllWithNewlines()); 98 } 99 } 100 } 101 expected_output_file.delete(); 102 } catch (IOException e) { 103 Log.error( 104 "BisectionSearchListener.handleDivergences() caught an IOException " + e.toString()); 105 } 106 } 107 108 } 109