diff options
-rw-r--r-- | agent/agent_shade_rules | 2 | ||||
-rw-r--r-- | agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt | 2 | ||||
-rw-r--r-- | agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel | 1 | ||||
-rw-r--r-- | agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt | 45 | ||||
-rw-r--r-- | repositories.bzl | 9 | ||||
-rw-r--r-- | third_party/classgraph.BUILD | 10 |
6 files changed, 68 insertions, 1 deletions
diff --git a/agent/agent_shade_rules b/agent/agent_shade_rules index 83da8756..3a7019b9 100644 --- a/agent/agent_shade_rules +++ b/agent/agent_shade_rules @@ -1 +1,3 @@ rule kotlin.** com.code_intelligence.jazzer.third_party.kotlin.@1 +rule io.** com.code_intelligence.jazzer.third_party.io.@1 +rule nonapi.** com.code_intelligence.jazzer.third_party.nonapi.@1 diff --git a/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt b/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt index d282c84d..8f8cec33 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt +++ b/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt @@ -17,6 +17,7 @@ package com.code_intelligence.jazzer.agent import com.code_intelligence.jazzer.instrumentor.ClassNameGlobber +import com.code_intelligence.jazzer.instrumentor.CoverageRecorder import com.code_intelligence.jazzer.instrumentor.InstrumentationType import com.code_intelligence.jazzer.instrumentor.loadHooks import com.code_intelligence.jazzer.runtime.ManifestUtils @@ -79,6 +80,7 @@ fun premain(agentArgs: String?, instrumentation: Instrumentation) { argumentMap["instrumentation_includes"] ?: emptyList(), (argumentMap["instrumentation_excludes"] ?: emptyList()) + customHookNames ) + CoverageRecorder.classNameGlobber = classNameGlobber val dependencyClassNameGlobber = ClassNameGlobber( argumentMap["custom_hook_includes"] ?: emptyList(), (argumentMap["custom_hook_excludes"] ?: emptyList()) + customHookNames diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel index d3f88b46..038540e5 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel +++ b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel @@ -15,6 +15,7 @@ kt_jvm_library( "//agent/src/main/java/com/code_intelligence/jazzer/generated:JavaNoThrowMethods", "//agent/src/main/java/com/code_intelligence/jazzer/runtime", "//agent/src/main/java/com/code_intelligence/jazzer/utils", + "@com_github_classgraph_classgraph//:classgraph", "@com_github_jetbrains_kotlin//:kotlin-reflect", ], ) diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt index e7037dd0..2928e2a1 100644 --- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt +++ b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt @@ -23,6 +23,7 @@ import com.code_intelligence.jazzer.third_party.jacoco.core.data.ExecutionDataWr import com.code_intelligence.jazzer.third_party.jacoco.core.data.SessionInfo import com.code_intelligence.jazzer.third_party.jacoco.core.data.SessionInfoStore import com.code_intelligence.jazzer.third_party.jacoco.core.internal.data.CRC64 +import io.github.classgraph.ClassGraph import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.time.Instant @@ -32,10 +33,11 @@ private data class InstrumentedClassInfo( val classId: Long, val initialEdgeId: Int, val nextEdgeId: Int, - val bytecode: ByteArray + val bytecode: ByteArray, ) object CoverageRecorder { + var classNameGlobber = ClassNameGlobber(emptyList(), emptyList()) private val instrumentedClassInfo = mutableMapOf<String, InstrumentedClassInfo>() private var startTimestamp: Instant? = null private val additionalCoverage = mutableSetOf<Int>() @@ -150,6 +152,7 @@ object CoverageRecorder { fun analyzeCoverage(coveredIds: Set<Int>): CoverageBuilder? { return try { val coverage = CoverageBuilder() + analyzeAllUncoveredClasses(coverage) val rawExecutionData = dumpJacocoCoverage(coveredIds) ?: return null val executionDataStore = ExecutionDataStore() val sessionInfoStore = SessionInfoStore() @@ -174,4 +177,44 @@ object CoverageRecorder { null } } + + /** + * Traverses the entire classpath and analyzes all uncovered classes that match the include/exclude pattern. + * The returned [CoverageBuilder] will report coverage information for *all* classes on the classpath, not just + * those that were loaded while the fuzzer ran. + */ + private fun analyzeAllUncoveredClasses(coverage: CoverageBuilder): CoverageBuilder { + val coveredClassNames = instrumentedClassInfo + .keys + .asSequence() + .map { it.replace('/', '.') } + .toSet() + val emptyExecutionDataStore = ExecutionDataStore() + ClassGraph() + .enableClassInfo() + .ignoreClassVisibility() + .rejectPackages( + // Always exclude Jazzer-internal packages (including ClassGraph itself) from coverage reports. Classes + // from the Java standard library are never traversed. + "com.code_intelligence.jazzer.*", + "jaz", + ) + .scan().use { result -> + result.allClasses + .asSequence() + .filter { classInfo -> classNameGlobber.includes(classInfo.name) } + .filterNot { classInfo -> classInfo.name in coveredClassNames } + .forEach { classInfo -> + classInfo.resource.use { resource -> + EdgeCoverageInstrumentor(0).analyze( + emptyExecutionDataStore, + coverage, + resource.load(), + classInfo.name.replace('.', '/') + ) + } + } + } + return coverage + } } diff --git a/repositories.bzl b/repositories.bzl index 3a5bb3b7..23158a11 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -90,6 +90,15 @@ def jazzer_dependencies(): maybe( http_archive, + build_file = "@jazzer//third_party:classgraph.BUILD", + name = "com_github_classgraph_classgraph", + sha256 = "2b7c3930f007e4acca8a50a26957b09a55b2fabd23ed00715516a114ae3f1c1e", + strip_prefix = "classgraph-classgraph-4.8.116", + url = "https://github.com/classgraph/classgraph/archive/refs/tags/classgraph-4.8.116.tar.gz", + ) + + maybe( + http_archive, build_file = "@jazzer//third_party:asm.BUILD", name = "jazzer_ow2_asm", sha256 = "7b596cc584b241619911e99c5c96366fccd533b1a50b8720c151c2f74b5915e3", diff --git a/third_party/classgraph.BUILD b/third_party/classgraph.BUILD new file mode 100644 index 00000000..2d26caa6 --- /dev/null +++ b/third_party/classgraph.BUILD @@ -0,0 +1,10 @@ +load("@rules_java//java:defs.bzl", "java_library") + +java_library( + name = "classgraph", + srcs = glob([ + "src/main/java/io/github/classgraph/**/*.java", + "src/main/java/nonapi/io/github/classgraph/**/*.java", + ]), + visibility = ["//visibility:public"], +) |