diff options
Diffstat (limited to 'src')
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); +} |