All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.berkeley.cs.jqf.fuzz.repro.ReproGuidance Maven / Gradle / Ivy

There is a newer version: 2.0
Show newest version
/*
 * Copyright (c) 2017-2018 The Regents of the University of California
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package edu.berkeley.cs.jqf.fuzz.repro;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import edu.berkeley.cs.jqf.fuzz.guidance.Guidance;
import edu.berkeley.cs.jqf.fuzz.guidance.GuidanceException;
import edu.berkeley.cs.jqf.fuzz.guidance.Result;
import edu.berkeley.cs.jqf.fuzz.util.Coverage;
import edu.berkeley.cs.jqf.instrument.tracing.events.BranchEvent;
import edu.berkeley.cs.jqf.instrument.tracing.events.CallEvent;
import edu.berkeley.cs.jqf.instrument.tracing.events.TraceEvent;

/**
 * A front-end that provides a specified set of inputs for test
 * case reproduction,
 *
 * This class enables reproduction of a test case with an input file
 * generated by a guided fuzzing front-end such as AFL.
 *
 * @author Rohan Padhye
 */
public class ReproGuidance implements Guidance {
    private final File[] inputFiles;
    private final File traceDir;
    private int nextFileIdx = 0;
    private List traceStreams = new ArrayList<>();
    private InputStream inputStream;
    private Coverage coverage = new Coverage();

    private Set branchesCovered;
    HashMap branchDescCache = new HashMap<>();


    /**
     * Constructs an instance of ReproGuidance with a list of
     * input files to replay and a directory where the trace
     * events may be logged.
     *
     * @param inputFiles a list of input files
     * @param traceDir an optional directory, which if non-null will
     *                 be the destination for log files containing event
     *                 traces
     */
    public ReproGuidance(File[] inputFiles, File traceDir) {
        this.inputFiles = inputFiles;
        this.traceDir = traceDir;
        if (Boolean.getBoolean("jqf.repro.logUniqueBranches")) {
            branchesCovered = new HashSet<>();
        }
    }

    /**
     * Constructs an instance of ReproGuidance with a single
     * input file to replay and a directory where the trace
     * events may be logged.
     *
     * @param inputFile an input file
     * @param traceDir an optional directory, which if non-null will
     *                 be the destination for log files containing event
     *                 traces
     */
    public ReproGuidance(File inputFile, File traceDir) {
        this(new File[]{inputFile}, traceDir);
    }

    /**
     * Returns an input stream corresponding to the next input file.
     *
     * @return an input stream corresponding to the next input file
     */
    @Override
    public InputStream getInput() {
        try {
            File inputFile = inputFiles[nextFileIdx];
            this.inputStream = new BufferedInputStream(new FileInputStream(inputFile));
            return this.inputStream;
        } catch (IOException e) {
            throw new GuidanceException(e);
        }
    }

    /**
     * Returns true if there are more input files to replay.
     * @return true if there are more input files to replay
     */
    @Override
    public boolean hasInput() {
        return nextFileIdx < inputFiles.length;
    }

    /**
     * Logs the end of run in the log files, if any.
     *
     * @param result   the result of the fuzzing trial
     * @param error    the error thrown during the trial, or null
     */
    @Override
    public void handleResult(Result result, Throwable error) {
        // Print footer in log files
        String footer = String.format("# End %s\n", inputFiles[nextFileIdx].toString());

        // Close the open input file
        try {
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            throw new GuidanceException(e);
        }


        // Show errors for invalid tests
        if (result == Result.INVALID && error != null) {
            File inputFile = inputFiles[nextFileIdx];
            System.err.println(inputFile.getName() + ": Test run was invalid");
            error.printStackTrace();
        }

        // Increment file
        nextFileIdx++;


    }

    /**
     * Returns a callback that can log trace events or code coverage info.
     *
     * 

If the system property jqf.repro.logUniqueBranches was * set to true, then the callback collects coverage info into * the set {@link #branchesCovered}, which can be accessed using * {@link #getBranchesCovered()}.

* *

Otherwise, if the traceDir was non-null during the construction of * this Guidance instance, then one log file per thread of * execution is created in this directory. The callbacks generated * by this method write trace event descriptions in sequence to * their own thread's log files.

* *

If neither of the above are true, the returned callback simply updates * a total coverage map (see {@link #getCoverage()}.

* * @param thread the thread whose events to handle * @return a callback to log code coverage or execution traces */ @Override public Consumer generateCallBack(Thread thread) { if (branchesCovered != null) { return (e) -> { coverage.handleEvent(e); if (e instanceof BranchEvent) { BranchEvent b = (BranchEvent) e; int hash = b.getIid() * 31 + b.getArm(); String str = branchDescCache.get(hash); if (str == null) { str = String.format("(%09d) %s#%s():%d [%d]", b.getIid(), b.getContainingClass(), b.getContainingMethodName(), b.getLineNumber(), b.getArm()); branchDescCache.put(hash, str); } branchesCovered.add(str); } else if (e instanceof CallEvent) { CallEvent c = (CallEvent) e; String str = branchDescCache.get(c.getIid()); if (str == null) { str = String.format("(%09d) %s#%s():%d --> %s", c.getIid(), c.getContainingClass(), c.getContainingMethodName(), c.getLineNumber(), c.getInvokedMethodName()); branchDescCache.put(c.getIid(), str); } branchesCovered.add(str); } }; } else if (traceDir != null) { File traceFile = new File(traceDir, thread.getName() + ".log"); try { PrintStream out = new PrintStream(traceFile); traceStreams.add(out); // Return an event logging callback return (e) -> { coverage.handleEvent(e); out.println(e); }; } catch (FileNotFoundException e) { // Note the exception, but ignore trace events System.err.println("Could not open trace file: " + traceFile.getAbsolutePath()); } } // If none of the above work, just update coverage return coverage::handleEvent; } /** * Returns a reference to the coverage statistics. * @return a reference to the coverage statistics */ public Coverage getCoverage() { return coverage; } /** * Retyrns the set of branches covered by this repro. * *

This set will only be non-empty if the system * property jqf.repro.logUniqueBranches was * set to true before the guidance instance * was constructed.

* *

The format of each element in this set is a * custom format that strives to be both human and * machine readable.

* * @return the set of branches covered by this repro */ public Set getBranchesCovered() { return branchesCovered; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy