Add Jazzer API method for reporting findings from hooks

The method Jazzer.reportFindingFromHook can be used to reliably report a
finding from a hook, even if the hook is called within an overly generic
exception handler.

In order to make the fuzz target return quickly, a hard to catch error
is thrown after the finding has been registered via the global finding
variable. This is not guaranteed to terminate the execution of the fuzz
target right away, but the global finding variable ensures that the
finding will be reported eventually (unless the fuzz target never
returns).
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java
new file mode 100644
index 0000000..e36f974
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java
@@ -0,0 +1,52 @@
+// Copyright 2021 Code Intelligence GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.code_intelligence.jazzer.api;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Helper class with static methods that interact with Jazzer at runtime.
+ */
+final public class Jazzer {
+  private static Class jazzerInternal = null;
+
+  static {
+    try {
+      jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal");
+    } catch (ClassNotFoundException ignore) {
+      // Not running in the context of the agent. This is fine as long as no methods are called on
+      // this class.
+    }
+  }
+
+  /**
+   * Make Jazzer report the provided {@link Throwable} as a finding.
+   *
+   * <b>Note:</b> This method must only be called from a method hook. In a
+   * fuzz target, simply throw an exception to trigger a finding.
+   * @param finding the finding that Jazzer should report
+   */
+  public static void reportFindingFromHook(Throwable finding) {
+    try {
+      jazzerInternal.getMethod("reportFindingFromHook", Throwable.class).invoke(null, finding);
+    } catch (NullPointerException | IllegalAccessException | InvocationTargetException
+        | NoSuchMethodException e) {
+      // We can only reach this point if the runtime is not in the classpath, but it must be if
+      // hooks work and this function should only be called from them.
+      System.err.println("ERROR: Jazzer.reportFindingFromHook must be called from a method hook");
+      System.exit(1);
+    }
+  }
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchThrowable.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchThrowable.java
new file mode 100644
index 0000000..948d5a1
--- /dev/null
+++ b/agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchThrowable.java
@@ -0,0 +1,82 @@
+// Copyright 2021 Code Intelligence GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.code_intelligence.jazzer.runtime;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * An Error that rethrows itself when any of its getters is invoked.
+ */
+public class HardToCatchThrowable extends Error {
+  public HardToCatchThrowable() {
+    super();
+  }
+
+  @Override
+  public String getMessage() {
+    throw this;
+  }
+
+  @Override
+  public String getLocalizedMessage() {
+    throw this;
+  }
+
+  @Override
+  public synchronized Throwable initCause(Throwable cause) {
+    throw this;
+  }
+
+  @Override
+  public String toString() {
+    throw this;
+  }
+
+  @Override
+  public void printStackTrace() {
+    throw this;
+  }
+
+  @Override
+  public void printStackTrace(PrintStream s) {
+    throw this;
+  }
+
+  @Override
+  public void printStackTrace(PrintWriter s) {
+    throw this;
+  }
+
+  @Override
+  public StackTraceElement[] getStackTrace() {
+    throw this;
+  }
+
+  @Override
+  public int hashCode() {
+    throw this;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    throw this;
+  }
+
+  @Override
+  public Object clone() {
+    throw this;
+  }
+}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java
index 7424d2b..6988a6e 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java
+++ b/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java
@@ -17,4 +17,13 @@
 final public class JazzerInternal {
   // Accessed from native code.
   private static Throwable lastFinding;
+
+  // Accessed from api.Jazzer via reflection.
+  public static void reportFindingFromHook(Throwable finding) {
+    lastFinding = finding;
+    // Throw an Error that is hard to catch (short of outright ignoring it) in order to quickly
+    // terminate the execution of the fuzz target. The finding will be reported as soon as the fuzz
+    // target returns even if this Error is swallowed.
+    throw new HardToCatchThrowable();
+  }
 }