diff options
author | Fabian Meumertzheim <meumertzheim@code-intelligence.com> | 2021-09-13 21:43:37 +0200 |
---|---|---|
committer | Fabian Meumertzheim <fabian@meumertzhe.im> | 2021-09-14 15:43:52 +0200 |
commit | c91c0cc5852765de8a7159204485b2af31a31484 (patch) | |
tree | 6e47e882f122a9de80b00929f4158fd0ad466e3d /agent | |
parent | 6d347018769150822d188f526a798c8c023e7c41 (diff) | |
download | jazzer-api-c91c0cc5852765de8a7159204485b2af31a31484.tar.gz |
Report coverage for all classes on the classpath
Using the (very fast) classpath traverser ClassPath, we can generate
coverage data for *all* classes on the classpath rather than just those
that were loaded during the fuzzing run.
Diffstat (limited to 'agent')
4 files changed, 49 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 + } } |