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