Skip to content

Commit 2fe4c75

Browse files
committed
feat(driver): assign globally unique PCs in coverage counter registration
Coverage counter registration now assigns the global edge ID as the PC value, avoiding collisions across batches. Extra counters use a disjoint range starting at 0x80000000. RegisterCounterRange accepts a track_batch flag so the symbolizer can later update function-entry flags via SetCoveragePCFlags. getEverCoveredIds filters out extra-counter PCs that would overflow jint.
1 parent 50a0e8f commit 2fe4c75

2 files changed

Lines changed: 65 additions & 14 deletions

File tree

src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.cpp

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ void AssertNoException(JNIEnv &env) {
3737
_Exit(1);
3838
}
3939
}
40+
41+
// Tracks a registered PC table batch so we can update PCFlags later.
42+
struct PCTableBatch {
43+
uintptr_t pc_base;
44+
std::size_t count;
45+
jazzer::PCTableEntry *entries;
46+
};
47+
48+
std::vector<PCTableBatch> gCoveragePCBatches;
4049
} // namespace
4150

4251
namespace jazzer {
@@ -45,24 +54,27 @@ uint8_t *CountersTracker::coverage_counters_ = nullptr;
4554
uint8_t *CountersTracker::extra_counters_ = nullptr;
4655
std::mutex CountersTracker::mutex_;
4756

48-
void CountersTracker::RegisterCounterRange(uint8_t *start, uint8_t *end) {
57+
void CountersTracker::RegisterCounterRange(uint8_t *start, uint8_t *end,
58+
uintptr_t pc_base,
59+
bool track_batch) {
4960
if (start >= end) {
5061
return;
5162
}
5263

5364
std::size_t num_counters = end - start;
5465

55-
// libFuzzer requires an array containing the instruction addresses associated
56-
// with the coverage counters. Since these may be synthetic counters (not
57-
// associated with real code), we create PC entries with the flag set to 0 to
58-
// indicate they are not real PCs. The PC value is set to the counter index
59-
// for identification purposes.
66+
// libFuzzer pairs each 8-bit counter with a PC table entry. We assign
67+
// globally unique synthetic PCs so the symbolizer can resolve them back
68+
// to Java source locations.
6069
PCTableEntry *pc_entries = new PCTableEntry[num_counters];
6170
for (std::size_t i = 0; i < num_counters; ++i) {
62-
pc_entries[i] = {i, 0};
71+
pc_entries[i] = {pc_base + i, 0};
6372
}
6473

6574
std::lock_guard<std::mutex> lock(mutex_);
75+
if (track_batch) {
76+
gCoveragePCBatches.push_back({pc_base, num_counters, pc_entries});
77+
}
6678
__sanitizer_cov_8bit_counters_init(start, end);
6779
__sanitizer_cov_pcs_init(
6880
reinterpret_cast<uintptr_t *>(pc_entries),
@@ -94,8 +106,12 @@ void CountersTracker::RegisterNewCounters(JNIEnv &env, jint old_num_counters,
94106
<< std::endl;
95107
_Exit(1);
96108
}
109+
// Coverage counters use the global edge ID as the PC value and
110+
// track the batch so SetCoveragePCFlags can update entries later.
97111
RegisterCounterRange(coverage_counters_ + old_num_counters,
98-
coverage_counters_ + new_num_counters);
112+
coverage_counters_ + new_num_counters,
113+
static_cast<uintptr_t>(old_num_counters),
114+
/*track_batch=*/true);
99115
}
100116

101117
void CountersTracker::InitializeExtra(JNIEnv &env, jlong counters) {
@@ -123,8 +139,21 @@ void CountersTracker::RegisterExtraCounters(JNIEnv &env, jint start_offset,
123139
<< std::endl;
124140
_Exit(1);
125141
}
142+
// Extra counters use a disjoint PC range so the symbolizer can tell them
143+
// apart from coverage counters.
126144
RegisterCounterRange(extra_counters_ + start_offset,
127-
extra_counters_ + end_offset);
145+
extra_counters_ + end_offset,
146+
kExtraCountersPCBase + start_offset);
147+
}
148+
149+
void CountersTracker::SetCoveragePCFlags(std::size_t edge_id, uintptr_t flags) {
150+
std::lock_guard<std::mutex> lock(mutex_);
151+
for (auto &batch : gCoveragePCBatches) {
152+
if (edge_id >= batch.pc_base && edge_id < batch.pc_base + batch.count) {
153+
batch.entries[edge_id - batch.pc_base].PCFlags |= flags;
154+
return;
155+
}
156+
}
128157
}
129158

130159
} // namespace jazzer
@@ -149,13 +178,22 @@ Java_com_code_1intelligence_jazzer_runtime_CoverageMap_getEverCoveredIds(
149178
JNIEnv *env, jclass) {
150179
uintptr_t *covered_pcs;
151180
jint num_covered_pcs = __sanitizer_cov_get_observed_pcs(&covered_pcs);
152-
std::vector<jint> covered_edge_ids(covered_pcs,
153-
covered_pcs + num_covered_pcs);
181+
182+
// Filter out extra-counter PCs (>= kExtraCountersPCBase) which would
183+
// overflow jint and corrupt Java-side coverage analysis.
184+
std::vector<jint> covered_edge_ids;
185+
covered_edge_ids.reserve(num_covered_pcs);
186+
for (jint i = 0; i < num_covered_pcs; ++i) {
187+
if (covered_pcs[i] < jazzer::kExtraCountersPCBase) {
188+
covered_edge_ids.push_back(static_cast<jint>(covered_pcs[i]));
189+
}
190+
}
154191
delete[] covered_pcs;
155192

156-
jintArray covered_edge_ids_jni = env->NewIntArray(num_covered_pcs);
193+
jint count = static_cast<jint>(covered_edge_ids.size());
194+
jintArray covered_edge_ids_jni = env->NewIntArray(count);
157195
AssertNoException(*env);
158-
env->SetIntArrayRegion(covered_edge_ids_jni, 0, num_covered_pcs,
196+
env->SetIntArrayRegion(covered_edge_ids_jni, 0, count,
159197
covered_edge_ids.data());
160198
AssertNoException(*env);
161199
return covered_edge_ids_jni;

src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ struct __attribute__((packed)) PCTableEntry {
2626
[[maybe_unused]] uintptr_t PC, PCFlags;
2727
};
2828

29+
// Extra counters live in a disjoint PC range so the symbolizer can
30+
// distinguish them from coverage counters (which carry source locations).
31+
constexpr uintptr_t kExtraCountersPCBase = 0x80000000UL;
32+
2933
// CountersTracker manages coverage counter arrays and registers them with
3034
// libFuzzer. It handles two separate counter regions:
3135
// - Coverage counters: for bytecode edge coverage (used by CoverageMap)
@@ -38,7 +42,12 @@ class CountersTracker {
3842
static std::mutex mutex_;
3943

4044
// Shared helper to register a counter range with libFuzzer.
41-
static void RegisterCounterRange(uint8_t *start, uint8_t *end);
45+
// pc_base is the PC value assigned to the first counter in the range;
46+
// subsequent counters get pc_base+1, pc_base+2, etc.
47+
// If track_batch is true, the PC table entries are recorded so that
48+
// SetCoveragePCFlags can update them later (used for coverage counters).
49+
static void RegisterCounterRange(uint8_t *start, uint8_t *end,
50+
uintptr_t pc_base, bool track_batch = false);
4251

4352
public:
4453
// For CoverageMap: initialize coverage counters base address.
@@ -54,6 +63,10 @@ class CountersTracker {
5463
// For ExtraCountersTracker.java: register extra counters with libFuzzer.
5564
static void RegisterExtraCounters(JNIEnv &env, jint start_offset,
5665
jint end_offset);
66+
67+
// Set additional flags on the PC table entry for a coverage edge.
68+
// Used by the symbolizer to mark function-entry edges (PCFlags |= 1).
69+
static void SetCoveragePCFlags(std::size_t edge_id, uintptr_t flags);
5770
};
5871

5972
} // namespace jazzer

0 commit comments

Comments
 (0)