From 22bd28b643bdd7318c9f2c23d120052e090cfd99 Mon Sep 17 00:00:00 2001
From: katherine-hough <32645020+katherine-hough@users.noreply.github.com>
Date: Fri, 27 Oct 2023 14:32:30 -0400
Subject: [PATCH] * Added support for Jazzer's fuzzerInitialize and
fuzzerTearDown callbacks * Fixed defect where AnalysisReplayerManager#next
could return null * Changed CampaignAnalyzer#closeConnection to more
gracefully terminate the forked analysis process * Changed from using old
Jazzer replayer to using Jazzer's FuzzTarget and LifecycleMethodsInvoker to
perform analysis reruns
---
.../prl/meringue/AnalysisReplayerManager.java | 25 ++++---
.../neu/ccs/prl/meringue/ReplayerManager.java | 2 +-
meringue-jazzer-extension/pom.xml | 4 +-
.../ccs/prl/meringue/FuzzTargetRunner.java | 67 +++++++++++++++++++
.../neu/ccs/prl/meringue/JazzerFramework.java | 59 +++++++++-------
.../neu/ccs/prl/meringue/JazzerReplayer.java | 18 +++--
.../ccs/prl/meringue/JazzerTargetWrapper.java | 28 +-------
.../src/main/resources/META-INF/NOTICE.txt | 2 +-
.../ccs/prl/meringue/CampaignAnalyzer.java | 3 +-
9 files changed, 137 insertions(+), 71 deletions(-)
create mode 100644 meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/FuzzTargetRunner.java
diff --git a/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/AnalysisReplayerManager.java b/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/AnalysisReplayerManager.java
index 6f5fb6a..b93179b 100644
--- a/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/AnalysisReplayerManager.java
+++ b/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/AnalysisReplayerManager.java
@@ -9,6 +9,7 @@
public final class AnalysisReplayerManager implements Closeable, ReplayerManager {
private final ForkConnection connection;
private final StackTraceCleaner cleaner;
+ private File nextInput = null;
public AnalysisReplayerManager(int port, int maxTraceSize) throws IOException {
this.connection = new ForkConnection(port);
@@ -16,21 +17,29 @@ public AnalysisReplayerManager(int port, int maxTraceSize) throws IOException {
}
@Override
- public File nextInput() throws IOException {
- File input;
- try {
- input = connection.receive(File.class);
- } catch (ClassNotFoundException e) {
- throw new AssertionError(e);
+ public File nextInput() {
+ if (!hasNextInput()) {
+ throw new IllegalStateException();
}
// Reset the JaCoCo coverage
RT.getAgent().reset();
- return input;
+ File temp = nextInput;
+ nextInput = null;
+ return temp;
}
@Override
public boolean hasNextInput() {
- return !connection.isClosed();
+ if (nextInput == null) {
+ try {
+ nextInput = connection.receive(File.class);
+ } catch (ClassNotFoundException e) {
+ throw new AssertionError(e);
+ } catch (IOException e) {
+ return false;
+ }
+ }
+ return nextInput != null;
}
@Override
diff --git a/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/ReplayerManager.java b/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/ReplayerManager.java
index 33c9e3c..8c58abf 100644
--- a/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/ReplayerManager.java
+++ b/meringue-core/src/main/java/edu/neu/ccs/prl/meringue/ReplayerManager.java
@@ -7,7 +7,7 @@
public interface ReplayerManager extends Closeable {
File nextInput() throws IOException;
- boolean hasNextInput();
+ boolean hasNextInput() throws IOException;
void handleResult(Throwable failure) throws IOException;
}
diff --git a/meringue-jazzer-extension/pom.xml b/meringue-jazzer-extension/pom.xml
index 0bd0291..1ad9e6f 100644
--- a/meringue-jazzer-extension/pom.xml
+++ b/meringue-jazzer-extension/pom.xml
@@ -27,8 +27,8 @@
com.code-intelligence
- jazzer-replayer
- 0.10.0
+ jazzer
+ ${jazzer.version}
org.apache.maven.surefire
diff --git a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/FuzzTargetRunner.java b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/FuzzTargetRunner.java
new file mode 100644
index 0000000..3c36b83
--- /dev/null
+++ b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/FuzzTargetRunner.java
@@ -0,0 +1,67 @@
+package edu.neu.ccs.prl.meringue;
+
+import com.code_intelligence.jazzer.driver.FuzzTargetHolder;
+import com.code_intelligence.jazzer.driver.FuzzedDataProviderImpl;
+import com.code_intelligence.jazzer.driver.LifecycleMethodsInvoker;
+import com.code_intelligence.jazzer.utils.Log;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+public final class FuzzTargetRunner {
+ private final MethodHandle fuzzTargetMethod;
+ private final LifecycleMethodsInvoker lifecycleMethodsInvoker;
+ private final boolean useFuzzedDataProvider;
+ private final Object fuzzTargetInstance;
+
+ public FuzzTargetRunner(String testClassName) throws Throwable {
+ FuzzTargetHolder.FuzzTarget fuzzTarget = findFuzzTarget(testClassName);
+ lifecycleMethodsInvoker = fuzzTarget.lifecycleMethodsInvoker;
+ fuzzTarget.method.setAccessible(true);
+ fuzzTargetMethod = MethodHandles.lookup().unreflect(fuzzTarget.method);
+ useFuzzedDataProvider = fuzzTarget.usesFuzzedDataProvider();
+ fuzzTargetInstance = fuzzTarget.newInstance.call();
+ lifecycleMethodsInvoker.beforeFirstExecution();
+ Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
+ }
+
+ public Throwable run(byte[] data) {
+ try {
+ if (useFuzzedDataProvider) {
+ try (FuzzedDataProviderImpl provider = FuzzedDataProviderImpl.withJavaData(data)) {
+ runInternal(provider);
+ }
+ } else {
+ runInternal(data);
+ }
+ } catch (Throwable t) {
+ return t;
+ }
+ return null;
+ }
+
+ public void runInternal(Object argument) throws Throwable {
+ lifecycleMethodsInvoker.beforeEachExecution();
+ if (fuzzTargetInstance == null) {
+ fuzzTargetMethod.invoke(argument);
+ } else {
+ fuzzTargetMethod.invoke(fuzzTargetInstance, argument);
+ }
+ }
+
+ private void shutdown() {
+ try {
+ lifecycleMethodsInvoker.afterLastExecution();
+ } catch (Throwable t) {
+ Log.finding(t);
+ }
+ }
+
+ private FuzzTargetHolder.FuzzTarget findFuzzTarget(String testClassName) throws ReflectiveOperationException {
+ Class> clazz = Class.forName("com.code_intelligence.jazzer.driver.FuzzTargetFinder");
+ Method m = clazz.getDeclaredMethod("findFuzzTarget", String.class);
+ m.setAccessible(true);
+ return (FuzzTargetHolder.FuzzTarget) m.invoke(null, testClassName);
+ }
+}
diff --git a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerFramework.java b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerFramework.java
index d3a3a93..ba2f073 100644
--- a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerFramework.java
+++ b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerFramework.java
@@ -15,6 +15,8 @@ public final class JazzerFramework implements FuzzFramework {
private File reproducerDir;
private File workingDir;
private File logFile;
+ private File jazzerExecutable;
+ private File jazzerBootstrapJar;
private boolean quiet = false;
private ProcessBuilder builder;
@@ -26,13 +28,23 @@ public void initialize(CampaignConfiguration config, Properties frameworkArgumen
workingDir = new File(outputDir, "out");
logFile = new File(outputDir, "jazzer.log");
quiet = Boolean.parseBoolean(frameworkArguments.getProperty("quiet", "false"));
- List command = createCommand(config, frameworkArguments, outputDir, reproducerDir, corpusDir);
+ File jazzerExec = getJazzerResource(outputDir, "jazzer");
+ if (!jazzerExec.setExecutable(true)) {
+ throw new IllegalStateException("Failed to assign executable permissions to Jazzer executable");
+ }
+ jazzerExecutable = jazzerExec;
+ getJazzerResource(outputDir, "jazzer_standalone.jar");
+ String resourcePath = File.separator + String.join(File.separator, "com", "code_intelligence",
+ "jazzer", "runtime", "jazzer_bootstrap.jar");
+ jazzerBootstrapJar = getJazzerResource(outputDir, resourcePath, "jazzer_bootstrap.jar");
+ List command = createCommand(config, frameworkArguments);
builder = new ProcessBuilder().command(command).directory(workingDir);
if (config.getEnvironment() != null) {
builder.environment().clear();
builder.environment().putAll(config.getEnvironment());
}
- builder.environment().put("JAVA_HOME", FileUtil.javaExecToJavaHome(config.getJavaExecutable()).getAbsolutePath());
+ builder.environment().put("JAVA_HOME", FileUtil.javaExecToJavaHome(config.getJavaExecutable())
+ .getAbsolutePath());
}
@Override
@@ -46,7 +58,7 @@ public Process startCampaign() throws IOException {
return ProcessUtil.start(builder, true);
} else {
return builder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile))
- .redirectError(ProcessBuilder.Redirect.appendTo(logFile)).start();
+ .redirectError(ProcessBuilder.Redirect.appendTo(logFile)).start();
}
}
@@ -58,7 +70,7 @@ public File[] getCorpusFiles() {
@Override
public File[] getFailureFiles() {
return Arrays.stream(Objects.requireNonNull(workingDir.listFiles()))
- .filter(f -> f.getName().startsWith("crash-")).toArray(File[]::new);
+ .filter(f -> f.getName().startsWith("crash-")).toArray(File[]::new);
}
@Override
@@ -68,7 +80,7 @@ public Class extends Replayer> getReplayerClass() {
@Override
public Collection getRequiredClassPathElements() {
- return Collections.singleton(FileUtil.getClassPathElement(JazzerFramework.class));
+ return Arrays.asList(FileUtil.getClassPathElement(JazzerFramework.class), jazzerBootstrapJar);
}
@Override
@@ -86,14 +98,13 @@ public Process restartCampaign() throws IOException {
return ProcessUtil.start(builder, true);
} else {
return builder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile))
- .redirectError(ProcessBuilder.Redirect.appendTo(logFile)).start();
+ .redirectError(ProcessBuilder.Redirect.appendTo(logFile)).start();
}
}
- private static List createCommand(CampaignConfiguration config, Properties frameworkArguments,
- File outputDir, File reproducerDir, File corpusDir) throws IOException {
+ private List createCommand(CampaignConfiguration config, Properties frameworkArguments) {
List command = new LinkedList<>();
- command.add(getJazzerExecutable(outputDir).getAbsolutePath());
+ command.add(jazzerExecutable.getAbsolutePath());
String classPath = config.getTestClasspathJar().getAbsolutePath() + File.pathSeparator +
FileUtil.getClassPathElement(JazzerFramework.class).getAbsolutePath();
command.add("--cp=" + classPath);
@@ -121,8 +132,7 @@ private static List createCommand(CampaignConfiguration config, Properti
return command;
}
- private static File getJazzerExecutable(File outputDir) throws IOException {
- String executableName = "jazzer";
+ private static File getJazzerResource(File outputDir, String resourceName) throws IOException {
String resourcePathPrefix;
if (SystemUtils.IS_OS_MAC) {
resourcePathPrefix = "mac";
@@ -131,23 +141,22 @@ private static File getJazzerExecutable(File outputDir) throws IOException {
} else {
throw new IllegalStateException("Operating system not supported");
}
- String[] resourceNames = new String[]{"jazzer_standalone.jar", executableName};
File bin = new File(outputDir, "bin");
FileUtil.ensureDirectory(bin);
- File jazzerExec = new File(bin, executableName);
- for (String resourceName : resourceNames) {
- String resourcePath = resourcePathPrefix + File.separator + resourceName;
- try (InputStream in = JazzerFramework.class.getResourceAsStream(resourcePath)) {
- if (in == null) {
- throw new IllegalStateException("Unable to locate Jazzer resource: " + resourcePath);
- }
- File out = new File(bin, resourceName);
- Files.copy(in, out.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ String resourcePath = resourcePathPrefix + File.separator + resourceName;
+ return getJazzerResource(outputDir, resourcePath, resourceName);
+ }
+
+ private static File getJazzerResource(File outputDir, String resourcePath, String resourceName) throws IOException {
+ File bin = new File(outputDir, "bin");
+ FileUtil.ensureDirectory(bin);
+ try (InputStream in = JazzerFramework.class.getResourceAsStream(resourcePath)) {
+ if (in == null) {
+ throw new IllegalStateException("Unable to locate Jazzer resource: " + resourcePath);
}
+ File out = new File(bin, resourceName);
+ Files.copy(in, out.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ return out;
}
- if (!jazzerExec.setExecutable(true)) {
- throw new IllegalStateException("Failed to assign executable permissions to Jazzer executable");
- }
- return jazzerExec;
}
}
diff --git a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerReplayer.java b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerReplayer.java
index 49716d1..4a0423b 100644
--- a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerReplayer.java
+++ b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerReplayer.java
@@ -1,21 +1,27 @@
package edu.neu.ccs.prl.meringue;
-import com.code_intelligence.jazzer.replay.Replayer;
+import com.code_intelligence.jazzer.driver.Opt;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.Arrays;
public final class JazzerReplayer implements edu.neu.ccs.prl.meringue.Replayer {
+ private FuzzTargetRunner runner;
+
@Override
- public void configure(String testClassName, String testMethodName, ClassLoader classLoader) {
- JazzerTargetWrapper.fuzzerInitialize(new String[]{testClassName, testMethodName});
- JazzerTargetWrapper.setRethrow(false);
+ public void configure(String testClassName, String testMethodName, ClassLoader classLoader) throws Throwable {
+ if ("fuzzerTestOneInput".equals(testMethodName)) {
+ runner = new FuzzTargetRunner(testClassName);
+ } else {
+ Opt.targetArgs.setIfDefault(Arrays.asList(testClassName, testMethodName));
+ runner = new FuzzTargetRunner(JazzerTargetWrapper.class.getName());
+ }
}
private Throwable execute(File input) throws IOException {
- Replayer.executeFuzzTarget(JazzerTargetWrapper.class, Files.readAllBytes(input.toPath()));
- return JazzerTargetWrapper.getLastThrown();
+ return runner.run(Files.readAllBytes(input.toPath()));
}
@Override
diff --git a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerTargetWrapper.java b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerTargetWrapper.java
index 7159e88..a9279e6 100644
--- a/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerTargetWrapper.java
+++ b/meringue-jazzer-extension/src/main/java/edu/neu/ccs/prl/meringue/JazzerTargetWrapper.java
@@ -5,41 +5,17 @@
import java.lang.reflect.InvocationTargetException;
public final class JazzerTargetWrapper {
- private static Throwable lastThrown = null;
private static JazzerTarget target;
- private static boolean rethrow = true;
-
- private JazzerTargetWrapper() {
- throw new AssertionError(getClass().getSimpleName() + " is a static utility class and should " +
- "not be instantiated");
- }
public static void fuzzerInitialize(String[] args) {
- try {
- target = new JazzerTarget(args[0], args[1], JazzerTargetWrapper.class.getClassLoader());
- } catch (Throwable t) {
- t.printStackTrace();
- System.exit(-1);
- }
+ target = new JazzerTarget(args[0], args[1], JazzerTargetWrapper.class.getClassLoader());
}
public static void fuzzerTestOneInput(FuzzedDataProvider provider) throws Throwable {
- lastThrown = null;
try {
target.execute(provider);
} catch (InvocationTargetException t) {
- lastThrown = t.getTargetException();
- if (rethrow) {
- throw lastThrown;
- }
+ throw t.getTargetException();
}
}
-
- public static void setRethrow(boolean value) {
- rethrow = value;
- }
-
- public static Throwable getLastThrown() {
- return lastThrown;
- }
}
diff --git a/meringue-jazzer-extension/src/main/resources/META-INF/NOTICE.txt b/meringue-jazzer-extension/src/main/resources/META-INF/NOTICE.txt
index ee50951..f14292b 100644
--- a/meringue-jazzer-extension/src/main/resources/META-INF/NOTICE.txt
+++ b/meringue-jazzer-extension/src/main/resources/META-INF/NOTICE.txt
@@ -1,6 +1,6 @@
==============================================================
Jazzer
-This software release contains components from Jazzer (versions 0.21.1 and 0.10.0).
+This software release contains components from Jazzer (version 0.21.1)).
Jazzer is available at: https://github.com/CodeIntelligenceTesting/jazzer.
Jazzer is licensed under the following terms:
diff --git a/meringue-maven-plugin/src/main/java/edu/neu/ccs/prl/meringue/CampaignAnalyzer.java b/meringue-maven-plugin/src/main/java/edu/neu/ccs/prl/meringue/CampaignAnalyzer.java
index 3f42e27..ce26824 100644
--- a/meringue-maven-plugin/src/main/java/edu/neu/ccs/prl/meringue/CampaignAnalyzer.java
+++ b/meringue-maven-plugin/src/main/java/edu/neu/ccs/prl/meringue/CampaignAnalyzer.java
@@ -101,9 +101,8 @@ private void closeConnection() {
connection = null;
}
if (process != null && process.isAlive()) {
- process.destroyForcibly();
try {
- process.waitFor();
+ ProcessUtil.stop(process);
} catch (InterruptedException e) {
//
}