Skip to content

Commit c000f66

Browse files
committed
feat(driver): symbolize Java PCs so libFuzzer prints source locations
Override __sanitizer_symbolize_pc to resolve synthetic coverage PCs back to Java source file, method name, and line number. SourceLocationRegistry receives per-edge metadata from the instrumentor via JNI. The C++ symbolizer uses an async-signal-safe spinlock so it can run from crash/timeout signal handlers. Strings are interned via unordered_map and copied into stack-local buffers while locked to avoid use-after-free. Function-entry PC flags are set outside the spinlock to prevent nested locking.
1 parent 3ab5db7 commit c000f66

6 files changed

Lines changed: 390 additions & 0 deletions

File tree

src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ java_jni_library(
161161
],
162162
)
163163

164+
java_jni_library(
165+
name = "source_location_registry",
166+
srcs = ["SourceLocationRegistry.java"],
167+
visibility = [
168+
"//src/main/java/com/code_intelligence/jazzer/instrumentor:__pkg__",
169+
"//src/main/native/com/code_intelligence/jazzer/driver:__pkg__",
170+
],
171+
)
172+
164173
java_library(
165174
name = "runtime",
166175
srcs = [
@@ -188,6 +197,7 @@ java_library(
188197
":constants",
189198
":coverage_map",
190199
":extra_counters_tracker",
200+
":source_location_registry",
191201
":trace_data_flow_native_callbacks",
192202
"//src/main/java/com/code_intelligence/jazzer/api:hooks",
193203
],
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2026 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.code_intelligence.jazzer.runtime;
18+
19+
import com.github.fmeum.rules_jni.RulesJni;
20+
21+
/**
22+
* Registers per-edge source location metadata with the native symbolizer so that libFuzzer's {@code
23+
* -print_pcs}, {@code -print_funcs}, and {@code -print_coverage} flags show Java source locations
24+
* instead of meaningless hex addresses.
25+
*
26+
* <p>Called from the instrumentor after each class is instrumented. Thread-safe on the native side.
27+
*/
28+
public final class SourceLocationRegistry {
29+
static {
30+
RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver");
31+
}
32+
33+
private SourceLocationRegistry() {}
34+
35+
/**
36+
* Register source locations for a contiguous range of coverage edge IDs.
37+
*
38+
* @param sourceFile Qualified source path (e.g. "com/example/Foo.java")
39+
* @param methodNames Deduplicated method name table for this class
40+
* @param firstEdgeId Global ID of the first edge in this class
41+
* @param edgeData Flat array: [packedLine0, methodIdx0, packedLine1, methodIdx1, ...]. The sign
42+
* bit of each packedLine encodes whether the edge is a function entry point.
43+
*/
44+
public static native void registerLocations(
45+
String sourceFile, String[] methodNames, int firstEdgeId, int[] edgeData);
46+
}

src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ cc_library(
3030
":jazzer_fuzzer_callbacks",
3131
":libfuzzer_callbacks",
3232
":mutator",
33+
":synthetic_symbolizer",
3334
],
3435
)
3536

@@ -56,6 +57,18 @@ cc_library(
5657
alwayslink = True,
5758
)
5859

60+
cc_library(
61+
name = "synthetic_symbolizer",
62+
srcs = ["synthetic_symbolizer.cpp"],
63+
hdrs = ["synthetic_symbolizer.h"],
64+
deps = [
65+
":counters_tracker",
66+
"//src/main/java/com/code_intelligence/jazzer/runtime:source_location_registry.hdrs",
67+
],
68+
# JNI symbols are only referenced dynamically.
69+
alwayslink = True,
70+
)
71+
5972
cc_library(
6073
name = "fuzz_target_runner",
6174
srcs = ["fuzz_target_runner.cpp"],
@@ -156,6 +169,7 @@ cc_library(
156169
cc_library(
157170
name = "sanitizer_symbols",
158171
srcs = ["sanitizer_symbols.cpp"],
172+
deps = [":synthetic_symbolizer"],
159173
# Symbols are referenced dynamically by libFuzzer.
160174
alwayslink = True,
161175
)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include "synthetic_symbolizer.h"
16+
1517
// Suppress libFuzzer warnings about missing sanitizer methods in non-sanitizer
1618
// builds.
1719
extern "C" [[maybe_unused]] int __sanitizer_acquire_crash_state() { return 1; }
@@ -24,3 +26,13 @@ void DumpJvmStackTraces();
2426
extern "C" [[maybe_unused]] void __sanitizer_print_stack_trace() {
2527
jazzer::DumpJvmStackTraces();
2628
}
29+
30+
// Override libFuzzer's weak __sanitizer_symbolize_pc so that -print_pcs=1,
31+
// -print_funcs=1, and -print_coverage=1 show Java source locations.
32+
extern "C" [[maybe_unused]] void __sanitizer_symbolize_pc(void *pc,
33+
const char *fmt,
34+
char *out_buf,
35+
size_t out_buf_size) {
36+
jazzer::SymbolizePC(reinterpret_cast<uintptr_t>(pc), fmt, out_buf,
37+
out_buf_size);
38+
}

0 commit comments

Comments
 (0)