Skip to content

Commit

Permalink
Add foundations for Android support
Browse files Browse the repository at this point in the history
This includes the necessary changes to the native launcher, driver and
runtime, but does not yet include a way to instrument the application
under test - Android doesn't support Java agents.

Since building the Android targets requires a local installation of the
Android SDK and NDK, these targets aren't built by default and only
requested in the Linux CI pipeline.
  • Loading branch information
TheCoryBarker authored and fmeum committed Feb 15, 2023
1 parent 9fef777 commit 906c224
Show file tree
Hide file tree
Showing 27 changed files with 487 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ build:ci --features=layering_check
build --java_language_version=8
build --tool_java_language_version=8

# Android
build:android_arm --incompatible_enable_android_toolchain_resolution
build:android_arm --android_platforms=//:android_arm64
build:android_arm --copt=-D_ANDROID

# Windows
# Only compiles with clang on Windows.
build:windows --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/run-all-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
- os: ubuntu-20.04
arch: "linux"
cache: "/home/runner/.cache/bazel-disk"
bazel_args: "//launcher/android:jazzer_android"
- os: ubuntu-20.04
jdk: 19
# Workaround for https://github.com/bazelbuild/bazel/issues/14502
Expand Down
9 changes: 9 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ alias(
name = "jazzer",
actual = "//launcher:jazzer",
)

platform(
name = "android_arm64",
constraint_values = [
"@platforms//cpu:arm64",
"@platforms//os:android",
],
visibility = ["//:__subpackages__"],
)
22 changes: 22 additions & 0 deletions WORKSPACE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,25 @@ http_file(
sha256 = "c449591174982bbc003d1290003fcbc7b939215436922d2f0f25239d110d531a",
urls = ["https://repo1.maven.org/maven2/org/jacoco/org.jacoco.cli/0.8.8/org.jacoco.cli-0.8.8-nodeps.jar"],
)

http_archive(
name = "build_bazel_rules_android",
sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",
strip_prefix = "rules_android-0.1.1",
urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"],
)

http_archive(
name = "rules_android_ndk",
sha256 = "0fcaeed391bfc0ee784ab0365312e6c59fe75db9df3a67e8708c606e2a9cfd90",
strip_prefix = "rules_android_ndk-79923720aef601fad89c94e8802f5d77c1b73c5d",
url = "https://github.com/bazelbuild/rules_android_ndk/archive/79923720aef601fad89c94e8802f5d77c1b73c5d.zip",
)

load("//third_party/android:android_configure.bzl", "android_configure")

android_configure(name = "configure_android_rules")

load("@configure_android_rules//:android_configure.bzl", "android_workspace")

android_workspace()
5 changes: 5 additions & 0 deletions launcher/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ cc_library(
data = [
"//src/main/java/com/code_intelligence/jazzer:jazzer_standalone_deploy.jar",
],
linkopts = select({
"@platforms//os:android": ["-ldl"],
"//conditions:default": [],
}),
deps = [
"@bazel_tools//tools/cpp/runfiles",
"@com_google_absl//absl/strings",
Expand All @@ -29,6 +33,7 @@ cc_binary(
name = "jazzer_single_arch",
linkstatic = True,
tags = ["manual"],
visibility = ["//launcher/android:__pkg__"],
deps = [":jazzer_main"],
)

Expand Down
5 changes: 5 additions & 0 deletions launcher/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.code_intelligence.jazzer"
android:versionCode="1"
android:versionName="1.0" >
</manifest>
32 changes: 32 additions & 0 deletions launcher/android/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
load("//bazel:compat.bzl", "SKIP_ON_WINDOWS")

android_library(
name = "jazzer_android_lib",
data = [
"//launcher:jazzer_single_arch",
"//src/main/java/com/code_intelligence/jazzer/android:jazzer_standalone_android.apk",
],
tags = ["manual"],
target_compatible_with = SKIP_ON_WINDOWS,
)

android_binary(
name = "jazzer_android",
manifest = ":android_manifest",
min_sdk_version = 26,
tags = ["manual"],
target_compatible_with = SKIP_ON_WINDOWS,
visibility = ["//visibility:public"],
deps = [
":jazzer_android_lib",
],
)

filegroup(
name = "android_manifest",
srcs = ["AndroidManifest.xml"],
tags = ["manual"],
visibility = [
"//visibility:public",
],
)
106 changes: 93 additions & 13 deletions launcher/jvm_tooling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

#include "jvm_tooling.h"

#if defined(_ANDROID)
#include <dlfcn.h>
#endif

#include <cstdlib>
#include <fstream>
#include <iostream>
Expand Down Expand Up @@ -120,20 +124,74 @@ std::vector<std::string> splitEscaped(const std::string &str) {

namespace jazzer {

JVM::JVM(const std::string &executable_path) {
#if defined(_ANDROID)
typedef jint (*JNI_CreateJavaVM_t)(JavaVM **, JNIEnv **, void *);
JNI_CreateJavaVM_t LoadAndroidVMLibs() {
std::cout << "Loading Android libraries" << std::endl;

void *art_so = nullptr;
art_so = dlopen("libnativehelper.so", RTLD_NOW);

if (art_so == nullptr) {
std::cerr << "Could not find ART library" << std::endl;
exit(1);
}

typedef void *(*JniInvocationCreate_t)();
JniInvocationCreate_t JniInvocationCreate =
reinterpret_cast<JniInvocationCreate_t>(
dlsym(art_so, "JniInvocationCreate"));
if (JniInvocationCreate == nullptr) {
std::cout << "JniInvocationCreate is null" << std::endl;
exit(1);
}

void *impl = JniInvocationCreate();
typedef bool (*JniInvocationInit_t)(void *, const char *);
JniInvocationInit_t JniInvocationInit =
reinterpret_cast<JniInvocationInit_t>(dlsym(art_so, "JniInvocationInit"));
if (JniInvocationInit == nullptr) {
std::cout << "JniInvocationInit is null" << std::endl;
exit(1);
}

JniInvocationInit(impl, nullptr);

constexpr char create_jvm_symbol[] = "JNI_CreateJavaVM";
typedef jint (*JNI_CreateJavaVM_t)(JavaVM **, JNIEnv **, void *);
JNI_CreateJavaVM_t JNI_CreateArtVM =
reinterpret_cast<JNI_CreateJavaVM_t>(dlsym(art_so, create_jvm_symbol));
if (JNI_CreateArtVM == nullptr) {
std::cout << "JNI_CreateJavaVM is null" << std::endl;
exit(1);
}

return JNI_CreateArtVM;
}
#endif

std::string GetClassPath(const std::string &executable_path) {
// combine class path from command line flags and JAVA_FUZZER_CLASSPATH env
// variable
std::string class_path = absl::StrFormat("-Djava.class.path=%s", FLAGS_cp);
const auto class_path_from_env = std::getenv("JAVA_FUZZER_CLASSPATH");
if (class_path_from_env) {
class_path += absl::StrCat(ARG_SEPARATOR, class_path_from_env);
}

class_path +=
absl::StrCat(ARG_SEPARATOR, getInstrumentorAgentPath(executable_path));
return class_path;
}

JVM::JVM(const std::string &executable_path) {
std::string class_path = GetClassPath(executable_path);

std::vector<JavaVMOption> options;
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(class_path.c_str())});

#if !defined(_ANDROID)
// Set the maximum heap size to a value that is slightly smaller than
// libFuzzer's default rss_limit_mb. This prevents erroneous oom reports.
options.push_back(JavaVMOption{.optionString = (char *)"-Xmx1800m"});
Expand All @@ -148,44 +206,66 @@ JVM::JVM(const std::string &executable_path) {
JavaVMOption{.optionString = (char *)"-XX:+IgnoreUnrecognizedVMOptions"});
options.push_back(
JavaVMOption{.optionString = (char *)"-XX:+CriticalJNINatives"});
#endif

// Add additional JVM options set through JAVA_OPTS.
std::vector<std::string> java_opts_args;
const char *java_opts = std::getenv("JAVA_OPTS");
if (java_opts != nullptr) {
// Mimic the behavior of the JVM when it sees JAVA_TOOL_OPTIONS.
std::cerr << "Picked up JAVA_OPTS: " << java_opts << std::endl;

java_opts_args = absl::StrSplit(java_opts, ' ');
for (const std::string &java_opt : java_opts_args) {
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(java_opt.c_str())});
}
}

// add additional jvm options set through command line flags
// Add additional jvm options set through command line flags.
// Keep the vectors in scope as they contain the strings backing the C strings
// added to options.
std::vector<std::string> jvm_args;
if (!FLAGS_jvm_args.empty()) {
jvm_args = splitEscaped(FLAGS_jvm_args);
for (const auto &arg : jvm_args) {
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(arg.c_str())});
}
}
for (const auto &arg : jvm_args) {
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(arg.c_str())});
}

std::vector<std::string> additional_jvm_args;
if (!FLAGS_additional_jvm_args.empty()) {
additional_jvm_args = splitEscaped(FLAGS_additional_jvm_args);
for (const auto &arg : additional_jvm_args) {
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(arg.c_str())});
}
}
for (const auto &arg : additional_jvm_args) {
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(arg.c_str())});
}

JavaVMInitArgs jvm_init_args = {.version = JNI_VERSION_1_8,
#if !defined(_ANDROID)
jint jni_version = JNI_VERSION_1_8;
#else
jint jni_version = JNI_VERSION_1_6;
#endif

JavaVMInitArgs jvm_init_args = {.version = jni_version,
.nOptions = (int)options.size(),
.options = options.data(),
.ignoreUnrecognized = JNI_FALSE};

auto ret = JNI_CreateJavaVM(&jvm_, (void **)&env_, &jvm_init_args);
#if !defined(_ANDROID)
int ret = JNI_CreateJavaVM(&jvm_, (void **)&env_, &jvm_init_args);
#else
JNI_CreateJavaVM_t CreateArtVM = LoadAndroidVMLibs();
if (CreateArtVM == nullptr) {
std::cerr << "JNI_CreateJavaVM for Android not found" << std::endl;
exit(1);
}

std::cout << "Starting Art VM" << std::endl;
int ret = CreateArtVM(&jvm_, (JNIEnv_ **)&env_, &jvm_init_args);
#endif

if (ret != JNI_OK) {
throw std::runtime_error(
absl::StrFormat("JNI_CreateJavaVM returned code %d", ret));
Expand Down
6 changes: 3 additions & 3 deletions repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ def jazzer_dependencies():
maybe(
http_archive,
name = "fmeum_rules_jni",
sha256 = "9bd07dade81131aa7e1ac36830cef5d2a6a076406bec53dff51ea59e0efc77a9",
strip_prefix = "rules_jni-0.6.1",
url = "https://github.com/fmeum/rules_jni/archive/refs/tags/v0.6.1.tar.gz",
sha256 = "530a02c4d86f7bcfabd61e7de830f8c78fcb2ea70943eab8f2bfdad96620f1f5",
strip_prefix = "rules_jni-0.7.0",
url = "https://github.com/fmeum/rules_jni/archive/refs/tags/v0.7.0.tar.gz",
)

maybe(
Expand Down
8 changes: 8 additions & 0 deletions sanitizers/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ java_library(
"//sanitizers/src/main/java/com/code_intelligence/jazzer/sanitizers",
],
)

java_library(
name = "offline_only_sanitizers",
visibility = ["//visibility:public"],
runtime_deps = [
":sanitizers",
],
)
Loading

0 comments on commit 906c224

Please sign in to comment.