• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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