aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Teffeteller <mteffeteller@google.com>2023-06-13 12:51:38 -0500
committerGitHub <noreply@github.com>2023-06-13 19:51:38 +0200
commit30decf81a147c66fa5a098072c38ab6924ba0aa6 (patch)
tree43b4d2b2566e8b646c99bd5d924c418494a14f5b
parent0d072ce73b62bbd35d6119e4b56931011447e318 (diff)
downloadjazzer-api-30decf81a147c66fa5a098072c38ab6924ba0aa6.tar.gz
Add ability to initialize ART for android java fuzzers (#743)
* Add ability to initialize ART for android java fuzzers * Fix formatting and address feedback from maintainers * Run formatter * Disable windows * Skip windows on all new build targets * run buildifier formatter * Update with code feedback, change name of new init parameters * Run formatter * Address code feedback from maintainers * remote android from opt deps * Update AndroidRuntime.java remove reliance on opt androidruntime * Update BUILD.bazel remove opt from android runtime deps * Update Opt.java * Update Opt.java * Update Jazzer.java * Update Jazzer.java * Fix LD_LIBRARY_PATH and address other feedback * Run formatter
-rw-r--r--src/main/java/com/code_intelligence/jazzer/BUILD.bazel1
-rw-r--r--src/main/java/com/code_intelligence/jazzer/Jazzer.java46
-rw-r--r--src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java67
-rw-r--r--src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel15
-rw-r--r--src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel1
-rw-r--r--src/main/java/com/code_intelligence/jazzer/driver/Opt.java33
-rw-r--r--src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel11
-rw-r--r--src/main/native/com/code_intelligence/jazzer/driver/android_tooling.cpp61
8 files changed, 209 insertions, 26 deletions
diff --git a/src/main/java/com/code_intelligence/jazzer/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/BUILD.bazel
index 660e4b14..aed6769d 100644
--- a/src/main/java/com/code_intelligence/jazzer/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/BUILD.bazel
@@ -116,6 +116,7 @@ java_library(
"//src/main/java/com/code_intelligence/jazzer/utils:unsafe_utils",
],
deps = [
+ "//src/main/java/com/code_intelligence/jazzer/android:android_runtime",
"//src/main/java/com/code_intelligence/jazzer/driver",
"//src/main/java/com/code_intelligence/jazzer/runtime:constants",
"//src/main/java/com/code_intelligence/jazzer/utils:log",
diff --git a/src/main/java/com/code_intelligence/jazzer/Jazzer.java b/src/main/java/com/code_intelligence/jazzer/Jazzer.java
index e50693c7..3eb316dd 100644
--- a/src/main/java/com/code_intelligence/jazzer/Jazzer.java
+++ b/src/main/java/com/code_intelligence/jazzer/Jazzer.java
@@ -24,6 +24,7 @@ import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
+import com.code_intelligence.jazzer.android.AndroidRuntime;
import com.code_intelligence.jazzer.driver.Driver;
import com.code_intelligence.jazzer.utils.Log;
import com.code_intelligence.jazzer.utils.ZipUtils;
@@ -94,6 +95,10 @@ public class Jazzer {
}
// No native fuzzing has been requested, fuzz in the current process.
if (!fuzzNative) {
+ if (IS_ANDROID) {
+ final String initOptions = getAndroidRuntimeOptions();
+ AndroidRuntime.initialize(initOptions);
+ }
// We only create a wrapper script if libFuzzer runs in a mode that creates subprocesses.
// In LibFuzzer's fork mode, the subprocesses created continuously by the main libFuzzer
// process do not create further subprocesses. Creating a wrapper script for each subprocess
@@ -213,7 +218,7 @@ public class Jazzer {
char shellQuote = isPosixOrAndroid() ? '\'' : '"';
String launcherTemplate;
if (IS_ANDROID) {
- launcherTemplate = "#!/system/bin/env sh\n%s $@\n";
+ launcherTemplate = "#!/system/bin/env sh\n%s LD_LIBRARY_PATH=%s \n%s $@\n";
} else if (isPosix()) {
launcherTemplate = "#!/usr/bin/env sh\n%s $@\n";
} else {
@@ -229,22 +234,27 @@ public class Jazzer {
.stream()
.map(e -> e.getKey() + "='" + e.getValue() + "'")
.collect(joining(" "));
- String command = Stream
- .concat(Stream.of(javaBinary().toString()), javaBinaryArgs())
- // Escape individual arguments for the shell.
- .map(str -> shellQuote + str + shellQuote)
- .collect(joining(" "));
+ String command =
+ Stream
+ .concat(Stream.of(IS_ANDROID ? "exec" : javaBinary().toString()), javaBinaryArgs())
+ // Escape individual arguments for the shell.
+ .map(str -> shellQuote + str + shellQuote)
+ .collect(joining(" "));
String invocation = env.isEmpty() ? command : env + " " + command;
- String launcherContent = String.format(launcherTemplate, invocation);
// argv0 is printed by libFuzzer during reproduction, so have the launcher basename contain
// "jazzer".
Path launcher;
+ String launcherContent;
if (IS_ANDROID) {
+ String exportCommand = AndroidRuntime.getClassPathsCommand();
+ String ldLibraryPath = AndroidRuntime.getLdLibraryPath();
+ launcherContent = String.format(launcherTemplate, exportCommand, ldLibraryPath, invocation);
launcher = Files.createTempFile(
Paths.get("/data/local/tmp/"), "jazzer-", launcherExtension, launcherScriptAttributes);
} else {
+ launcherContent = String.format(launcherTemplate, invocation);
launcher = Files.createTempFile("jazzer-", launcherExtension, launcherScriptAttributes);
}
@@ -255,9 +265,7 @@ public class Jazzer {
private static Path javaBinary() {
String javaBinaryName;
- if (IS_ANDROID) {
- javaBinaryName = "dalvikvm";
- } else if (isPosix()) {
+ if (isPosix()) {
javaBinaryName = "java";
} else {
javaBinaryName = "java.exe";
@@ -294,10 +302,10 @@ public class Jazzer {
}
// ManagementFactory wont work with Android
- Stream<String> stream =
- Stream.of("-cp", System.getProperty("java.class.path"), "-Xplugin:libopenjdkjvmti.so",
- "-agentpath:" + agentPath.toString() + "=" + nativeAgentOptions, "-Xcompiler-option",
- "--debuggable", "-Djdk.attach.allowAttachSelf=true", Jazzer.class.getName());
+ Stream<String> stream = Stream.of("app_process", "-Djdk.attach.allowAttachSelf=true",
+ "-Xplugin:libopenjdkjvmti.so",
+ "-agentpath:" + agentPath.toString() + "=" + nativeAgentOptions, "-Xcompiler-option",
+ "--debuggable", "/system/bin", Jazzer.class.getName());
return stream;
}
@@ -474,6 +482,16 @@ public class Jazzer {
return !IS_ANDROID && FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
}
+ private static String getAndroidRuntimeOptions() {
+ List<String> validInitOptions = Arrays.asList("use_platform_libs", "use_none", "");
+ String initOptString = System.getProperty("jazzer.android_init_options");
+ if (!validInitOptions.contains(initOptString)) {
+ Log.error("Invalid android_init_options set for Android Runtime.");
+ exit(1);
+ }
+ return initOptString;
+ }
+
private static boolean isPosixOrAndroid() {
if (isPosix()) {
return true;
diff --git a/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java b/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java
new file mode 100644
index 00000000..3a80c314
--- /dev/null
+++ b/src/main/java/com/code_intelligence/jazzer/android/AndroidRuntime.java
@@ -0,0 +1,67 @@
+// 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.android;
+
+import com.code_intelligence.jazzer.utils.Log;
+import com.github.fmeum.rules_jni.RulesJni;
+
+/**
+ * Loads Android tooling library and registers native functions.
+ */
+public class AndroidRuntime {
+ private static final String DO_NOT_INITIALIZE = "use_none";
+ private static final String FUZZ_DIR = "/data/fuzz/";
+ private static final String PLATFORM_LIB_DIRS = ":/system/lib64/:/apex/com.android.i18n@1/lib64/";
+
+ public static void initialize(String runtimeLibs) {
+ if (runtimeLibs == null) {
+ return;
+ }
+
+ RulesJni.loadLibrary("jazzer_android_tooling", "/com/code_intelligence/jazzer/driver");
+ if (runtimeLibs.equals(DO_NOT_INITIALIZE)) {
+ Log.warn("Android Runtime (ART) is not being initialized for this fuzzer.");
+ } else {
+ registerNatives();
+ }
+ };
+
+ /**
+ * Returns a command to set the classpath for fuzzing.
+ *
+ * @return The classpath command.
+ */
+ public static String getClassPathsCommand() {
+ return "export CLASSPATH=" + System.getProperty("java.class.path");
+ }
+
+ /**
+ * Builds and returns the value to set for LD_LIBRARY_PATH.
+ * This value is consumed when launching jazzer on the device
+ * and specifies which directories to search for dependencies.
+ *
+ * @return The string for LD_LIBRARY_PATH.
+ */
+ public static String getLdLibraryPath() {
+ String initOptString = System.getProperty("jazzer.android_init_options");
+ if (initOptString.equals(DO_NOT_INITIALIZE) || initOptString.equals("")) {
+ return FUZZ_DIR;
+ }
+
+ return FUZZ_DIR + PLATFORM_LIB_DIRS;
+ }
+
+ private static native int registerNatives();
+}
diff --git a/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel
index 98b7d11f..1204c4ee 100644
--- a/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/android/BUILD.bazel
@@ -78,3 +78,18 @@ android_binary(
":jazzer_standalone_library",
],
)
+
+java_jni_library(
+ name = "android_runtime",
+ srcs = ["AndroidRuntime.java"],
+ native_libs = ["//src/main/native/com/code_intelligence/jazzer/driver:jazzer_android_tooling"],
+ target_compatible_with = SKIP_ON_WINDOWS,
+ visibility = [
+ "//src/main/java/com/code_intelligence/jazzer:__pkg__",
+ "//src/main/java/com/code_intelligence/jazzer/driver:__subpackages__",
+ "//src/main/native/com/code_intelligence/jazzer/driver:__subpackages__",
+ ],
+ deps = [
+ "//src/main/java/com/code_intelligence/jazzer/utils:log",
+ ],
+)
diff --git a/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel b/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel
index 48e4af44..37202c6d 100644
--- a/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel
+++ b/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel
@@ -21,6 +21,7 @@ java_library(
":offline_instrumentor",
":opt",
"//src/main/java/com/code_intelligence/jazzer/agent:agent_installer",
+ "//src/main/java/com/code_intelligence/jazzer/android:android_runtime",
"//src/main/java/com/code_intelligence/jazzer/driver/junit:junit_runner",
"//src/main/java/com/code_intelligence/jazzer/runtime:constants",
"//src/main/java/com/code_intelligence/jazzer/utils:log",
diff --git a/src/main/java/com/code_intelligence/jazzer/driver/Opt.java b/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
index 723540a8..f1a45c3b 100644
--- a/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
+++ b/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
@@ -69,13 +69,14 @@ public final class Opt {
"asan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=address'");
boolSetting(
"ubsan", false, "Allow fuzzing of native libraries compiled with '-fsanitize=undefined'");
- boolSetting("hwasan", false, "Allow fuzzing of native libraries compiled with hwasan");
boolSetting("native", false,
"Allow fuzzing of native libraries compiled with '-fsanitize=fuzzer' (implied by --asan and --ubsan)");
+ // Options currently used by Android only
+ stringSetting("android_init_options", null,
+ "Which libraries to use when initializing ART (native launcher only)");
+ boolSetting("hwasan", false, "Allow fuzzing of native libraries compiled with hwasan");
}
- public static final List<String> cp =
- stringListSetting("cp", "The class path to use for fuzzing (native launcher only)");
public static final String autofuzz = stringSetting("autofuzz", "",
"Fully qualified reference (optionally with a Javadoc-style signature) to a "
+ "method on the class path to be fuzzed with automatically generated arguments "
@@ -103,10 +104,6 @@ public final class Opt {
public static final boolean hooks = boolSetting(
"hooks", true, "Apply fuzzing instrumentation (use 'trace' for finer-grained control)");
public static final String idSyncFile = stringSetting("id_sync_file", null, null);
- public static final List<String> additionalClassesExcludes =
- stringListSetting("additional_classes_excludes",
- "Glob patterns matching names of classes from Java that are not in your jar file, "
- + "but may be included in your program");
public static final Set<Long> ignore =
unmodifiableSet(stringListSetting("ignore", ',',
"Hex strings representing deduplication tokens of findings that should be ignored")
@@ -141,7 +138,6 @@ public final class Opt {
public static final Supplier<List<String>> instrumentationExcludes =
lazyStringListSetting("instrumentation_excludes",
"Glob patterns matching names of classes that should not be instrumented for fuzzing");
-
// The values of this setting depends on autofuzz.
public static final List<String> targetArgs = autofuzz.isEmpty()
? stringListSetting(
@@ -166,16 +162,29 @@ public final class Opt {
public static final boolean conditionalHooks =
boolSetting("internal.conditional_hooks", false, null);
- // Some scenarios require instrumenting the jar before fuzzing begins
- public static final List<String> instrumentOnly = stringListSetting("instrument_only", ',',
- "Comma separated list of jar files to instrument. No fuzzing is performed.");
-
static final boolean mergeInner = boolSetting("internal.merge_inner", false, null);
private static final boolean help =
boolSetting("help", false, "Show this list of all available arguments");
private static final boolean version = boolSetting("version", false, "Print version information");
+ // Methods below currently used by Android only
+ public static final List<String> cp =
+ stringListSetting("cp", "The class path to use for fuzzing (native launcher only)");
+
+ public static final List<String> additionalClassesExcludes =
+ stringListSetting("additional_classes_excludes",
+ "Glob patterns matching names of classes from Java that are not in your jar file, "
+ + "but may be included in your program");
+
+ // Default to false. Sets if fuzzing is taking place on Android device (virtual or physical)
+ public static final boolean isAndroid =
+ boolSetting("android", false, "Jazzer is running on Android");
+
+ // Some scenarios require instrumenting the jar before fuzzing begins
+ public static final List<String> instrumentOnly = stringListSetting("instrument_only", ',',
+ "Comma separated list of jar files to instrument. No fuzzing is performed.");
+
static {
OptParser.failOnUnknownArgument();
diff --git a/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel b/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel
index 59c62aed..27d8a1c5 100644
--- a/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel
+++ b/src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel
@@ -33,6 +33,17 @@ cc_library(
],
)
+cc_jni_library(
+ name = "jazzer_android_tooling",
+ srcs = ["android_tooling.cpp"],
+ platforms = MULTI_PLATFORM,
+ target_compatible_with = SKIP_ON_WINDOWS,
+ visibility = ["//src/main/java/com/code_intelligence/jazzer/android:__pkg__"],
+ deps = [
+ "//src/main/java/com/code_intelligence/jazzer/android:android_runtime.hdrs",
+ ],
+)
+
cc_library(
name = "coverage_tracker",
srcs = ["coverage_tracker.cpp"],
diff --git a/src/main/native/com/code_intelligence/jazzer/driver/android_tooling.cpp b/src/main/native/com/code_intelligence/jazzer/driver/android_tooling.cpp
new file mode 100644
index 00000000..73444696
--- /dev/null
+++ b/src/main/native/com/code_intelligence/jazzer/driver/android_tooling.cpp
@@ -0,0 +1,61 @@
+// 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.
+
+#include <dlfcn.h>
+#include <jni.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+#include "com_code_intelligence_jazzer_android_AndroidRuntime.h"
+
+const char *RUNTIME_LIBRARY = "libandroid_runtime.so";
+
+// Register native methods from the Android Runtime (ART) framework.
+[[maybe_unused]] jint
+Java_com_code_1intelligence_jazzer_android_AndroidRuntime_registerNatives(
+ JNIEnv *env, jclass clazz) {
+ void *handle = nullptr;
+ handle = dlopen(RUNTIME_LIBRARY, RTLD_LAZY);
+
+ if (handle == nullptr) {
+ std::cerr
+ << "ERROR: Unable to locate runtime library. Check LD_LIBRARY_PATH."
+ << std::endl;
+ exit(1);
+ }
+ // reset errors
+ dlerror();
+
+ // Load the symbol from library
+ typedef jint (*Register_Frameworks_t)(JNIEnv *);
+ Register_Frameworks_t Register_Frameworks;
+
+ Register_Frameworks = reinterpret_cast<Register_Frameworks_t>(
+ dlsym(handle, "registerFrameworkNatives"));
+ const char *dlsym_error = dlerror();
+ if (dlsym_error) {
+ std::cerr << "ERROR: Unable to invoke registerFrameworkNatives."
+ << std::endl;
+ exit(1);
+ }
+
+ if (Register_Frameworks == nullptr) {
+ std::cerr << "ERROR: Register_Frameworks is null." << std::endl;
+ exit(1);
+ }
+
+ return Register_Frameworks(env);
+}