aboutsummaryrefslogtreecommitdiff
path: root/agent
diff options
context:
space:
mode:
authorFabian Meumertzheim <meumertzheim@code-intelligence.com>2021-09-13 21:43:37 +0200
committerFabian Meumertzheim <fabian@meumertzhe.im>2021-09-14 15:43:52 +0200
commitc91c0cc5852765de8a7159204485b2af31a31484 (patch)
tree6e47e882f122a9de80b00929f4158fd0ad466e3d /agent
parent6d347018769150822d188f526a798c8c023e7c41 (diff)
downloadjazzer-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')
-rw-r--r--agent/agent_shade_rules2
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt2
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel1
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt45
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
+ }
}