Skip to content

Commit

Permalink
launcher: Use actual executable path instead of argv[0]
Browse files Browse the repository at this point in the history
When Jazzer is executed from `PATH`, `argv[0]` will just be `jazzer`,
which we can't find `jazzer_standalone.jar` relative to.
  • Loading branch information
fmeum committed Mar 23, 2023
1 parent bf66576 commit 07bd293
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 18 deletions.
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pkg_tar(
"@platforms//os:windows": ".\\",
"//conditions:default": "./",
}),
visibility = ["//tests:__pkg__"],
)

alias(
Expand Down
2 changes: 1 addition & 1 deletion launcher/fuzzed_data_provider_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class FuzzedDataProviderTest : public ::testing::Test {
FLAGS_cp = Runfiles::CreateForTest()->Rlocation(
"jazzer/launcher/testdata/fuzz_target_mocks_deploy.jar");

jvm_ = std::make_unique<JVM>("test_executable");
jvm_ = std::make_unique<JVM>();
}

static void TearDownTestCase() { jvm_.reset(nullptr); }
Expand Down
2 changes: 1 addition & 1 deletion launcher/jazzer_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,6 @@ int main(int argc, char **argv) {
}
}

StartLibFuzzer(std::unique_ptr<jazzer::JVM>(new jazzer::JVM(argv[0])),
StartLibFuzzer(std::unique_ptr<jazzer::JVM>(new jazzer::JVM()),
std::vector<std::string>(argv + 1, argv + argc));
}
56 changes: 42 additions & 14 deletions launcher/jvm_tooling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

#if defined(_ANDROID)
#include <dlfcn.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else // Assume Linux
#include <unistd.h>
#endif

#include <cstdlib>
Expand Down Expand Up @@ -50,6 +57,24 @@ constexpr auto kJazzerBazelRunfilesPath =
"jazzer_standalone_deploy.jar";
constexpr auto kJazzerFileName = "jazzer_standalone.jar";

// Returns the absolute path to the current executable. Compared to argv[0],
// this path can always be used to locate the Jazzer JAR next to it, even when
// Jazzer is executed from PATH.
std::string getExecutablePath() {
char buf[655536];
#if defined(__APPLE__)
uint32_t buf_size = sizeof(buf);
if (_NSGetExecutablePath(buf, &buf_size) != 0) {
#elif defined(_WIN32)
if (GetModuleFileNameA(NULL, buf, sizeof(buf)) == 0) {
#else // Assume Linux
if (readlink("/proc/self/exe", buf, sizeof(buf)) == -1) {
#endif
return "";
}
return {buf};
}

std::string dirFromFullPath(const std::string &path) {
const auto pos = path.rfind(kPathSeparator);
if (pos != std::string::npos) {
Expand All @@ -60,17 +85,20 @@ std::string dirFromFullPath(const std::string &path) {

// getInstrumentorAgentPath searches for the fuzzing instrumentation agent and
// returns the location if it is found. Otherwise it calls exit(0).
std::string getInstrumentorAgentPath(const std::string &executable_path) {
std::string getInstrumentorAgentPath() {
// User provided agent location takes precedence.
if (!FLAGS_agent_path.empty()) {
if (std::ifstream(FLAGS_agent_path).good()) return FLAGS_agent_path;
std::cerr << "ERROR: Could not find " << kJazzerFileName << " at \""
<< FLAGS_agent_path << "\"" << std::endl;
exit(1);
}
// First check if we are running inside the Bazel tree and use the agent
// runfile.
{

auto executable_path = getExecutablePath();

if (!executable_path.empty()) {
// First check if we are running inside the Bazel tree and use the agent
// runfile.
using bazel::tools::cpp::runfiles::Runfiles;
std::string error;
std::unique_ptr<Runfiles> runfiles(Runfiles::Create(
Expand All @@ -80,13 +108,14 @@ std::string getInstrumentorAgentPath(const std::string &executable_path) {
if (!bazel_path.empty() && std::ifstream(bazel_path).good())
return bazel_path;
}

// If the agent is not in the bazel path we look next to the jazzer binary.
const auto dir = dirFromFullPath(executable_path);
auto agent_path =
absl::StrFormat("%s%c%s", dir, kPathSeparator, kJazzerFileName);
if (std::ifstream(agent_path).good()) return agent_path;
}

// If the agent is not in the bazel path we look next to the jazzer binary.
const auto dir = dirFromFullPath(executable_path);
auto agent_path =
absl::StrFormat("%s%c%s", dir, kPathSeparator, kJazzerFileName);
if (std::ifstream(agent_path).good()) return agent_path;
std::cerr << "ERROR: Could not find " << kJazzerFileName
<< ". Please provide the pathname via the --agent_path flag."
<< std::endl;
Expand Down Expand Up @@ -170,7 +199,7 @@ JNI_CreateJavaVM_t LoadAndroidVMLibs() {
}
#endif

std::string GetClassPath(const std::string &executable_path) {
std::string GetClassPath() {
// 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);
Expand All @@ -179,13 +208,12 @@ std::string GetClassPath(const std::string &executable_path) {
class_path += absl::StrCat(ARG_SEPARATOR, class_path_from_env);
}

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

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

std::vector<JavaVMOption> options;
options.push_back(
Expand Down
2 changes: 1 addition & 1 deletion launcher/jvm_tooling.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class JVM {
public:
// Creates a JVM instance with default options + options that were provided as
// command line flags.
explicit JVM(const std::string &executable_path);
explicit JVM();

// Destroy the running JVM instance.
~JVM();
Expand Down
2 changes: 1 addition & 1 deletion launcher/jvm_tooling_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class JvmToolingTest : public ::testing::Test {
FLAGS_cp = Runfiles::CreateForTest()->Rlocation(
"jazzer/launcher/testdata/fuzz_target_mocks_deploy.jar");

jvm_ = std::unique_ptr<JVM>(new JVM("test_executable"));
jvm_ = std::unique_ptr<JVM>(new JVM());
}

static void TearDownTestCase() { jvm_.reset(nullptr); }
Expand Down
10 changes: 10 additions & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,13 @@ java_fuzz_target_test(
"//tests/src/test/proto:simple_java_proto",
],
)

sh_test(
name = "jazzer_from_path_test",
srcs = ["src/test/shell/jazzer_from_path_test.sh"],
args = ["$(rlocationpath //:jazzer_release)"],
data = [
"//:jazzer_release",
"@bazel_tools//tools/bash/runfiles",
],
)
43 changes: 43 additions & 0 deletions tests/src/test/shell/jazzer_from_path_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
# Copyright 2022 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.

# Verify that the Jazzer launcher finds the jar when executed from PATH.


# --- begin runfiles.bash initialization v3 ---
# Copy-pasted from the Bazel Bash runfiles library v3.
set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
source "$0.runfiles/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v3 ---

# Unpack the release archive to a temporary directory.
jazzer_release="$(rlocation "$1")"
tmp="$(mktemp -d)"
trap 'rm -r "$tmp"' EXIT
# GNU tar on Windows requires --force-local to support colons in archives names,
# macOS tar does not support it.
tar -xzf "$jazzer_release" -C "$tmp" --force-local || tar -xzf "$jazzer_release" -C "$tmp"

# Add the Jazzer launcher to PATH first so that it is picked over host Jazzer
# installations.
PATH="$tmp:$PATH"
export PATH

jazzer --version

0 comments on commit 07bd293

Please sign in to comment.