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