1 // Copyright 2021 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <jni.h>
16
17 #include <algorithm>
18 #include <fstream>
19 #include <iostream>
20 #include <mutex>
21 #include <utility>
22 #include <vector>
23
24 #include "absl/strings/str_split.h"
25 #include "com_code_intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks.h"
26
27 namespace {
28 bool is_using_native_libraries = false;
29 std::once_flag ignore_list_flag;
30 std::vector<std::pair<uintptr_t, uintptr_t>> ignore_for_interception_ranges;
31
32 /**
33 * Adds the address ranges of executable segments of the library lib_name to
34 * the ignorelist for C standard library function interception (strcmp, memcmp,
35 * ...).
36 */
ignoreLibraryForInterception(const std::string & lib_name)37 void ignoreLibraryForInterception(const std::string &lib_name) {
38 std::ifstream loaded_libs("/proc/self/maps");
39 if (!loaded_libs) {
40 // This early exit is taken e.g. on macOS, where /proc does not exist.
41 return;
42 }
43 std::string line;
44 while (std::getline(loaded_libs, line)) {
45 if (!absl::StrContains(line, lib_name)) continue;
46 // clang-format off
47 // A typical line looks as follows:
48 // 7f15356c9000-7f1536367000 r-xp 0020d000 fd:01 19275673 /usr/lib/jvm/java-15-openjdk-amd64/lib/server/libjvm.so
49 // clang-format on
50 std::vector<std::string> parts =
51 absl::StrSplit(line, ' ', absl::SkipEmpty());
52 if (parts.size() != 6) {
53 std::cout << "ERROR: Invalid format for /proc/self/maps\n"
54 << line << std::endl;
55 exit(1);
56 }
57 // Skip non-executable address rang"s.
58 if (!absl::StrContains(parts[1], "x")) continue;
59 std::string range_str = parts[0];
60 std::vector<std::string> range = absl::StrSplit(range_str, "-");
61 if (range.size() != 2) {
62 std::cout
63 << "ERROR: Unexpected address range format in /proc/self/maps line: "
64 << range_str << std::endl;
65 exit(1);
66 }
67 std::size_t pos;
68 auto start = std::stoull(range[0], &pos, 16);
69 if (pos != range[0].size()) {
70 std::cout
71 << "ERROR: Unexpected address range format in /proc/self/maps line: "
72 << range_str << std::endl;
73 exit(1);
74 }
75 auto end = std::stoull(range[1], &pos, 16);
76 if (pos != range[0].size()) {
77 std::cout
78 << "ERROR: Unexpected address range format in /proc/self/maps line: "
79 << range_str << std::endl;
80 exit(1);
81 }
82 ignore_for_interception_ranges.emplace_back(start, end);
83 }
84 }
85
86 const std::vector<std::string> kLibrariesToIgnoreForInterception = {
87 // The launcher executable itself can be treated just like a library.
88 "jazzer", "libjazzer_preload.so",
89 "libinstrument.so", "libjava.so",
90 "libjimage.so", "libjli.so",
91 "libjvm.so", "libnet.so",
92 "libverify.so", "libzip.so",
93 };
94 } // namespace
95
__sanitizer_weak_is_relevant_pc(void * caller_pc)96 extern "C" [[maybe_unused]] bool __sanitizer_weak_is_relevant_pc(
97 void *caller_pc) {
98 // If the fuzz target is not using native libraries, calls to strcmp, memcmp,
99 // etc. should never be intercepted. The values reported if they were at best
100 // duplicate the values received from our bytecode instrumentation and at
101 // worst pollute the table of recent compares with string internal to the JDK.
102 if (!is_using_native_libraries) return false;
103 // If the fuzz target is using native libraries, intercept calls only if they
104 // don't originate from those address ranges that are known to belong to the
105 // JDK.
106 return std::none_of(
107 ignore_for_interception_ranges.cbegin(),
108 ignore_for_interception_ranges.cend(),
109 [caller_pc](const std::pair<uintptr_t, uintptr_t> &range) {
110 uintptr_t start;
111 uintptr_t end;
112 std::tie(start, end) = range;
113 auto address = reinterpret_cast<uintptr_t>(caller_pc);
114 return start <= address && address <= end;
115 });
116 }
117
118 [[maybe_unused]] void
Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_handleLibraryLoad(JNIEnv *,jclass)119 Java_com_code_1intelligence_jazzer_runtime_TraceDataFlowNativeCallbacks_handleLibraryLoad(
120 JNIEnv *, jclass) {
121 std::call_once(ignore_list_flag, [] {
122 std::cout << "INFO: detected a native library load, enabling interception "
123 "for libc functions"
124 << std::endl;
125 for (const auto &lib_name : kLibrariesToIgnoreForInterception)
126 ignoreLibraryForInterception(lib_name);
127 // Enable the ignore list after it has been populated since vector is not
128 // thread-safe with respect to concurrent writes and reads.
129 is_using_native_libraries = true;
130 });
131 }
132