• 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 "coverage_tracker.h"
16 
17 #include <jni.h>
18 
19 #include <algorithm>
20 #include <memory>
21 #include <stdexcept>
22 
23 #include "absl/strings/str_format.h"
24 
25 extern "C" void __sanitizer_cov_8bit_counters_init(uint8_t *start,
26                                                    uint8_t *end);
27 extern "C" void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
28                                          const uintptr_t *pcs_end);
29 extern "C" size_t __sanitizer_cov_get_observed_pcs(uintptr_t **pc_entries);
30 
31 constexpr auto kCoverageMapClass =
32     "com/code_intelligence/jazzer/runtime/CoverageMap";
33 constexpr auto kByteBufferClass = "java/nio/ByteBuffer";
34 constexpr auto kCoverageRecorderClass =
35     "com/code_intelligence/jazzer/instrumentor/CoverageRecorder";
36 
37 // The initial size of the Java coverage map (512 counters).
38 constexpr std::size_t kInitialCoverageCountersBufferSize = 1u << 9u;
39 // The maximum size of the Java coverage map (1,048,576 counters).
40 // Since the memory for the coverage map needs to be allocated contiguously,
41 // increasing the maximum size incurs additional memory (but not runtime)
42 // overhead for all fuzz targets.
43 constexpr std::size_t kMaxCoverageCountersBufferSize = 1u << 20u;
44 static_assert(kMaxCoverageCountersBufferSize <=
45               std::numeric_limits<jint>::max());
46 
47 namespace {
AssertNoException(JNIEnv & env)48 void AssertNoException(JNIEnv &env) {
49   if (env.ExceptionCheck()) {
50     env.ExceptionDescribe();
51     throw std::runtime_error(
52         "Java exception occurred in CoverageTracker JNI code");
53   }
54 }
55 }  // namespace
56 
57 namespace jazzer {
58 
59 uint8_t *CoverageTracker::counters_ = nullptr;
60 uint32_t *CoverageTracker::fake_instructions_ = nullptr;
61 PCTableEntry *CoverageTracker::pc_entries_ = nullptr;
62 
Setup(JNIEnv & env)63 void CoverageTracker::Setup(JNIEnv &env) {
64   if (counters_ != nullptr) {
65     throw std::runtime_error(
66         "CoverageTracker::Setup must not be called more than once");
67   }
68   JNINativeMethod coverage_tracker_native_methods[]{
69       {(char *)"registerNewCoverageCounters", (char *)"()V",
70        (void *)&RegisterNewCoverageCounters},
71   };
72   jclass coverage_map = env.FindClass(kCoverageMapClass);
73   env.RegisterNatives(coverage_map, coverage_tracker_native_methods, 1);
74 
75   // libFuzzer requires an array containing the instruction addresses associated
76   // with the coverage counters registered above. Given that we are
77   // instrumenting Java code, we need to synthesize addresses that are known not
78   // to conflict with any valid instruction address in native code. Just like
79   // atheris we ensure there are no collisions by using the addresses of an
80   // allocated buffer. Note: We intentionally never deallocate the allocations
81   // made here as they have static lifetime and we can't guarantee they wouldn't
82   // be freed before libFuzzer stops using them.
83   constexpr std::size_t counters_size = kMaxCoverageCountersBufferSize;
84   counters_ = new uint8_t[counters_size];
85   Clear();
86 
87   // Never deallocated, see above.
88   fake_instructions_ = new uint32_t[counters_size];
89   std::fill(fake_instructions_, fake_instructions_ + counters_size, 0);
90 
91   // Never deallocated, see above.
92   pc_entries_ = new PCTableEntry[counters_size];
93   for (std::size_t i = 0; i < counters_size; ++i) {
94     pc_entries_[i].PC = reinterpret_cast<uintptr_t>(fake_instructions_ + i);
95     // TODO: Label Java PCs corresponding to functions as such.
96     pc_entries_[i].PCFlags = 0;
97   }
98 
99   // Register the first batch of coverage counters.
100   RegisterNewCoverageCounters(env, nullptr);
101 }
102 
RegisterNewCoverageCounters(JNIEnv & env,jclass cls)103 void JNICALL CoverageTracker::RegisterNewCoverageCounters(JNIEnv &env,
104                                                           jclass cls) {
105   jclass coverage_map = env.FindClass(kCoverageMapClass);
106   AssertNoException(env);
107   jfieldID counters_buffer_id = env.GetStaticFieldID(
108       coverage_map, "mem", absl::StrFormat("L%s;", kByteBufferClass).c_str());
109   AssertNoException(env);
110   jobject counters_buffer =
111       env.GetStaticObjectField(coverage_map, counters_buffer_id);
112   AssertNoException(env);
113 
114   jclass byte_buffer = env.FindClass(kByteBufferClass);
115   AssertNoException(env);
116   jmethodID byte_buffer_capacity_id =
117       env.GetMethodID(byte_buffer, "capacity", "()I");
118   AssertNoException(env);
119   jint old_counters_buffer_size =
120       env.CallIntMethod(counters_buffer, byte_buffer_capacity_id);
121   AssertNoException(env);
122 
123   jint new_counters_buffer_size;
124   if (old_counters_buffer_size == 0) {
125     new_counters_buffer_size = kInitialCoverageCountersBufferSize;
126   } else {
127     new_counters_buffer_size = 2 * old_counters_buffer_size;
128     if (new_counters_buffer_size > kMaxCoverageCountersBufferSize) {
129       throw std::runtime_error(
130           "Maximal size of the coverage counters buffer exceeded");
131     }
132   }
133 
134   jobject new_counters_buffer = env.NewDirectByteBuffer(
135       static_cast<void *>(counters_), new_counters_buffer_size);
136   AssertNoException(env);
137   env.SetStaticObjectField(coverage_map, counters_buffer_id,
138                            new_counters_buffer);
139   AssertNoException(env);
140 
141   // Register only the new second half of the counters buffer with libFuzzer.
142   __sanitizer_cov_8bit_counters_init(counters_ + old_counters_buffer_size,
143                                      counters_ + new_counters_buffer_size);
144   __sanitizer_cov_pcs_init(
145       (uintptr_t *)(pc_entries_ + old_counters_buffer_size),
146       (uintptr_t *)(pc_entries_ + new_counters_buffer_size));
147 }
148 
Clear()149 void CoverageTracker::Clear() {
150   std::fill(counters_, counters_ + kMaxCoverageCountersBufferSize, 0);
151 }
152 
GetCoverageCounters()153 uint8_t *CoverageTracker::GetCoverageCounters() { return counters_; }
154 
RecordInitialCoverage(JNIEnv & env)155 void CoverageTracker::RecordInitialCoverage(JNIEnv &env) {
156   jclass coverage_recorder = env.FindClass(kCoverageRecorderClass);
157   AssertNoException(env);
158   jmethodID coverage_recorder_update_covered_ids_with_coverage_map =
159       env.GetStaticMethodID(coverage_recorder,
160                             "updateCoveredIdsWithCoverageMap", "()V");
161   AssertNoException(env);
162   env.CallStaticVoidMethod(
163       coverage_recorder,
164       coverage_recorder_update_covered_ids_with_coverage_map);
165   AssertNoException(env);
166 }
167 
ReplayInitialCoverage(JNIEnv & env)168 void CoverageTracker::ReplayInitialCoverage(JNIEnv &env) {
169   jclass coverage_recorder = env.FindClass(kCoverageRecorderClass);
170   AssertNoException(env);
171   jmethodID coverage_recorder_update_covered_ids_with_coverage_map =
172       env.GetStaticMethodID(coverage_recorder, "replayCoveredIds", "()V");
173   AssertNoException(env);
174   env.CallStaticVoidMethod(
175       coverage_recorder,
176       coverage_recorder_update_covered_ids_with_coverage_map);
177   AssertNoException(env);
178 }
179 
ComputeCoverage(JNIEnv & env)180 std::string CoverageTracker::ComputeCoverage(JNIEnv &env) {
181   uintptr_t *covered_pcs;
182   size_t num_covered_pcs = __sanitizer_cov_get_observed_pcs(&covered_pcs);
183   std::vector<jint> covered_edge_ids{};
184   covered_edge_ids.reserve(num_covered_pcs);
185   const uintptr_t first_pc = pc_entries_[0].PC;
186   std::for_each(covered_pcs, covered_pcs + num_covered_pcs,
187                 [&covered_edge_ids, first_pc](const uintptr_t pc) {
188                   jint edge_id =
189                       (pc - first_pc) / sizeof(fake_instructions_[0]);
190                   covered_edge_ids.push_back(edge_id);
191                 });
192   delete[] covered_pcs;
193 
194   jclass coverage_recorder = env.FindClass(kCoverageRecorderClass);
195   AssertNoException(env);
196   jmethodID coverage_recorder_compute_file_coverage = env.GetStaticMethodID(
197       coverage_recorder, "computeFileCoverage", "([I)Ljava/lang/String;");
198   AssertNoException(env);
199   jintArray covered_edge_ids_jni = env.NewIntArray(num_covered_pcs);
200   AssertNoException(env);
201   env.SetIntArrayRegion(covered_edge_ids_jni, 0, num_covered_pcs,
202                         covered_edge_ids.data());
203   AssertNoException(env);
204   auto file_coverage_jni = (jstring)(env.CallStaticObjectMethod(
205       coverage_recorder, coverage_recorder_compute_file_coverage,
206       covered_edge_ids_jni));
207   AssertNoException(env);
208   auto file_coverage_cstr = env.GetStringUTFChars(file_coverage_jni, nullptr);
209   AssertNoException(env);
210   std::string file_coverage(file_coverage_cstr);
211   env.ReleaseStringUTFChars(file_coverage_jni, file_coverage_cstr);
212   AssertNoException(env);
213   return file_coverage;
214 }
215 }  // namespace jazzer
216