Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add foundations for Android support #587

Merged
merged 3 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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__"],
)
36 changes: 36 additions & 0 deletions WORKSPACE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ http_archive(
url = "https://github.com/CodeIntelligenceTesting/rules_jvm/archive/refs/tags/java-8.tar.gz",
)

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",
)

http_file(
name = "genhtml",
downloaded_file_path = "genhtml",
Expand Down Expand Up @@ -171,3 +185,25 @@ rules_pkg_dependencies()
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")

protobuf_deps()

http_file(
name = "jacocoagent",
downloaded_file_path = "jacocoagent.jar",
sha256 = "67de51e9ca1db044f3a3d10613518befb02e8eee1015f2ff6d56cfb9d4506546",
urls = ["https://repo1.maven.org/maven2/org/jacoco/org.jacoco.agent/0.8.8/org.jacoco.agent-0.8.8-runtime.jar"],
)

http_file(
name = "jacococli",
downloaded_file_path = "jacococli.jar",
sha256 = "c449591174982bbc003d1290003fcbc7b939215436922d2f0f25239d110d531a",
urls = ["https://repo1.maven.org/maven2/org/jacoco/org.jacoco.cli/0.8.8/org.jacoco.cli-0.8.8-nodeps.jar"],
)

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>
TheCoryBarker marked this conversation as resolved.
Show resolved Hide resolved
#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;

TheCoryBarker marked this conversation as resolved.
Show resolved Hide resolved
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