aboutsummaryrefslogtreecommitdiff
path: root/agent/src/main/java/com/code_intelligence/jazzer
diff options
context:
space:
mode:
Diffstat (limited to 'agent/src/main/java/com/code_intelligence/jazzer')
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt171
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel16
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/CoverageIdStrategy.kt200
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/RuntimeInstrumentor.kt181
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java32
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java26
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/BUILD.bazel29
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java211
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java22
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java22
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Consumer3.java20
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Consumer4.java20
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Consumer5.java20
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Function1.java22
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Function2.java22
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Function3.java20
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Function4.java20
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Function5.java20
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java444
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java39
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java39
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java39
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java39
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/HookType.java25
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java643
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java207
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java31
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java117
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java31
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel17
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java317
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java716
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java229
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel35
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/ClassInstrumentor.kt53
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt246
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DescriptorUtils.kt90
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DeterministicRandom.kt35
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/EdgeCoverageInstrumentor.kt187
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt133
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookInstrumentor.kt48
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookMethodVisitor.kt399
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hooks.kt114
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Instrumentor.kt45
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/StaticMethodStrategy.java48
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentor.kt258
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel17
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java156
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel88
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java129
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java250
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java82
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java41
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java35
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProvider.java213
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/SignalHandler.java31
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java347
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java102
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java47
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java35
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/runtime/UnsafeProvider.java50
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/utils/BUILD.bazel15
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/utils/ClassNameGlobber.kt115
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/utils/ExceptionUtils.kt172
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/utils/ManifestUtils.kt54
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/utils/Utils.kt107
66 files changed, 0 insertions, 7784 deletions
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
deleted file mode 100644
index f9b026f1..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-@file:JvmName("Agent")
-
-package com.code_intelligence.jazzer.agent
-
-import com.code_intelligence.jazzer.driver.Opt
-import com.code_intelligence.jazzer.instrumentor.CoverageRecorder
-import com.code_intelligence.jazzer.instrumentor.Hooks
-import com.code_intelligence.jazzer.instrumentor.InstrumentationType
-import com.code_intelligence.jazzer.runtime.NativeLibHooks
-import com.code_intelligence.jazzer.runtime.TraceCmpHooks
-import com.code_intelligence.jazzer.runtime.TraceDivHooks
-import com.code_intelligence.jazzer.runtime.TraceIndirHooks
-import com.code_intelligence.jazzer.utils.ClassNameGlobber
-import com.code_intelligence.jazzer.utils.ManifestUtils
-import java.io.File
-import java.lang.instrument.Instrumentation
-import java.net.URI
-import java.nio.file.Paths
-import java.util.jar.JarFile
-import kotlin.io.path.ExperimentalPathApi
-import kotlin.io.path.exists
-import kotlin.io.path.isDirectory
-
-private object AgentJarFinder {
- val agentJarFile = jarUriForClass(AgentJarFinder::class.java)?.let { JarFile(File(it)) }
-}
-
-fun jarUriForClass(clazz: Class<*>): URI? {
- return clazz.protectionDomain?.codeSource?.location?.toURI()
-}
-
-@OptIn(ExperimentalPathApi::class)
-@Suppress("UNUSED_PARAMETER")
-fun premain(agentArgs: String?, instrumentation: Instrumentation) {
- // Add the agent jar (i.e., the jar out of which we are currently executing) to the search path of the bootstrap
- // class loader to ensure that instrumented classes can find the CoverageMap class regardless of which ClassLoader
- // they are using.
- if (AgentJarFinder.agentJarFile != null) {
- instrumentation.appendToBootstrapClassLoaderSearch(AgentJarFinder.agentJarFile)
- } else {
- println("WARN: Failed to add agent JAR to bootstrap class loader search path")
- }
-
- val manifestCustomHookNames =
- ManifestUtils.combineManifestValues(ManifestUtils.HOOK_CLASSES).flatMap {
- it.split(':')
- }.filter { it.isNotBlank() }
- val allCustomHookNames = (manifestCustomHookNames + Opt.customHooks).toSet()
- val disabledCustomHookNames = Opt.disabledHooks.toSet()
- val customHookNames = allCustomHookNames - disabledCustomHookNames
- val disabledCustomHooksToPrint = allCustomHookNames - customHookNames.toSet()
- if (disabledCustomHooksToPrint.isNotEmpty()) {
- println("INFO: Not using the following disabled hooks: ${disabledCustomHooksToPrint.joinToString(", ")}")
- }
-
- val classNameGlobber = ClassNameGlobber(Opt.instrumentationIncludes, Opt.instrumentationExcludes + customHookNames)
- CoverageRecorder.classNameGlobber = classNameGlobber
- val customHookClassNameGlobber = ClassNameGlobber(Opt.customHookIncludes, Opt.customHookExcludes + customHookNames)
- // FIXME: Setting trace to the empty string explicitly results in all rather than no trace types
- // being applied - this is unintuitive.
- val instrumentationTypes = (Opt.trace.takeIf { it.isNotEmpty() } ?: listOf("all")).flatMap {
- when (it) {
- "cmp" -> setOf(InstrumentationType.CMP)
- "cov" -> setOf(InstrumentationType.COV)
- "div" -> setOf(InstrumentationType.DIV)
- "gep" -> setOf(InstrumentationType.GEP)
- "indir" -> setOf(InstrumentationType.INDIR)
- "native" -> setOf(InstrumentationType.NATIVE)
- // Disable GEP instrumentation by default as it appears to negatively affect fuzzing
- // performance. Our current GEP instrumentation only reports constant indices, but even
- // when we instead reported non-constant indices, they tended to completely fill up the
- // table of recent compares and value profile map.
- "all" -> InstrumentationType.values().toSet() - InstrumentationType.GEP
- else -> {
- println("WARN: Skipping unknown instrumentation type $it")
- emptySet()
- }
- }
- }.toSet()
- val idSyncFile = Opt.idSyncFile.takeUnless { it.isEmpty() }?.let {
- Paths.get(it).also { path ->
- println("INFO: Synchronizing coverage IDs in ${path.toAbsolutePath()}")
- }
- }
- val dumpClassesDir = Opt.dumpClassesDir.takeUnless { it.isEmpty() }?.let {
- Paths.get(it).toAbsolutePath().also { path ->
- if (path.exists() && path.isDirectory()) {
- println("INFO: Dumping instrumented classes into $path")
- } else {
- println("ERROR: Cannot dump instrumented classes into $path; does not exist or not a directory")
- }
- }
- }
- val includedHookNames = instrumentationTypes
- .mapNotNull { type ->
- when (type) {
- InstrumentationType.CMP -> TraceCmpHooks::class.java.name
- InstrumentationType.DIV -> TraceDivHooks::class.java.name
- InstrumentationType.INDIR -> TraceIndirHooks::class.java.name
- InstrumentationType.NATIVE -> NativeLibHooks::class.java.name
- else -> null
- }
- }
- val coverageIdSynchronizer = if (idSyncFile != null)
- FileSyncCoverageIdStrategy(idSyncFile)
- else
- MemSyncCoverageIdStrategy()
-
- val (includedHooks, customHooks) = Hooks.loadHooks(includedHookNames.toSet(), customHookNames.toSet())
- // If we don't append the JARs containing the custom hooks to the bootstrap class loader,
- // third-party hooks not contained in the agent JAR will not be able to instrument Java standard
- // library classes. These classes are loaded by the bootstrap / system class loader and would
- // not be considered when resolving references to hook methods, leading to NoClassDefFoundError
- // being thrown.
- customHooks.hookClasses
- .mapNotNull { jarUriForClass(it) }
- .toSet()
- .map { JarFile(File(it)) }
- .forEach { instrumentation.appendToBootstrapClassLoaderSearch(it) }
-
- val runtimeInstrumentor = RuntimeInstrumentor(
- instrumentation,
- classNameGlobber,
- customHookClassNameGlobber,
- instrumentationTypes,
- includedHooks.hooks,
- customHooks.hooks,
- customHooks.additionalHookClassNameGlobber,
- coverageIdSynchronizer,
- dumpClassesDir,
- )
-
- // These classes are e.g. dependencies of the RuntimeInstrumentor or hooks and thus were loaded
- // before the instrumentor was ready. Since we haven't enabled it yet, they can safely be
- // "retransformed": They haven't been transformed yet.
- val classesToRetransform = instrumentation.allLoadedClasses
- .filter {
- classNameGlobber.includes(it.name) ||
- customHookClassNameGlobber.includes(it.name) ||
- customHooks.additionalHookClassNameGlobber.includes(it.name)
- }
- .filter {
- instrumentation.isModifiableClass(it)
- }
- .toTypedArray()
-
- instrumentation.addTransformer(runtimeInstrumentor, true)
-
- if (classesToRetransform.isNotEmpty()) {
- if (instrumentation.isRetransformClassesSupported) {
- instrumentation.retransformClasses(*classesToRetransform)
- } else {
- println("WARN: Instrumentation was not applied to the following classes as they are dependencies of hooks:")
- println("WARN: ${classesToRetransform.joinToString()}")
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
deleted file mode 100644
index db6ae264..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
+++ /dev/null
@@ -1,16 +0,0 @@
-load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
-
-kt_jvm_library(
- name = "agent_lib",
- srcs = [
- "Agent.kt",
- "CoverageIdStrategy.kt",
- "RuntimeInstrumentor.kt",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//agent/src/main/java/com/code_intelligence/jazzer/instrumentor",
- "//agent/src/main/java/com/code_intelligence/jazzer/runtime",
- "//driver/src/main/java/com/code_intelligence/jazzer/driver:opt",
- ],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/agent/CoverageIdStrategy.kt b/agent/src/main/java/com/code_intelligence/jazzer/agent/CoverageIdStrategy.kt
deleted file mode 100644
index 5d1d28e3..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/agent/CoverageIdStrategy.kt
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.agent
-
-import com.code_intelligence.jazzer.utils.append
-import com.code_intelligence.jazzer.utils.readFully
-import java.nio.channels.FileChannel
-import java.nio.channels.FileLock
-import java.nio.file.Path
-import java.nio.file.StandardOpenOption
-import java.util.UUID
-
-/**
- * Indicates a fatal failure to generate synchronized coverage IDs.
- */
-class CoverageIdException(cause: Throwable? = null) :
- RuntimeException("Failed to synchronize coverage IDs", cause)
-
-/**
- * [CoverageIdStrategy] provides an abstraction to switch between context specific coverage ID generation.
- *
- * Coverage (i.e., edge) IDs differ from other kinds of IDs, such as those generated for call sites or cmp
- * instructions, in that they should be consecutive, collision-free, and lie in a known, small range.
- * This precludes us from generating them simply as hashes of class names.
- */
-interface CoverageIdStrategy {
-
- /**
- * [withIdForClass] provides the initial coverage ID of the given [className] as parameter to the
- * [block] to execute. [block] has to return the number of additionally used IDs.
- */
- @Throws(CoverageIdException::class)
- fun withIdForClass(className: String, block: (Int) -> Int)
-}
-
-/**
- * A memory synced strategy for coverage ID generation.
- *
- * This strategy uses a synchronized block to guard access to a global edge ID counter.
- * Even though concurrent fuzzing is not fully supported this strategy enables consistent coverage
- * IDs in case of concurrent class loading.
- *
- * It only prevents races within one VM instance.
- */
-class MemSyncCoverageIdStrategy : CoverageIdStrategy {
- private var nextEdgeId = 0
-
- @Synchronized
- override fun withIdForClass(className: String, block: (Int) -> Int) {
- nextEdgeId += block(nextEdgeId)
- }
-}
-
-/**
- * A strategy for coverage ID generation that synchronizes the IDs assigned to a class with other processes via the
- * specified [idSyncFile].
- * This class takes care of synchronizing the access to the file between multiple processes as long as the general
- * contract of [CoverageIdStrategy] is followed.
- */
-class FileSyncCoverageIdStrategy(private val idSyncFile: Path) : CoverageIdStrategy {
- private val uuid: UUID = UUID.randomUUID()
- private var idFileLock: FileLock? = null
-
- private var cachedFirstId: Int? = null
- private var cachedClassName: String? = null
- private var cachedIdCount: Int? = null
-
- /**
- * This method is synchronized to prevent concurrent access to the internal file lock which would result in
- * [java.nio.channels.OverlappingFileLockException]. Furthermore, every coverage ID obtained by [obtainFirstId]
- * is always committed back again to the sync file by [commitIdCount].
- */
- @Synchronized
- override fun withIdForClass(className: String, block: (Int) -> Int) {
- var actualNumEdgeIds = 0
- try {
- val firstId = obtainFirstId(className)
- actualNumEdgeIds = block(firstId)
- } finally {
- commitIdCount(actualNumEdgeIds)
- }
- }
-
- /**
- * Obtains a coverage ID for [className] such that all cooperating agent processes will obtain the same ID.
- * There are two cases to consider:
- * - This agent process is the first to encounter [className], i.e., it does not find a record for that class in
- * [idSyncFile]. In this case, a lock on the file is held until the class has been instrumented and a record with
- * the required number of coverage IDs has been added.
- * - Another agent process has already encountered [className], i.e., there is a record that class in [idSyncFile].
- * In this case, the lock on the file is returned immediately and the extracted first coverage ID is returned to
- * the caller. The caller is still expected to call [commitIdCount] so that desynchronization can be detected.
- */
- private fun obtainFirstId(className: String): Int {
- try {
- check(idFileLock == null) { "Already holding a lock on the ID file" }
- val localIdFile = FileChannel.open(
- idSyncFile,
- StandardOpenOption.WRITE,
- StandardOpenOption.READ
- )
- // Wait until we have obtained the lock on the sync file. We hold the lock from this point until we have
- // finished reading and writing (if necessary) to the file.
- val localIdFileLock = localIdFile.lock()
- check(localIdFileLock.isValid && !localIdFileLock.isShared)
- // Parse the sync file, which consists of lines of the form
- // <class name>:<first ID>:<num IDs>
- val idInfo = localIdFileLock.channel().readFully()
- .lineSequence()
- .filterNot { it.isBlank() }
- .map { line ->
- val parts = line.split(':')
- check(parts.size == 4) {
- "Expected ID file line to be of the form '<class name>:<first ID>:<num IDs>:<uuid>', got '$line'"
- }
- val lineClassName = parts[0]
- val lineFirstId = parts[1].toInt()
- check(lineFirstId >= 0) { "Negative first ID in line: $line" }
- val lineIdCount = parts[2].toInt()
- check(lineIdCount >= 0) { "Negative ID count in line: $line" }
- Triple(lineClassName, lineFirstId, lineIdCount)
- }.toList()
- cachedClassName = className
- val idInfoForClass = idInfo.filter { it.first == className }
- return when (idInfoForClass.size) {
- 0 -> {
- // We are the first to encounter this class and thus need to hold the lock until the class has been
- // instrumented and we know the required number of coverage IDs.
- idFileLock = localIdFileLock
- // Compute the next free ID as the maximum over the sums of first ID and ID count, starting at 0 if
- // this is the first ID to be assigned. In fact, since this is the only way new lines are added to
- // the file, the maximum is always attained by the last line.
- val nextFreeId = idInfo.asSequence().map { it.second + it.third }.lastOrNull() ?: 0
- cachedFirstId = nextFreeId
- nextFreeId
- }
- 1 -> {
- // This class has already been instrumented elsewhere, so we just return the first ID and ID count
- // reported from there and release the lock right away. The caller is still expected to call
- // commitIdCount.
- localIdFile.close()
- cachedIdCount = idInfoForClass.single().third
- idInfoForClass.single().second
- }
- else -> {
- localIdFile.close()
- System.err.println(idInfo.joinToString("\n") { "${it.first}:${it.second}:${it.third}" })
- throw IllegalStateException("Multiple entries for $className in ID file")
- }
- }
- } catch (e: Exception) {
- throw CoverageIdException(e)
- }
- }
-
- /**
- * Records the number of coverage IDs used to instrument the class specified in a previous call to [obtainFirstId].
- * If instrumenting the class should fail, this function must still be called. In this case, [idCount] is set to 0.
- */
- private fun commitIdCount(idCount: Int) {
- val localIdFileLock = idFileLock
- try {
- check(cachedClassName != null)
- if (localIdFileLock == null) {
- // We released the lock already in obtainFirstId since the class had already been instrumented
- // elsewhere. As we know the expected number of IDs for the current class in this case, check for
- // deviations.
- check(cachedIdCount != null)
- check(idCount == cachedIdCount) {
- "$cachedClassName has $idCount edges, but $cachedIdCount edges reserved in ID file"
- }
- } else {
- // We are the first to instrument this class and should record the number of IDs in the sync file.
- check(cachedFirstId != null)
- localIdFileLock.channel().append("$cachedClassName:$cachedFirstId:$idCount:$uuid\n")
- localIdFileLock.channel().force(true)
- }
- idFileLock = null
- cachedFirstId = null
- cachedIdCount = null
- cachedClassName = null
- } catch (e: Exception) {
- throw CoverageIdException(e)
- } finally {
- localIdFileLock?.channel()?.close()
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/agent/RuntimeInstrumentor.kt b/agent/src/main/java/com/code_intelligence/jazzer/agent/RuntimeInstrumentor.kt
deleted file mode 100644
index fe2efd54..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/agent/RuntimeInstrumentor.kt
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.agent
-
-import com.code_intelligence.jazzer.instrumentor.ClassInstrumentor
-import com.code_intelligence.jazzer.instrumentor.CoverageRecorder
-import com.code_intelligence.jazzer.instrumentor.Hook
-import com.code_intelligence.jazzer.instrumentor.InstrumentationType
-import com.code_intelligence.jazzer.utils.ClassNameGlobber
-import java.lang.instrument.ClassFileTransformer
-import java.lang.instrument.Instrumentation
-import java.nio.file.Path
-import java.security.ProtectionDomain
-import kotlin.math.roundToInt
-import kotlin.system.exitProcess
-import kotlin.time.measureTimedValue
-
-class RuntimeInstrumentor(
- private val instrumentation: Instrumentation,
- private val classesToFullyInstrument: ClassNameGlobber,
- private val classesToHookInstrument: ClassNameGlobber,
- private val instrumentationTypes: Set<InstrumentationType>,
- private val includedHooks: List<Hook>,
- private val customHooks: List<Hook>,
- // Dedicated name globber for additional classes to hook stated in hook annotations is needed due to
- // existing include and exclude pattern of classesToHookInstrument. All classes are included in hook
- // instrumentation except the ones from default excludes, like JDK and Kotlin classes. But additional
- // classes to hook, based on annotations, are allowed to reference normally ignored ones, like JDK
- // and Kotlin internals.
- // FIXME: Adding an additional class to hook will apply _all_ hooks to it and not only the one it's
- // defined in. At some point we might want to track the list of classes per custom hook rather than globally.
- private val additionalClassesToHookInstrument: ClassNameGlobber,
- private val coverageIdSynchronizer: CoverageIdStrategy,
- private val dumpClassesDir: Path?,
-) : ClassFileTransformer {
-
- @OptIn(kotlin.time.ExperimentalTime::class)
- override fun transform(
- loader: ClassLoader?,
- internalClassName: String,
- classBeingRedefined: Class<*>?,
- protectionDomain: ProtectionDomain?,
- classfileBuffer: ByteArray,
- ): ByteArray? {
- return try {
- // Bail out early if we would instrument ourselves. This prevents ClassCircularityErrors as we might need to
- // load additional Jazzer classes until we reach the full exclusion logic.
- if (internalClassName.startsWith("com/code_intelligence/jazzer/"))
- return null
- transformInternal(internalClassName, classfileBuffer)
- } catch (t: Throwable) {
- // Throwables raised from transform are silently dropped, making it extremely hard to detect instrumentation
- // failures. The docs advise to use a top-level try-catch.
- // https://docs.oracle.com/javase/9/docs/api/java/lang/instrument/ClassFileTransformer.html
- t.printStackTrace()
- throw t
- }.also { instrumentedByteCode ->
- // Only dump classes that were instrumented.
- if (instrumentedByteCode != null && dumpClassesDir != null) {
- dumpToClassFile(internalClassName, instrumentedByteCode)
- dumpToClassFile(internalClassName, classfileBuffer, basenameSuffix = ".original")
- }
- }
- }
-
- private fun dumpToClassFile(internalClassName: String, bytecode: ByteArray, basenameSuffix: String = "") {
- val relativePath = "$internalClassName$basenameSuffix.class"
- val absolutePath = dumpClassesDir!!.resolve(relativePath)
- val dumpFile = absolutePath.toFile()
- dumpFile.parentFile.mkdirs()
- dumpFile.writeBytes(bytecode)
- }
-
- override fun transform(
- module: Module?,
- loader: ClassLoader?,
- internalClassName: String,
- classBeingRedefined: Class<*>?,
- protectionDomain: ProtectionDomain?,
- classfileBuffer: ByteArray
- ): ByteArray? {
- return try {
- if (module != null && !module.canRead(RuntimeInstrumentor::class.java.module)) {
- // Make all other modules read our (unnamed) module, which allows them to access the classes needed by the
- // instrumentations, e.g. CoverageMap. If a module can't be modified, it should not be instrumented as the
- // injected bytecode might throw NoClassDefFoundError.
- // https://mail.openjdk.java.net/pipermail/jigsaw-dev/2021-May/014663.html
- if (!instrumentation.isModifiableModule(module)) {
- val prettyClassName = internalClassName.replace('/', '.')
- println("WARN: Failed to instrument $prettyClassName in unmodifiable module ${module.name}, skipping")
- return null
- }
- instrumentation.redefineModule(
- module,
- /* extraReads */ setOf(RuntimeInstrumentor::class.java.module),
- emptyMap(),
- emptyMap(),
- emptySet(),
- emptyMap()
- )
- }
- transform(loader, internalClassName, classBeingRedefined, protectionDomain, classfileBuffer)
- } catch (t: Throwable) {
- // Throwables raised from transform are silently dropped, making it extremely hard to detect instrumentation
- // failures. The docs advise to use a top-level try-catch.
- // https://docs.oracle.com/javase/9/docs/api/java/lang/instrument/ClassFileTransformer.html
- t.printStackTrace()
- throw t
- }
- }
-
- @OptIn(kotlin.time.ExperimentalTime::class)
- fun transformInternal(internalClassName: String, classfileBuffer: ByteArray): ByteArray? {
- val fullInstrumentation = when {
- classesToFullyInstrument.includes(internalClassName) -> true
- classesToHookInstrument.includes(internalClassName) -> false
- additionalClassesToHookInstrument.includes(internalClassName) -> false
- else -> return null
- }
- val prettyClassName = internalClassName.replace('/', '.')
- val (instrumentedBytecode, duration) = measureTimedValue {
- try {
- instrument(internalClassName, classfileBuffer, fullInstrumentation)
- } catch (e: CoverageIdException) {
- System.err.println("ERROR: Coverage IDs are out of sync")
- e.printStackTrace()
- exitProcess(1)
- } catch (e: Exception) {
- println("WARN: Failed to instrument $prettyClassName, skipping")
- e.printStackTrace()
- return null
- }
- }
- val durationInMs = duration.inWholeMilliseconds
- val sizeIncrease = ((100.0 * (instrumentedBytecode.size - classfileBuffer.size)) / classfileBuffer.size).roundToInt()
- if (fullInstrumentation) {
- println("INFO: Instrumented $prettyClassName (took $durationInMs ms, size +$sizeIncrease%)")
- } else {
- println("INFO: Instrumented $prettyClassName with custom hooks only (took $durationInMs ms, size +$sizeIncrease%)")
- }
- return instrumentedBytecode
- }
-
- private fun instrument(internalClassName: String, bytecode: ByteArray, fullInstrumentation: Boolean): ByteArray {
- return ClassInstrumentor(bytecode).run {
- if (fullInstrumentation) {
- // Hook instrumentation must be performed after data flow tracing as the injected
- // bytecode would trigger the GEP callbacks for byte[]. Coverage instrumentation
- // must be performed after hook instrumentation as the injected bytecode would
- // trigger the GEP callbacks for ByteBuffer.
- traceDataFlow(instrumentationTypes)
- hooks(includedHooks + customHooks)
- coverageIdSynchronizer.withIdForClass(internalClassName) { firstId ->
- coverage(firstId).also { actualNumEdgeIds ->
- CoverageRecorder.recordInstrumentedClass(
- internalClassName,
- bytecode,
- firstId,
- actualNumEdgeIds
- )
- }
- }
- } else {
- hooks(customHooks)
- }
- instrumentedBytecode
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java b/agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java
deleted file mode 100644
index 93340ee8..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzConstructionException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-// An exception wrapping a Throwable thrown during the construction of parameters for, but not the
-// actual invocation of an autofuzzed method.
-/**
- * Only used internally.
- */
-public class AutofuzzConstructionException extends RuntimeException {
- public AutofuzzConstructionException() {
- super();
- }
- public AutofuzzConstructionException(String message) {
- super(message);
- }
- public AutofuzzConstructionException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java b/agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java
deleted file mode 100644
index 7e6203ce..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/AutofuzzInvocationException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-// An exception wrapping a {@link Throwable} thrown during the actual invocation of, but not the
-// construction of parameters for an autofuzzed method.
-/**
- * Only used internally.
- */
-public class AutofuzzInvocationException extends RuntimeException {
- public AutofuzzInvocationException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/api/BUILD.bazel
deleted file mode 100644
index b26bb846..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/BUILD.bazel
+++ /dev/null
@@ -1,29 +0,0 @@
-java_library(
- name = "api",
- srcs = [
- "AutofuzzConstructionException.java",
- "AutofuzzInvocationException.java",
- "CannedFuzzedDataProvider.java",
- "Consumer1.java",
- "Consumer2.java",
- "Consumer3.java",
- "Consumer4.java",
- "Consumer5.java",
- "Function1.java",
- "Function2.java",
- "Function3.java",
- "Function4.java",
- "Function5.java",
- "FuzzedDataProvider.java",
- "FuzzerSecurityIssueCritical.java",
- "FuzzerSecurityIssueHigh.java",
- "FuzzerSecurityIssueLow.java",
- "FuzzerSecurityIssueMedium.java",
- "HookType.java",
- "Jazzer.java",
- "MethodHook.java",
- "MethodHooks.java",
- "//agent/src/main/java/jaz",
- ],
- visibility = ["//visibility:public"],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java b/agent/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java
deleted file mode 100644
index 7209a497..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/CannedFuzzedDataProvider.java
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Replays recorded FuzzedDataProvider invocations that were executed while fuzzing.
- * Note: This class is only meant to be used by Jazzer's generated reproducers.
- */
-final public class CannedFuzzedDataProvider implements FuzzedDataProvider {
- private final Iterator<Object> nextReply;
-
- public CannedFuzzedDataProvider(String can) {
- byte[] rawIn = Base64.getDecoder().decode(can);
- ArrayList<Object> recordedReplies;
- try (ByteArrayInputStream byteStream = new ByteArrayInputStream(rawIn)) {
- try (ObjectInputStream objectStream = new ObjectInputStream(byteStream)) {
- recordedReplies = (ArrayList<Object>) objectStream.readObject();
- }
- } catch (IOException | ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- nextReply = recordedReplies.iterator();
- }
-
- public static CannedFuzzedDataProvider create(List<Object> objects) {
- try {
- try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
- try (ObjectOutputStream out = new ObjectOutputStream(bout)) {
- out.writeObject(new ArrayList<>(objects));
- String base64 = Base64.getEncoder().encodeToString(bout.toByteArray());
- return new CannedFuzzedDataProvider(base64);
- }
- }
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public boolean consumeBoolean() {
- return (boolean) nextReply.next();
- }
-
- @Override
- public boolean[] consumeBooleans(int maxLength) {
- return (boolean[]) nextReply.next();
- }
-
- @Override
- public byte consumeByte() {
- return (byte) nextReply.next();
- }
-
- @Override
- public byte consumeByte(byte min, byte max) {
- return (byte) nextReply.next();
- }
-
- @Override
- public short consumeShort() {
- return (short) nextReply.next();
- }
-
- @Override
- public short consumeShort(short min, short max) {
- return (short) nextReply.next();
- }
-
- @Override
- public short[] consumeShorts(int maxLength) {
- return (short[]) nextReply.next();
- }
-
- @Override
- public int consumeInt() {
- return (int) nextReply.next();
- }
-
- @Override
- public int consumeInt(int min, int max) {
- return (int) nextReply.next();
- }
-
- @Override
- public int[] consumeInts(int maxLength) {
- return (int[]) nextReply.next();
- }
-
- @Override
- public long consumeLong() {
- return (long) nextReply.next();
- }
-
- @Override
- public long consumeLong(long min, long max) {
- return (long) nextReply.next();
- }
-
- @Override
- public long[] consumeLongs(int maxLength) {
- return (long[]) nextReply.next();
- }
-
- @Override
- public float consumeFloat() {
- return (float) nextReply.next();
- }
-
- @Override
- public float consumeRegularFloat() {
- return (float) nextReply.next();
- }
-
- @Override
- public float consumeRegularFloat(float min, float max) {
- return (float) nextReply.next();
- }
-
- @Override
- public float consumeProbabilityFloat() {
- return (float) nextReply.next();
- }
-
- @Override
- public double consumeDouble() {
- return (double) nextReply.next();
- }
-
- @Override
- public double consumeRegularDouble(double min, double max) {
- return (double) nextReply.next();
- }
-
- @Override
- public double consumeRegularDouble() {
- return (double) nextReply.next();
- }
-
- @Override
- public double consumeProbabilityDouble() {
- return (double) nextReply.next();
- }
-
- @Override
- public char consumeChar() {
- return (char) nextReply.next();
- }
-
- @Override
- public char consumeChar(char min, char max) {
- return (char) nextReply.next();
- }
-
- @Override
- public char consumeCharNoSurrogates() {
- return (char) nextReply.next();
- }
-
- @Override
- public String consumeAsciiString(int maxLength) {
- return (String) nextReply.next();
- }
-
- @Override
- public String consumeString(int maxLength) {
- return (String) nextReply.next();
- }
-
- @Override
- public String consumeRemainingAsAsciiString() {
- return (String) nextReply.next();
- }
-
- @Override
- public String consumeRemainingAsString() {
- return (String) nextReply.next();
- }
-
- @Override
- public byte[] consumeBytes(int maxLength) {
- return (byte[]) nextReply.next();
- }
-
- @Override
- public byte[] consumeRemainingAsBytes() {
- return (byte[]) nextReply.next();
- }
-
- @Override
- public int remainingBytes() {
- return (int) nextReply.next();
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java
deleted file mode 100644
index 472c2efd..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer1.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.util.function.Consumer;
-
-@FunctionalInterface
-public interface Consumer1<T1> extends Consumer<T1> {
- @Override void accept(T1 t1);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java
deleted file mode 100644
index d951ade7..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer2.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.util.function.BiConsumer;
-
-@FunctionalInterface
-public interface Consumer2<T1, T2> extends BiConsumer<T1, T2> {
- @Override void accept(T1 t1, T2 t2);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer3.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer3.java
deleted file mode 100644
index c508fe53..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer3.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-@FunctionalInterface
-public interface Consumer3<T1, T2, T3> {
- void accept(T1 t1, T2 t2, T3 t3);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer4.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer4.java
deleted file mode 100644
index 6ee70141..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer4.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-@FunctionalInterface
-public interface Consumer4<T1, T2, T3, T4> {
- void accept(T1 t1, T2 t2, T3 t3, T4 t4);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer5.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer5.java
deleted file mode 100644
index 523df53c..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Consumer5.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-@FunctionalInterface
-public interface Consumer5<T1, T2, T3, T4, T5> {
- void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Function1.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Function1.java
deleted file mode 100644
index 43d68cc7..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Function1.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.util.function.Function;
-
-@FunctionalInterface
-public interface Function1<T1, R> extends Function<T1, R> {
- @Override R apply(T1 t1);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Function2.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Function2.java
deleted file mode 100644
index 6e733b1c..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Function2.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.util.function.BiFunction;
-
-@FunctionalInterface
-public interface Function2<T1, T2, R> extends BiFunction<T1, T2, R> {
- @Override R apply(T1 t1, T2 t2);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Function3.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Function3.java
deleted file mode 100644
index 07d593f9..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Function3.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-@FunctionalInterface
-public interface Function3<T1, T2, T3, R> {
- R apply(T1 t1, T2 t2, T3 t3);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Function4.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Function4.java
deleted file mode 100644
index 0e6ec75e..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Function4.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-@FunctionalInterface
-public interface Function4<T1, T2, T3, T4, R> {
- R apply(T1 t1, T2 t2, T3 t3, T4 t4);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Function5.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Function5.java
deleted file mode 100644
index cd833f78..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Function5.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-@FunctionalInterface
-public interface Function5<T1, T2, T3, T4, T5, R> {
- R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java
deleted file mode 100644
index b1f38b50..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzedDataProvider.java
+++ /dev/null
@@ -1,444 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Random;
-
-/**
- * A convenience wrapper turning the raw fuzzer input bytes into Java primitive types.
- *
- * <p>The methods defined by this interface behave similarly to {@link Random#nextInt()}, with all
- * returned values depending deterministically on the fuzzer input for the current run.
- */
-public interface FuzzedDataProvider {
- /**
- * Consumes a {@code boolean} from the fuzzer input.
- *
- * @return a {@code boolean}
- */
- boolean consumeBoolean();
-
- /**
- * Consumes a {@code boolean} array from the fuzzer input.
- * <p>The array will usually have length {@code length}, but might be shorter if the fuzzer input
- * is not sufficiently long.
- *
- * @param maxLength the maximum length of the array
- * @return a {@code boolean} array of length at most {@code length}
- */
- boolean[] consumeBooleans(int maxLength);
-
- /**
- * Consumes a {@code byte} from the fuzzer input.
- *
- * @return a {@code byte}
- */
- byte consumeByte();
-
- /**
- * Consumes a {@code byte} between {@code min} and {@code max} from the fuzzer input.
- *
- * @param min the inclusive lower bound on the returned value
- * @param max the inclusive upper bound on the returned value
- * @return a {@code byte} in the range {@code [min, max]}
- */
- byte consumeByte(byte min, byte max);
-
- /**
- * Consumes a {@code byte} array from the fuzzer input.
- * <p>The array will usually have length {@code length}, but might be shorter if the fuzzer input
- * is not sufficiently long.
- *
- * @param maxLength the maximum length of the array
- * @return a {@code byte} array of length at most {@code length}
- */
- byte[] consumeBytes(int maxLength);
-
- /**
- * Consumes the remaining fuzzer input as a {@code byte} array.
- * <p><b>Note:</b> After calling this method, further calls to methods of this interface will
- * return fixed values only.
- *
- * @return a {@code byte} array
- */
- byte[] consumeRemainingAsBytes();
-
- /**
- * Consumes a {@code short} from the fuzzer input.
- *
- * @return a {@code short}
- */
- short consumeShort();
-
- /**
- * Consumes a {@code short} between {@code min} and {@code max} from the fuzzer input.
- *
- * @param min the inclusive lower bound on the returned value
- * @param max the inclusive upper bound on the returned value
- * @return a {@code short} in the range {@code [min, max]}
- */
- short consumeShort(short min, short max);
-
- /**
- * Consumes a {@code short} array from the fuzzer input.
- * <p>The array will usually have length {@code length}, but might be shorter if the fuzzer input
- * is not sufficiently long.
- *
- * @param maxLength the maximum length of the array
- * @return a {@code short} array of length at most {@code length}
- */
- short[] consumeShorts(int maxLength);
-
- /**
- * Consumes an {@code int} from the fuzzer input.
- *
- * @return an {@code int}
- */
- int consumeInt();
-
- /**
- * Consumes an {@code int} between {@code min} and {@code max} from the fuzzer input.
- *
- * @param min the inclusive lower bound on the returned value
- * @param max the inclusive upper bound on the returned value
- * @return an {@code int} in the range {@code [min, max]}
- */
- int consumeInt(int min, int max);
-
- /**
- * Consumes an {@code int} array from the fuzzer input.
- * <p>The array will usually have length {@code length}, but might be shorter if the fuzzer input
- * is not sufficiently long.
- *
- * @param maxLength the maximum length of the array
- * @return an {@code int} array of length at most {@code length}
- */
- int[] consumeInts(int maxLength);
-
- /**
- * Consumes a {@code long} from the fuzzer input.
- *
- * @return a {@code long}
- */
- long consumeLong();
-
- /**
- * Consumes a {@code long} between {@code min} and {@code max} from the fuzzer input.
- *
- * @param min the inclusive lower bound on the returned value
- * @param max the inclusive upper bound on the returned value
- * @return a {@code long} in the range @{code [min, max]}
- */
- long consumeLong(long min, long max);
-
- /**
- * Consumes a {@code long} array from the fuzzer input.
- * <p>The array will usually have length {@code length}, but might be shorter if the fuzzer input
- * is not sufficiently long.
- *
- * @param maxLength the maximum length of the array
- * @return a {@code long} array of length at most {@code length}
- */
- long[] consumeLongs(int maxLength);
-
- /**
- * Consumes a {@code float} from the fuzzer input.
- *
- * @return a {@code float} that may have a special value (e.g. a NaN or infinity)
- */
- float consumeFloat();
-
- /**
- * Consumes a regular {@code float} from the fuzzer input.
- *
- * @return a {@code float} that is not a special value (e.g. not a NaN or infinity)
- */
- float consumeRegularFloat();
-
- /**
- * Consumes a regular {@code float} between {@code min} and {@code max} from the fuzzer input.
- *
- * @return a {@code float} in the range {@code [min, max]}
- */
- float consumeRegularFloat(float min, float max);
-
- /**
- * Consumes a {@code float} between 0.0 and 1.0 (inclusive) from the fuzzer input.
- *
- * @return a {@code float} in the range {@code [0.0, 1.0]}
- */
- float consumeProbabilityFloat();
-
- /**
- * Consumes a {@code double} from the fuzzer input.
- *
- * @return a {@code double} that may have a special value (e.g. a NaN or infinity)
- */
- double consumeDouble();
-
- /**
- * Consumes a regular {@code double} from the fuzzer input.
- *
- * @return a {@code double} that is not a special value (e.g. not a NaN or infinity)
- */
- double consumeRegularDouble();
-
- /**
- * Consumes a regular {@code double} between {@code min} and {@code max} from the fuzzer input.
- *
- * @return a {@code double} in the range {@code [min, max]}
- */
- double consumeRegularDouble(double min, double max);
-
- /**
- * Consumes a {@code double} between 0.0 and 1.0 (inclusive) from the fuzzer input.
- *
- * @return a {@code double} in the range {@code [0.0, 1.0]}
- */
- double consumeProbabilityDouble();
-
- /**
- * Consumes a {@code char} from the fuzzer input.
- */
- char consumeChar();
-
- /**
- * Consumes a {@code char} between {@code min} and {@code max} from the fuzzer input.
- *
- * @param min the inclusive lower bound on the returned value
- * @param max the inclusive upper bound on the returned value
- * @return a {@code char} in the range {@code [min, max]}
- */
- char consumeChar(char min, char max);
-
- /**
- * Consumes a {@code char} from the fuzzer input that is never a UTF-16 surrogate character.
- */
- char consumeCharNoSurrogates();
-
- /**
- * Consumes a {@link String} from the fuzzer input.
- * <p>The returned string may be of any length between 0 and {@code maxLength}, even if there is
- * more fuzzer input available.
- *
- * @param maxLength the maximum length of the string
- * @return a {@link String} of length between 0 and {@code maxLength} (inclusive)
- */
- String consumeString(int maxLength);
-
- /**
- * Consumes the remaining fuzzer input as a {@link String}.
- * <p><b>Note:</b> After calling this method, further calls to methods of this interface will
- * return fixed values only.
- *
- * @return a {@link String}
- */
- String consumeRemainingAsString();
-
- /**
- * Consumes an ASCII-only {@link String} from the fuzzer input.
- * <p>The returned string may be of any length between 0 and {@code maxLength}, even if there is
- * more fuzzer input available.
- *
- * @param maxLength the maximum length of the string
- * @return a {@link String} of length between 0 and {@code maxLength} (inclusive) that contains
- * only ASCII characters
- */
- String consumeAsciiString(int maxLength);
-
- /**
- * Consumes the remaining fuzzer input as an ASCII-only {@link String}.
- * <p><b>Note:</b> After calling this method, further calls to methods of this interface will
- * return fixed values only.
- *
- * @return a {@link String} that contains only ASCII characters
- */
- String consumeRemainingAsAsciiString();
-
- /**
- * Returns the number of unconsumed bytes in the fuzzer input.
- *
- * @return the number of unconsumed bytes in the fuzzer input
- */
- int remainingBytes();
-
- /**
- * Picks an element from {@code collection} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param collection the {@link Collection} to pick an element from.
- * @param <T> the type of the collection element
- * @return an element from {@code collection} chosen based on the fuzzer input
- */
- @SuppressWarnings("unchecked")
- default<T> T pickValue(Collection<T> collection) {
- int size = collection.size();
- if (size == 0) {
- throw new IllegalArgumentException("collection is empty");
- }
- if (collection instanceof List<?>) {
- return ((List<T>) collection).get(consumeInt(0, size - 1));
- } else {
- return (T) pickValue(collection.toArray());
- }
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @param <T> the type of the array element
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default<T> T pickValue(T[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default boolean pickValue(boolean[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default byte pickValue(byte[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default short pickValue(short[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default int pickValue(int[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default long pickValue(long[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default double pickValue(double[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default float pickValue(float[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks an element from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @return an element from {@code array} chosen based on the fuzzer input
- */
- default char pickValue(char[] array) {
- return array[consumeInt(0, array.length - 1)];
- }
-
- /**
- * Picks {@code numOfElements} elements from {@code collection} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param collection the {@link Collection} to pick an element from.
- * @param numOfElements the number of elements to pick.
- * @param <T> the type of the collection element
- * @return an array of size {@code numOfElements} from {@code collection} chosen based on the
- * fuzzer input
- */
- default<T> List<T> pickValues(Collection<T> collection, int numOfElements) {
- int size = collection.size();
- if (size == 0) {
- throw new IllegalArgumentException("collection is empty");
- }
- if (numOfElements > collection.size()) {
- throw new IllegalArgumentException("numOfElements exceeds collection.size()");
- }
-
- List<T> remainingElements = new ArrayList<>(collection);
- List<T> pickedElements = new ArrayList<>();
- for (int i = 0; i < numOfElements; i++) {
- T element = pickValue(remainingElements);
- pickedElements.add(element);
- remainingElements.remove(element);
- }
- return pickedElements;
- }
-
- /**
- * Picks {@code numOfElements} elements from {@code array} based on the fuzzer input.
- * <p><b>Note:</b> The distribution of picks is not perfectly uniform.
- *
- * @param array the array to pick an element from.
- * @param numOfElements the number of elements to pick.
- * @param <T> the type of the array element
- * @return an array of size {@code numOfElements} from {@code array} chosen based on the fuzzer
- * input
- */
- default<T> List<T> pickValues(T[] array, int numOfElements) {
- return pickValues(Arrays.asList(array), numOfElements);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java
deleted file mode 100644
index fbde853b..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueCritical.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-/**
- * Thrown to indicate that a fuzz target has detected a critical severity security issue rather than
- * a normal bug.
- * <p>
- * There is only a semantical but no functional difference between throwing exceptions of this type
- * or any other. However, automated fuzzing platforms can use the extra information to handle the
- * detected issues appropriately.
- */
-public class FuzzerSecurityIssueCritical extends RuntimeException {
- public FuzzerSecurityIssueCritical() {}
-
- public FuzzerSecurityIssueCritical(String message) {
- super(message);
- }
-
- public FuzzerSecurityIssueCritical(String message, Throwable cause) {
- super(message, cause);
- }
-
- public FuzzerSecurityIssueCritical(Throwable cause) {
- super(cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java
deleted file mode 100644
index 05837b0e..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueHigh.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-/**
- * Thrown to indicate that a fuzz target has detected a high severity security issue rather than a
- * normal bug.
- * <p>
- * There is only a semantical but no functional difference between throwing exceptions of this type
- * or any other. However, automated fuzzing platforms can use the extra information to handle the
- * detected issues appropriately.
- */
-public class FuzzerSecurityIssueHigh extends RuntimeException {
- public FuzzerSecurityIssueHigh() {}
-
- public FuzzerSecurityIssueHigh(String message) {
- super(message);
- }
-
- public FuzzerSecurityIssueHigh(String message, Throwable cause) {
- super(message, cause);
- }
-
- public FuzzerSecurityIssueHigh(Throwable cause) {
- super(cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java
deleted file mode 100644
index 364b3afb..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueLow.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-/**
- * Thrown to indicate that a fuzz target has detected a low severity security issue rather than a
- * normal bug.
- *
- * There is only a semantical but no functional difference between throwing exceptions of this type
- * or any other. However, automated fuzzing platforms can use the extra information to handle the
- * detected issues appropriately.
- */
-public class FuzzerSecurityIssueLow extends RuntimeException {
- public FuzzerSecurityIssueLow() {}
-
- public FuzzerSecurityIssueLow(String message) {
- super(message);
- }
-
- public FuzzerSecurityIssueLow(String message, Throwable cause) {
- super(message, cause);
- }
-
- public FuzzerSecurityIssueLow(Throwable cause) {
- super(cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java b/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java
deleted file mode 100644
index be7c8c8f..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/FuzzerSecurityIssueMedium.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-/**
- * Thrown to indicate that a fuzz target has detected a medium severity security issue rather than a
- * normal bug.
- * <p>
- * There is only a semantical but no functional difference between throwing exceptions of this type
- * or any other. However, automated fuzzing platforms can use the extra information to handle the
- * detected issues appropriately.
- */
-public class FuzzerSecurityIssueMedium extends RuntimeException {
- public FuzzerSecurityIssueMedium() {}
-
- public FuzzerSecurityIssueMedium(String message) {
- super(message);
- }
-
- public FuzzerSecurityIssueMedium(String message, Throwable cause) {
- super(message, cause);
- }
-
- public FuzzerSecurityIssueMedium(Throwable cause) {
- super(cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/HookType.java b/agent/src/main/java/com/code_intelligence/jazzer/api/HookType.java
deleted file mode 100644
index 8ed4337f..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/HookType.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-/**
- * The type of a {@link MethodHook}.
- */
-// Note: The order of entries is important and is used during instrumentation.
-public enum HookType {
- BEFORE,
- REPLACE,
- AFTER,
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java b/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java
deleted file mode 100644
index 97adf578..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/Jazzer.java
+++ /dev/null
@@ -1,643 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.InvocationTargetException;
-import java.security.SecureRandom;
-
-/**
- * Helper class with static methods that interact with Jazzer at runtime.
- */
-final public class Jazzer {
- /**
- * A 32-bit random number that hooks can use to make pseudo-random choices
- * between multiple possible mutations they could guide the fuzzer towards.
- * Hooks <b>must not</b> base the decision whether or not to report a finding
- * on this number as this will make findings non-reproducible.
- * <p>
- * This is the same number that libFuzzer uses as a seed internally, which
- * makes it possible to deterministically reproduce a previous fuzzing run by
- * supplying the seed value printed by libFuzzer as the value of the
- * {@code -seed}.
- */
- public static final int SEED = getLibFuzzerSeed();
-
- private static final Class<?> JAZZER_INTERNAL;
-
- private static final MethodHandle ON_FUZZ_TARGET_READY;
-
- private static final MethodHandle TRACE_STRCMP;
- private static final MethodHandle TRACE_STRSTR;
- private static final MethodHandle TRACE_MEMCMP;
- private static final MethodHandle TRACE_PC_INDIR;
-
- private static final MethodHandle CONSUME;
- private static final MethodHandle AUTOFUZZ_FUNCTION_1;
- private static final MethodHandle AUTOFUZZ_FUNCTION_2;
- private static final MethodHandle AUTOFUZZ_FUNCTION_3;
- private static final MethodHandle AUTOFUZZ_FUNCTION_4;
- private static final MethodHandle AUTOFUZZ_FUNCTION_5;
- private static final MethodHandle AUTOFUZZ_CONSUMER_1;
- private static final MethodHandle AUTOFUZZ_CONSUMER_2;
- private static final MethodHandle AUTOFUZZ_CONSUMER_3;
- private static final MethodHandle AUTOFUZZ_CONSUMER_4;
- private static final MethodHandle AUTOFUZZ_CONSUMER_5;
-
- static {
- Class<?> jazzerInternal = null;
- MethodHandle onFuzzTargetReady = null;
- MethodHandle traceStrcmp = null;
- MethodHandle traceStrstr = null;
- MethodHandle traceMemcmp = null;
- MethodHandle tracePcIndir = null;
- MethodHandle consume = null;
- MethodHandle autofuzzFunction1 = null;
- MethodHandle autofuzzFunction2 = null;
- MethodHandle autofuzzFunction3 = null;
- MethodHandle autofuzzFunction4 = null;
- MethodHandle autofuzzFunction5 = null;
- MethodHandle autofuzzConsumer1 = null;
- MethodHandle autofuzzConsumer2 = null;
- MethodHandle autofuzzConsumer3 = null;
- MethodHandle autofuzzConsumer4 = null;
- MethodHandle autofuzzConsumer5 = null;
- try {
- jazzerInternal = Class.forName("com.code_intelligence.jazzer.runtime.JazzerInternal");
- MethodType onFuzzTargetReadyType = MethodType.methodType(void.class, Runnable.class);
- onFuzzTargetReady = MethodHandles.publicLookup().findStatic(
- jazzerInternal, "registerOnFuzzTargetReadyCallback", onFuzzTargetReadyType);
- Class<?> traceDataFlowNativeCallbacks =
- Class.forName("com.code_intelligence.jazzer.runtime.TraceDataFlowNativeCallbacks");
-
- // Use method handles for hints as the calls are potentially performance critical.
- MethodType traceStrcmpType =
- MethodType.methodType(void.class, String.class, String.class, int.class, int.class);
- traceStrcmp = MethodHandles.publicLookup().findStatic(
- traceDataFlowNativeCallbacks, "traceStrcmp", traceStrcmpType);
- MethodType traceStrstrType =
- MethodType.methodType(void.class, String.class, String.class, int.class);
- traceStrstr = MethodHandles.publicLookup().findStatic(
- traceDataFlowNativeCallbacks, "traceStrstr", traceStrstrType);
- MethodType traceMemcmpType =
- MethodType.methodType(void.class, byte[].class, byte[].class, int.class, int.class);
- traceMemcmp = MethodHandles.publicLookup().findStatic(
- traceDataFlowNativeCallbacks, "traceMemcmp", traceMemcmpType);
- MethodType tracePcIndirType = MethodType.methodType(void.class, int.class, int.class);
- tracePcIndir = MethodHandles.publicLookup().findStatic(
- traceDataFlowNativeCallbacks, "tracePcIndir", tracePcIndirType);
-
- Class<?> metaClass = Class.forName("com.code_intelligence.jazzer.autofuzz.Meta");
- MethodType consumeType =
- MethodType.methodType(Object.class, FuzzedDataProvider.class, Class.class);
- consume = MethodHandles.publicLookup().findStatic(metaClass, "consume", consumeType);
-
- autofuzzFunction1 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(Object.class, FuzzedDataProvider.class, Function1.class));
- autofuzzFunction2 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(Object.class, FuzzedDataProvider.class, Function2.class));
- autofuzzFunction3 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(Object.class, FuzzedDataProvider.class, Function3.class));
- autofuzzFunction4 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(Object.class, FuzzedDataProvider.class, Function4.class));
- autofuzzFunction5 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(Object.class, FuzzedDataProvider.class, Function5.class));
- autofuzzConsumer1 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer1.class));
- autofuzzConsumer2 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer2.class));
- autofuzzConsumer3 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer3.class));
- autofuzzConsumer4 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer4.class));
- autofuzzConsumer5 = MethodHandles.publicLookup().findStatic(metaClass, "autofuzz",
- MethodType.methodType(void.class, FuzzedDataProvider.class, Consumer5.class));
- } catch (ClassNotFoundException ignore) {
- // Not running in the context of the agent. This is fine as long as no methods are called on
- // this class.
- } catch (NoSuchMethodException | IllegalAccessException e) {
- // This should never happen as the Jazzer API is loaded from the agent and thus should always
- // match the version of the runtime classes.
- System.err.println("ERROR: Incompatible version of the Jazzer API detected, please update.");
- e.printStackTrace();
- System.exit(1);
- }
- JAZZER_INTERNAL = jazzerInternal;
- ON_FUZZ_TARGET_READY = onFuzzTargetReady;
- TRACE_STRCMP = traceStrcmp;
- TRACE_STRSTR = traceStrstr;
- TRACE_MEMCMP = traceMemcmp;
- TRACE_PC_INDIR = tracePcIndir;
- CONSUME = consume;
- AUTOFUZZ_FUNCTION_1 = autofuzzFunction1;
- AUTOFUZZ_FUNCTION_2 = autofuzzFunction2;
- AUTOFUZZ_FUNCTION_3 = autofuzzFunction3;
- AUTOFUZZ_FUNCTION_4 = autofuzzFunction4;
- AUTOFUZZ_FUNCTION_5 = autofuzzFunction5;
- AUTOFUZZ_CONSUMER_1 = autofuzzConsumer1;
- AUTOFUZZ_CONSUMER_2 = autofuzzConsumer2;
- AUTOFUZZ_CONSUMER_3 = autofuzzConsumer3;
- AUTOFUZZ_CONSUMER_4 = autofuzzConsumer4;
- AUTOFUZZ_CONSUMER_5 = autofuzzConsumer5;
- }
-
- private Jazzer() {}
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Function1} with (partially) specified
- * type variables, e.g. {@code (Function1<String, ?>) String::new}.
- * @return the return value of {@code func}, or {@code null} if {@code autofuzz} failed to invoke
- * the function.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- @SuppressWarnings("unchecked")
- public static <T1, R> R autofuzz(FuzzedDataProvider data, Function1<T1, R> func) {
- try {
- return (R) AUTOFUZZ_FUNCTION_1.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- // Not reached.
- return null;
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Function2} with (partially) specified
- * type variables.
- * @return the return value of {@code func}, or {@code null} if {@code autofuzz} failed to invoke
- * the function.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- @SuppressWarnings("unchecked")
- public static <T1, T2, R> R autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func) {
- try {
- return (R) AUTOFUZZ_FUNCTION_2.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- // Not reached.
- return null;
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Function3} with (partially) specified
- * type variables.
- * @return the return value of {@code func}, or {@code null} if {@code autofuzz} failed to invoke
- * the function.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, R> R autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func) {
- try {
- return (R) AUTOFUZZ_FUNCTION_3.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- // Not reached.
- return null;
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Function4} with (partially) specified
- * type variables.
- * @return the return value of {@code func}, or {@code null} if {@code autofuzz} failed to invoke
- * the function.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, T4, R> R autofuzz(
- FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func) {
- try {
- return (R) AUTOFUZZ_FUNCTION_4.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- // Not reached.
- return null;
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Function5} with (partially) specified
- * type variables.
- * @return the return value of {@code func}, or {@code null} if {@code autofuzz} failed to invoke
- * the function.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, T4, T5, R> R autofuzz(
- FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func) {
- try {
- return (R) AUTOFUZZ_FUNCTION_5.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- // Not reached.
- return null;
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Consumer1} with explicitly specified
- * type variable.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- public static <T1> void autofuzz(FuzzedDataProvider data, Consumer1<T1> func) {
- try {
- AUTOFUZZ_CONSUMER_1.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Consumer2} with (partially) specified
- * type variables.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- public static <T1, T2> void autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func) {
- try {
- AUTOFUZZ_CONSUMER_2.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Consumer3} with (partially) specified
- * type variables.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- public static <T1, T2, T3> void autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func) {
- try {
- AUTOFUZZ_CONSUMER_3.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Consumer4} with (partially) specified
- * type variables.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- public static <T1, T2, T3, T4> void autofuzz(
- FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func) {
- try {
- AUTOFUZZ_CONSUMER_4.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- }
-
- /**
- * Attempts to invoke {@code func} with arguments created automatically from the fuzzer input
- * using only public methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to execute {@code func} in
- * meaningful ways for a number of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param func a method reference for the function to autofuzz. If there are multiple overloads,
- * resolve ambiguities by explicitly casting to {@link Consumer5} with (partially) specified
- * type variables.
- * @throws Throwable any {@link Throwable} thrown by {@code func}, or an {@link
- * AutofuzzConstructionException} if autofuzz failed to construct the arguments for the call.
- * The {@link Throwable} is thrown unchecked.
- */
- public static <T1, T2, T3, T4, T5> void autofuzz(
- FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func) {
- try {
- AUTOFUZZ_CONSUMER_5.invoke(data, func);
- } catch (AutofuzzInvocationException e) {
- rethrowUnchecked(e.getCause());
- } catch (Throwable t) {
- rethrowUnchecked(t);
- }
- }
-
- /**
- * Attempts to construct an instance of {@code type} from the fuzzer input using only public
- * methods available on the classpath.
- * <p>
- * <b>Note:</b> This function is inherently heuristic and may fail to return meaningful values for
- * a variety of reasons.
- *
- * @param data the {@link FuzzedDataProvider} instance provided to {@code fuzzerTestOneInput}.
- * @param type the {@link Class} to construct an instance of.
- * @return an instance of {@code type} constructed from the fuzzer input, or {@code null} if
- * autofuzz failed to create an instance.
- */
- @SuppressWarnings("unchecked")
- public static <T> T consume(FuzzedDataProvider data, Class<T> type) {
- try {
- return (T) CONSUME.invokeExact(data, type);
- } catch (AutofuzzConstructionException ignored) {
- return null;
- } catch (Throwable t) {
- rethrowUnchecked(t);
- // Not reached.
- return null;
- }
- }
-
- /**
- * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code
- * target}.
- * <p>
- * If the relation between the raw fuzzer input and the value of {@code current} is relatively
- * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to
- * achieve equality.
- *
- * @param current a non-constant string observed during fuzz target execution
- * @param target a string that {@code current} should become equal to, but currently isn't
- * @param id a (probabilistically) unique identifier for this particular compare hint
- */
- public static void guideTowardsEquality(String current, String target, int id) {
- if (TRACE_STRCMP == null) {
- return;
- }
- try {
- TRACE_STRCMP.invokeExact(current, target, 1, id);
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Instructs the fuzzer to guide its mutations towards making {@code current} equal to {@code
- * target}.
- * <p>
- * If the relation between the raw fuzzer input and the value of {@code current} is relatively
- * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to
- * achieve equality.
- *
- * @param current a non-constant byte array observed during fuzz target execution
- * @param target a byte array that {@code current} should become equal to, but currently isn't
- * @param id a (probabilistically) unique identifier for this particular compare hint
- */
- public static void guideTowardsEquality(byte[] current, byte[] target, int id) {
- if (TRACE_MEMCMP == null) {
- return;
- }
- try {
- TRACE_MEMCMP.invokeExact(current, target, 1, id);
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Instructs the fuzzer to guide its mutations towards making {@code haystack} contain {@code
- * needle} as a substring.
- * <p>
- * If the relation between the raw fuzzer input and the value of {@code haystack} is relatively
- * complex, running the fuzzer with the argument {@code -use_value_profile=1} may be necessary to
- * satisfy the substring check.
- *
- * @param haystack a non-constant string observed during fuzz target execution
- * @param needle a string that should be contained in {@code haystack} as a substring, but
- * currently isn't
- * @param id a (probabilistically) unique identifier for this particular compare hint
- */
- public static void guideTowardsContainment(String haystack, String needle, int id) {
- if (TRACE_STRSTR == null) {
- return;
- }
- try {
- TRACE_STRSTR.invokeExact(haystack, needle, id);
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Instructs the fuzzer to attain as many possible values for the absolute value of {@code state}
- * as possible.
- * <p>
- * Call this function from a fuzz target or a hook to help the fuzzer track partial progress
- * (e.g. by passing the length of a common prefix of two lists that should become equal) or
- * explore different values of state that is not directly related to code coverage (see the
- * MazeFuzzer example).
- * <p>
- * <b>Note:</b> This hint only takes effect if the fuzzer is run with the argument
- * {@code -use_value_profile=1}.
- *
- * @param state a numeric encoding of a state that should be varied by the fuzzer
- * @param id a (probabilistically) unique identifier for this particular state hint
- */
- public static void exploreState(byte state, int id) {
- if (TRACE_PC_INDIR == null) {
- return;
- }
- // We only use the lower 7 bits of state, which allows for 128 different state values tracked
- // per id. The particular amount of 7 bits of state is also used in libFuzzer's
- // TracePC::HandleCmp:
- // https://github.com/llvm/llvm-project/blob/c12d49c4e286fa108d4d69f1c6d2b8d691993ffd/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390
- // This value should be large enough for most use cases (e.g. tracking the length of a prefix in
- // a comparison) while being small enough that the bitmap isn't filled up too quickly
- // (65536 bits/ 128 bits per id = 512 ids).
-
- // We use tracePcIndir as a way to set a bit in libFuzzer's value profile bitmap. In
- // TracePC::HandleCallerCallee, which is what this function ultimately calls through to, the
- // lower 12 bits of each argument are combined into a 24-bit index into the bitmap, which is
- // then reduced modulo a 16-bit prime. To keep the modulo bias small, we should fill as many
- // of the relevant bits as possible. However, there are the following restrictions:
- // 1. Since we use the return address trampoline to set the caller address indirectly, its
- // upper 3 bits are fixed, which leaves a total of 21 variable bits on x86_64.
- // 2. On arm64 macOS, where every instruction is aligned to 4 bytes, the lower 2 bits of the
- // caller address will always be zero, further reducing the number of variable bits in the
- // caller parameter to 7.
- // https://github.com/llvm/llvm-project/blob/c12d49c4e286fa108d4d69f1c6d2b8d691993ffd/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L121
- // Even taking these restrictions into consideration, we pass state in the lowest bits of the
- // caller address, which is used to form the lowest bits of the bitmap index. This should result
- // in the best caching behavior as state is expected to change quickly in consecutive runs and
- // in this way all its bitmap entries would be located close to each other in memory.
- int lowerBits = (state & 0x7f) | (id << 7);
- int upperBits = id >>> 5;
- try {
- TRACE_PC_INDIR.invokeExact(upperBits, lowerBits);
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Make Jazzer report the provided {@link Throwable} as a finding.
- * <p>
- * <b>Note:</b> This method must only be called from a method hook. In a
- * fuzz target, simply throw an exception to trigger a finding.
- * @param finding the finding that Jazzer should report
- */
- public static void reportFindingFromHook(Throwable finding) {
- try {
- JAZZER_INTERNAL.getMethod("reportFindingFromHook", Throwable.class).invoke(null, finding);
- } catch (NullPointerException | IllegalAccessException | NoSuchMethodException e) {
- // We can only reach this point if the runtime is not on the classpath, e.g. in case of a
- // reproducer. Just throw the finding.
- rethrowUnchecked(finding);
- } catch (InvocationTargetException e) {
- // reportFindingFromHook throws a HardToCatchThrowable, which will bubble up wrapped in an
- // InvocationTargetException that should not be stopped here.
- if (e.getCause().getClass().getName().endsWith(".HardToCatchError")) {
- throw(Error) e.getCause();
- } else {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * Register a callback to be executed right before the fuzz target is executed for the first time.
- * <p>
- * This can be used to disable hooks until after Jazzer has been fully initializing, e.g. to
- * prevent Jazzer internals from triggering hooks on Java standard library classes.
- *
- * @param callback the callback to execute
- */
- public static void onFuzzTargetReady(Runnable callback) {
- try {
- ON_FUZZ_TARGET_READY.invokeExact(callback);
- } catch (Throwable e) {
- e.printStackTrace();
- }
- }
-
- private static int getLibFuzzerSeed() {
- // The Jazzer driver sets this property based on the value of libFuzzer's -seed command-line
- // option, which allows for fully reproducible fuzzing runs if set. If not running in the
- // context of the driver, fall back to a random number instead.
- String rawSeed = System.getProperty("jazzer.seed");
- if (rawSeed == null) {
- return new SecureRandom().nextInt();
- }
- // If jazzer.seed is set, we expect it to be a valid integer.
- return Integer.parseUnsignedInt(rawSeed);
- }
-
- // Rethrows a (possibly checked) exception while avoiding a throws declaration.
- @SuppressWarnings("unchecked")
- private static <T extends Throwable> void rethrowUnchecked(Throwable t) throws T {
- throw(T) t;
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java b/agent/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java
deleted file mode 100644
index 3a1c5f39..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/MethodHook.java
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Repeatable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.invoke.MethodType;
-
-/**
- * Registers the annotated method as a hook that should run before, instead or
- * after the method specified by the annotation parameters.
- * <p>
- * Depending on {@link #type()} this method will be called after, instead or
- * before every call to the target method and has
- * access to its parameters and return value. The target method is specified by
- * {@link #targetClassName()} and {@link #targetMethod()}. In case of an
- * overloaded method, {@link #targetMethodDescriptor()} can be used to restrict
- * the application of the hook to a particular overload.
- * <p>
- * The signature of the annotated method must be as follows (this does not
- * restrict the method name and parameter names, which are arbitrary),
- * depending on the value of {@link #type()}:
- *
- * <dl>
- * <dt><span class="strong">{@link HookType#BEFORE}</span>
- * <dd>
- * <pre>{@code
- * public static void hook(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
- * }</pre>
- * Arguments:
- * <p><ul>
- * <li>{@code method}: A {@link java.lang.invoke.MethodHandle} representing the
- * original method. The original method can be invoked via
- * {@link java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This
- * requires passing {@code thisObject} as the first argument if the method is
- * not static. This argument can be {@code null}.
- * <li>{@code thisObject}: An {@link Object} containing the implicit
- * {@code this} argument to the original method. If the original method is
- * static, this argument will be {@code null}.
- * <li>{@code arguments}: An array of {@link Object}s containing the arguments
- * passed to the original method. Primitive types (e.g. {@code boolean}) will be
- * wrapped into their corresponding wrapper type (e.g. {@link Boolean}).
- * <li>{@code hookId}: A random {@code int} identifying the particular call
- * site.This can be used to derive additional coverage information.
- * </ul>
- *
- * <dt><span class="strong">{@link HookType#REPLACE}</span>
- * <dd>
- * <pre>{@code
- * public static Object hook(MethodHandle method, Object thisObject, Object[] arguments, int hookId)
- * }</pre>
- * The return type may alternatively be taken to be the exact return type of
- * target method or a wrapper type thereof. The returned object will be casted
- * and unwrapped automatically.
- * <p>
- * Arguments:
- * <p><ul>
- * <li>{@code method}: A {@link java.lang.invoke.MethodHandle} representing the
- * original method. The original method can be invoked via
- * {@link java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This
- * requires passing {@code thisObject} as the first argument if the method is
- * not static. This argument can be {@code null}.
- * <li>{@code thisObject}: An {@link Object} containing the implicit
- * {@code this} argument to the original method. If the original method is
- * static, this argument will be {@code null}.
- * <li>{@code arguments}: An array of {@link Object}s containing the arguments
- * passed to the original method. Primitive types (e.g. {@code boolean}) will be
- * wrapped into their corresponding wrapper type (e.g. {@link Boolean}).
- * <li>{@code hookId}: A random {@code int} identifying the particular call
- * site.This can be used to derive additional coverage information.
- * </ul><p>
- * <p>
- * Return value: the value that should take the role of the value the target
- * method would have returned
- * <p>
- * <dt><span class="strong">{@link HookType#AFTER}</span>
- * <dd>
- * <pre>{@code
- * public static void hook(MethodHandle method, Object thisObject, Object[] arguments, int hookId,
- * Object returnValue)
- * }</pre>
- * Arguments:
- * <p><ul>
- * <li>{@code method}: A {@link java.lang.invoke.MethodHandle} representing the
- * original method. The original method can be invoked via
- * {@link java.lang.invoke.MethodHandle#invokeWithArguments(Object...)}. This
- * requires passing {@code thisObject} as the first argument if the method is
- * not static. This argument can be {@code null}.
- * <li>{@code thisObject}: An {@link Object} containing the implicit
- * {@code this} argument to the original method. If the original method is
- * static, this argument will be {@code null}.
- * <li>{@code arguments}: An array of {@link Object}s containing the arguments
- * passed to the original method. Primitive types (e.g. {@code boolean}) will be
- * wrapped into their corresponding wrapper type (e.g. {@link Boolean}).
- * <li>{@code hookId}: A random {@code int} identifying the particular call
- * site.This can be used to derive additional coverage information.
- * <li>{@code returnValue}: An {@link Object} containing the return value of the
- * invocation of the original method. Primitive types (e.g. {@code boolean})
- * will be wrapped into their corresponding wrapper type (e.g. {@link Boolean}).
- * If the original method has return type {@code void}, this value will be
- * {@code null}.
- * <p>
- * Multiple {@link HookType#BEFORE} and {@link HookType#AFTER} hooks are
- * allowed to reference the same target method. Exclusively one
- * {@link HookType#REPLACE} hook may reference a target method, no other types
- * allowed. Attention must be paid to not guide the Fuzzer in different
- * directions via {@link Jazzer}'s {@code guideTowardsXY} methods in the
- * different hooks.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-@Repeatable(MethodHooks.class)
-@Documented
-public @interface MethodHook {
- /**
- * The time at which the annotated method should be called.
- * <p>
- * If this is {@link HookType#BEFORE}, the annotated method will be called
- * before the target method and has access to its arguments.
- * <p>
- * If this is {@link HookType#REPLACE}, the annotated method will be called
- * instead of the target method. It has access to its arguments and can
- * return a value that will replace the target method's return value.
- * <p>
- * If this is {@link HookType#AFTER}, the annotated method will be called
- * after the target method and has access to its arguments and return
- * value.
- *
- * @return when the hook should be called
- */
- HookType type();
-
- /**
- * The name of the class that contains the method that should be hooked,
- * as returned by {@link Class#getName()}.
- * <p>
- * If an interface or abstract class is specified, also calls to all
- * implementations and subclasses available on the classpath during startup
- * are hooked, respectively. Interfaces and subclasses are not taken into
- * account for concrete classes.
- * <p>
- * Examples:
- * <p><ul>
- * <li>{@link String}: {@code "java.lang.String"}
- * <li>{@link java.nio.file.FileSystem}: {@code "java.nio.file.FileSystem"}
- * </ul><p>
- *
- * @return the name of the class containing the method to be hooked
- */
- String targetClassName();
-
- /**
- * The name of the method to be hooked. Use {@code "<init>"} for
- * constructors.
- * <p>
- * Examples:
- * <p><ul>
- * <li>{@link String#equals(Object)}: {@code "equals"}
- * <li>{@link String#String()}: {@code "<init>"}
- * </ul><p>
- *
- * @return the name of the method to be hooked
- */
- String targetMethod();
-
- /**
- * The descriptor of the method to be hooked. This is only needed if there
- * are multiple methods with the same name and not all of them should be
- * hooked.
- * <p>
- * The descriptor of a method is an internal representation of the method's
- * signature, which includes the types of its parameters and its return
- * value. For more information on descriptors, see the
- * <a href=https://docs.oracle.com/javase/specs/jvms/se15/html/jvms-4.html#jvms-4.3.3>JVM
- * Specification, Section 4.3.3</a> and {@link MethodType#toMethodDescriptorString()}
- *
- * @return the descriptor of the method to be hooked
- */
- String targetMethodDescriptor() default "";
-
- /**
- * Array of additional classes to hook.
- * <p>
- * Hooks are applied on call sites. This means that classes calling the one
- * defined in this annotation need to be instrumented to actually execute
- * the hook. This property can be used to hook normally ignored classes.
- *
- * @return fully qualified class names to hook
- */
- String[] additionalClassesToHook() default {};
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java b/agent/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java
deleted file mode 100644
index 7eec24b3..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/api/MethodHooks.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.api;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Internal helper allowing to apply multiple {@link MethodHook} annotations to the same method.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-@Documented
-public @interface MethodHooks {
- MethodHook[] value();
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java
deleted file mode 100644
index 2fbed971..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzCodegenVisitor.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.autofuzz;
-
-import java.util.Stack;
-import java.util.stream.Collectors;
-
-public class AutofuzzCodegenVisitor {
- private final Stack<Group> groups = new Stack<>();
- private int variableCounter = 0;
-
- AutofuzzCodegenVisitor() {
- init();
- }
-
- private void init() {
- pushGroup("", "", "");
- }
-
- public void pushGroup(String prefix, String delimiter, String suffix) {
- groups.push(new Group(prefix, delimiter, suffix));
- }
-
- public void pushElement(String element) {
- groups.peek().push(element);
- }
-
- public void popElement() {
- groups.peek().pop();
- }
-
- public void popGroup() {
- if (groups.size() == 1) {
- throw new AutofuzzError(
- "popGroup must be called exactly once for every pushGroup: " + toDebugString());
- }
- pushElement(groups.pop().toString());
- }
-
- public String generate() {
- if (groups.size() != 1) {
- throw new AutofuzzError(
- "popGroup must be called exactly once for every pushGroup: " + toDebugString());
- }
- return groups.pop().toString();
- }
-
- public void addCharLiteral(char c) {
- pushElement("'" + escapeForLiteral(Character.toString(c)) + "'");
- }
-
- public void addStringLiteral(String string) {
- pushElement('"' + escapeForLiteral(string) + '"');
- }
-
- public String uniqueVariableName() {
- return String.format("autofuzzVariable%s", variableCounter++);
- }
-
- private String escapeForLiteral(String string) {
- // The list of escape sequences is taken from:
- // https://docs.oracle.com/javase/tutorial/java/data/characters.html
- return string.replace("\t", "\\t")
- .replace("\b", "\\b")
- .replace("\n", "\\n")
- .replace("\r", "\\r")
- .replace("\f", "\\f")
- .replace("\f", "\\f")
- .replace("\"", "\\\"")
- .replace("'", "\\'")
- .replace("\\", "\\\\");
- }
-
- private String toDebugString() {
- return groups.stream()
- .map(group -> group.elements.stream().collect(Collectors.joining(", ", "[", "]")))
- .collect(Collectors.joining(", ", "[", "]"));
- }
-
- private static class Group {
- private final String prefix;
- private final String delimiter;
- private final String suffix;
- private final Stack<String> elements = new Stack<>();
-
- Group(String prefix, String delimiter, String suffix) {
- this.prefix = prefix;
- this.delimiter = delimiter;
- this.suffix = suffix;
- }
-
- public void push(String element) {
- elements.push(element);
- }
-
- public void pop() {
- elements.pop();
- }
-
- @Override
- public String toString() {
- return elements.stream().collect(Collectors.joining(delimiter, prefix, suffix));
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java
deleted file mode 100644
index a94b385d..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/AutofuzzError.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.autofuzz;
-
-/**
- * An error indicating an internal error in the autofuzz functionality.
- */
-public class AutofuzzError extends Error {
- private static final String MESSAGE_TRAILER = String.format(
- "%nPlease file an issue at:%n https://github.com/CodeIntelligenceTesting/jazzer/issues/new/choose");
-
- public AutofuzzError(String message) {
- super(message + MESSAGE_TRAILER);
- }
-
- public AutofuzzError(String message, Throwable cause) {
- super(message + MESSAGE_TRAILER, cause);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel
deleted file mode 100644
index 779f79cb..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/BUILD.bazel
+++ /dev/null
@@ -1,17 +0,0 @@
-java_library(
- name = "autofuzz",
- srcs = [
- "AutofuzzCodegenVisitor.java",
- "AutofuzzError.java",
- "FuzzTarget.java",
- "Meta.java",
- "YourAverageJavaClass.java",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//agent/src/main/java/com/code_intelligence/jazzer/api",
- "//agent/src/main/java/com/code_intelligence/jazzer/utils",
- "@com_github_classgraph_classgraph//:classgraph",
- "@com_github_jhalterman_typetools//:typetools",
- ],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java
deleted file mode 100644
index 3b0d046b..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/FuzzTarget.java
+++ /dev/null
@@ -1,317 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.autofuzz;
-
-import com.code_intelligence.jazzer.api.AutofuzzConstructionException;
-import com.code_intelligence.jazzer.api.AutofuzzInvocationException;
-import com.code_intelligence.jazzer.api.FuzzedDataProvider;
-import com.code_intelligence.jazzer.utils.SimpleGlobMatcher;
-import com.code_intelligence.jazzer.utils.Utils;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Executable;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public final class FuzzTarget {
- private static final String AUTOFUZZ_REPRODUCER_TEMPLATE = "public class Crash_%s {\n"
- + " public static void main(String[] args) throws Throwable {\n"
- + " %s;\n"
- + " }\n"
- + "}";
- private static final long MAX_EXECUTIONS_WITHOUT_INVOCATION = 100;
-
- private static String methodReference;
- private static Executable[] targetExecutables;
- private static Map<Executable, Class<?>[]> throwsDeclarations;
- private static Set<SimpleGlobMatcher> ignoredExceptionMatchers;
- private static long executionsSinceLastInvocation = 0;
-
- public static void fuzzerInitialize(String[] args) {
- if (args.length == 0 || !args[0].contains("::")) {
- System.err.println(
- "Expected the argument to --autofuzz to be a method reference (e.g. System.out::println)");
- System.exit(1);
- }
- methodReference = args[0];
- String[] parts = methodReference.split("::", 2);
- String className = parts[0];
- String methodNameAndOptionalDescriptor = parts[1];
- String methodName;
- String descriptor;
- int descriptorStart = methodNameAndOptionalDescriptor.indexOf('(');
- if (descriptorStart != -1) {
- methodName = methodNameAndOptionalDescriptor.substring(0, descriptorStart);
- // URL decode the descriptor to allow copy-pasting from javadoc links such as:
- // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#valueOf(char%5B%5D)
- try {
- descriptor =
- URLDecoder.decode(methodNameAndOptionalDescriptor.substring(descriptorStart), "UTF-8");
- } catch (UnsupportedEncodingException e) {
- // UTF-8 is always supported.
- e.printStackTrace();
- System.exit(1);
- return;
- }
- } else {
- methodName = methodNameAndOptionalDescriptor;
- descriptor = null;
- }
-
- Class<?> targetClass = null;
- String targetClassName = className;
- do {
- try {
- // Explicitly invoking static initializers to trigger some coverage in the code.
- targetClass = Class.forName(targetClassName, true, ClassLoader.getSystemClassLoader());
- } catch (ClassNotFoundException e) {
- int classSeparatorIndex = targetClassName.lastIndexOf(".");
- if (classSeparatorIndex == -1) {
- System.err.printf(
- "Failed to find class %s for autofuzz, please ensure it is contained in the classpath "
- + "specified with --cp and specify the full package name%n",
- className);
- e.printStackTrace();
- System.exit(1);
- return;
- }
- StringBuilder classNameBuilder = new StringBuilder(targetClassName);
- classNameBuilder.setCharAt(classSeparatorIndex, '$');
- targetClassName = classNameBuilder.toString();
- }
- } while (targetClass == null);
-
- boolean isConstructor = methodName.equals("new");
- if (isConstructor) {
- targetExecutables =
- Arrays.stream(targetClass.getConstructors())
- .filter(constructor
- -> descriptor == null
- || Utils.getReadableDescriptor(constructor).equals(descriptor))
- .toArray(Executable[] ::new);
- } else {
- // We use getDeclaredMethods and filter for the public access modifier instead of using
- // getMethods as we want to exclude methods inherited from superclasses or interfaces, which
- // can lead to unexpected results when autofuzzing. If desired, these can be autofuzzed
- // explicitly instead.
- targetExecutables =
- Arrays.stream(targetClass.getDeclaredMethods())
- .filter(method -> Modifier.isPublic(method.getModifiers()))
- .filter(method
- -> method.getName().equals(methodName)
- && (descriptor == null
- || Utils.getReadableDescriptor(method).equals(descriptor)))
- .toArray(Executable[] ::new);
- }
- if (targetExecutables.length == 0) {
- if (isConstructor) {
- if (descriptor == null) {
- System.err.printf(
- "Failed to find accessible constructors in class %s for autofuzz.%n", className);
- } else {
- System.err.printf(
- "Failed to find accessible constructors with signature %s in class %s for autofuzz.%n"
- + "Accessible constructors:%n%s",
- descriptor, className,
- Arrays.stream(targetClass.getConstructors())
- .map(method
- -> String.format("%s::new%s", method.getDeclaringClass().getName(),
- Utils.getReadableDescriptor(method)))
- .distinct()
- .collect(Collectors.joining(System.lineSeparator())));
- }
- } else {
- if (descriptor == null) {
- System.err.printf("Failed to find accessible methods named %s in class %s for autofuzz.%n"
- + "Accessible methods:%n%s",
- methodName, className,
- Arrays.stream(targetClass.getMethods())
- .map(method
- -> String.format(
- "%s::%s", method.getDeclaringClass().getName(), method.getName()))
- .distinct()
- .collect(Collectors.joining(System.lineSeparator())));
- } else {
- System.err.printf(
- "Failed to find accessible methods named %s with signature %s in class %s for autofuzz.%n"
- + "Accessible methods with that name:%n%s",
- methodName, descriptor, className,
- Arrays.stream(targetClass.getMethods())
- .filter(method -> method.getName().equals(methodName))
- .map(method
- -> String.format("%s::%s%s", method.getDeclaringClass().getName(),
- method.getName(), Utils.getReadableDescriptor(method)))
- .distinct()
- .collect(Collectors.joining(System.lineSeparator())));
- }
- }
- System.exit(1);
- }
-
- ignoredExceptionMatchers = Arrays.stream(args)
- .skip(1)
- .filter(s -> s.contains("*"))
- .map(SimpleGlobMatcher::new)
- .collect(Collectors.toSet());
-
- List<Class<?>> alwaysIgnore =
- Arrays.stream(args)
- .skip(1)
- .filter(s -> !s.contains("*"))
- .map(name -> {
- try {
- return ClassLoader.getSystemClassLoader().loadClass(name);
- } catch (ClassNotFoundException e) {
- System.err.printf("Failed to find class '%s' specified in --autofuzz_ignore", name);
- System.exit(1);
- }
- throw new Error("Not reached");
- })
- .collect(Collectors.toList());
- throwsDeclarations =
- Arrays.stream(targetExecutables)
- .collect(Collectors.toMap(method
- -> method,
- method
- -> Stream.concat(Arrays.stream(method.getExceptionTypes()), alwaysIgnore.stream())
- .toArray(Class[] ::new)));
- }
-
- public static void fuzzerTestOneInput(FuzzedDataProvider data) throws Throwable {
- AutofuzzCodegenVisitor codegenVisitor = null;
- if (Meta.isDebug()) {
- codegenVisitor = new AutofuzzCodegenVisitor();
- }
- fuzzerTestOneInput(data, codegenVisitor);
- if (codegenVisitor != null) {
- System.err.println(codegenVisitor.generate());
- }
- }
-
- public static void dumpReproducer(FuzzedDataProvider data, String reproducerPath, String sha) {
- AutofuzzCodegenVisitor codegenVisitor = new AutofuzzCodegenVisitor();
- try {
- fuzzerTestOneInput(data, codegenVisitor);
- } catch (Throwable ignored) {
- }
- String javaSource = String.format(AUTOFUZZ_REPRODUCER_TEMPLATE, sha, codegenVisitor.generate());
- Path javaPath = Paths.get(reproducerPath, String.format("Crash_%s.java", sha));
- try {
- Files.write(javaPath, javaSource.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
- } catch (IOException e) {
- System.err.printf("ERROR: Failed to write Java reproducer to %s%n", javaPath);
- e.printStackTrace();
- }
- System.out.printf(
- "reproducer_path='%s'; Java reproducer written to %s%n", reproducerPath, javaPath);
- }
-
- private static void fuzzerTestOneInput(
- FuzzedDataProvider data, AutofuzzCodegenVisitor codegenVisitor) throws Throwable {
- Executable targetExecutable;
- if (FuzzTarget.targetExecutables.length == 1) {
- targetExecutable = FuzzTarget.targetExecutables[0];
- } else {
- targetExecutable = data.pickValue(FuzzTarget.targetExecutables);
- }
- Object returnValue = null;
- try {
- if (targetExecutable instanceof Method) {
- returnValue = Meta.autofuzz(data, (Method) targetExecutable, codegenVisitor);
- } else {
- returnValue = Meta.autofuzz(data, (Constructor<?>) targetExecutable, codegenVisitor);
- }
- executionsSinceLastInvocation = 0;
- } catch (AutofuzzConstructionException e) {
- if (Meta.isDebug()) {
- e.printStackTrace();
- }
- // Ignore exceptions thrown while constructing the parameters for the target method. We can
- // only guess how to generate valid parameters and any exceptions thrown while doing so
- // are most likely on us. However, if this happens too often, Autofuzz got stuck and we should
- // let the user know.
- executionsSinceLastInvocation++;
- if (executionsSinceLastInvocation >= MAX_EXECUTIONS_WITHOUT_INVOCATION) {
- System.err.printf("Failed to generate valid arguments to '%s' in %d attempts; giving up%n",
- methodReference, executionsSinceLastInvocation);
- System.exit(1);
- } else if (executionsSinceLastInvocation == MAX_EXECUTIONS_WITHOUT_INVOCATION / 2) {
- // The application under test might perform classpath modifications or create classes
- // dynamically that implement interfaces or extend abstract classes. Rescanning the
- // classpath might help with constructing objects.
- Meta.rescanClasspath();
- }
- } catch (AutofuzzInvocationException e) {
- executionsSinceLastInvocation = 0;
- Throwable cause = e.getCause();
- Class<?> causeClass = cause.getClass();
- // Do not report exceptions declared to be thrown by the method under test.
- for (Class<?> declaredThrow : throwsDeclarations.get(targetExecutable)) {
- if (declaredThrow.isAssignableFrom(causeClass)) {
- return;
- }
- }
-
- if (ignoredExceptionMatchers.stream().anyMatch(m -> m.matches(causeClass.getName()))) {
- return;
- }
- cleanStackTraces(cause);
- throw cause;
- } catch (Throwable t) {
- System.err.println("Unexpected exception encountered during autofuzz");
- t.printStackTrace();
- System.exit(1);
- } finally {
- if (returnValue instanceof Closeable) {
- ((Closeable) returnValue).close();
- }
- }
- }
-
- // Removes all stack trace elements that live in the Java reflection packages or the autofuzz
- // package from the bottom of all stack frames.
- private static void cleanStackTraces(Throwable t) {
- Throwable cause = t;
- while (cause != null) {
- StackTraceElement[] elements = cause.getStackTrace();
- int firstInterestingPos;
- for (firstInterestingPos = elements.length - 1; firstInterestingPos > 0;
- firstInterestingPos--) {
- String className = elements[firstInterestingPos].getClassName();
- if (!className.startsWith("com.code_intelligence.jazzer.autofuzz.")
- && !className.startsWith("java.lang.reflect.")
- && !className.startsWith("jdk.internal.reflect.")) {
- break;
- }
- }
- cause.setStackTrace(Arrays.copyOfRange(elements, 0, firstInterestingPos + 1));
- cause = cause.getCause();
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java
deleted file mode 100644
index 3d48017f..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/Meta.java
+++ /dev/null
@@ -1,716 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.autofuzz;
-
-import com.code_intelligence.jazzer.api.AutofuzzConstructionException;
-import com.code_intelligence.jazzer.api.AutofuzzInvocationException;
-import com.code_intelligence.jazzer.api.Consumer1;
-import com.code_intelligence.jazzer.api.Consumer2;
-import com.code_intelligence.jazzer.api.Consumer3;
-import com.code_intelligence.jazzer.api.Consumer4;
-import com.code_intelligence.jazzer.api.Consumer5;
-import com.code_intelligence.jazzer.api.Function1;
-import com.code_intelligence.jazzer.api.Function2;
-import com.code_intelligence.jazzer.api.Function3;
-import com.code_intelligence.jazzer.api.Function4;
-import com.code_intelligence.jazzer.api.Function5;
-import com.code_intelligence.jazzer.api.FuzzedDataProvider;
-import com.code_intelligence.jazzer.utils.Utils;
-import io.github.classgraph.ClassGraph;
-import io.github.classgraph.ClassInfoList;
-import io.github.classgraph.ScanResult;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Executable;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.lang.reflect.WildcardType;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import net.jodah.typetools.TypeResolver;
-import net.jodah.typetools.TypeResolver.Unknown;
-
-public class Meta {
- static final WeakHashMap<Class<?>, List<Class<?>>> implementingClassesCache = new WeakHashMap<>();
- static final WeakHashMap<Class<?>, List<Class<?>>> nestedBuilderClassesCache =
- new WeakHashMap<>();
- static final WeakHashMap<Class<?>, List<Method>> originalObjectCreationMethodsCache =
- new WeakHashMap<>();
- static final WeakHashMap<Class<?>, List<Method>> cascadingBuilderMethodsCache =
- new WeakHashMap<>();
-
- public static Object autofuzz(FuzzedDataProvider data, Method method) {
- return autofuzz(data, method, null);
- }
-
- static Object autofuzz(FuzzedDataProvider data, Method method, AutofuzzCodegenVisitor visitor) {
- Object result;
- if (Modifier.isStatic(method.getModifiers())) {
- if (visitor != null) {
- // This group will always have two elements: The class name and the method call.
- visitor.pushGroup(
- String.format("%s.", method.getDeclaringClass().getCanonicalName()), "", "");
- }
- try {
- result = autofuzz(data, method, null, visitor);
- } finally {
- if (visitor != null) {
- visitor.popGroup();
- }
- }
- } else {
- if (visitor != null) {
- // This group will always have two elements: The thisObject and the method call.
- // Since the this object can be a complex expression, wrap it in paranthesis.
- visitor.pushGroup("(", ").", "");
- }
- Object thisObject = consume(data, method.getDeclaringClass(), visitor);
- if (thisObject == null) {
- throw new AutofuzzConstructionException();
- }
- try {
- result = autofuzz(data, method, thisObject, visitor);
- } finally {
- if (visitor != null) {
- visitor.popGroup();
- }
- }
- }
- return result;
- }
-
- public static Object autofuzz(FuzzedDataProvider data, Method method, Object thisObject) {
- return autofuzz(data, method, thisObject, null);
- }
-
- static Object autofuzz(
- FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor) {
- if (visitor != null) {
- visitor.pushGroup(String.format("%s(", method.getName()), ", ", ")");
- }
- Object[] arguments = consumeArguments(data, method, visitor);
- if (visitor != null) {
- visitor.popGroup();
- }
- try {
- return method.invoke(thisObject, arguments);
- } catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) {
- // We should ensure that the arguments fed into the method are always valid.
- throw new AutofuzzError(getDebugSummary(method, thisObject, arguments), e);
- } catch (InvocationTargetException e) {
- throw new AutofuzzInvocationException(e.getCause());
- }
- }
-
- public static <R> R autofuzz(FuzzedDataProvider data, Constructor<R> constructor) {
- return autofuzz(data, constructor, null);
- }
-
- static <R> R autofuzz(
- FuzzedDataProvider data, Constructor<R> constructor, AutofuzzCodegenVisitor visitor) {
- if (visitor != null) {
- // getCanonicalName is correct also for nested classes.
- visitor.pushGroup(
- String.format("new %s(", constructor.getDeclaringClass().getCanonicalName()), ", ", ")");
- }
- Object[] arguments = consumeArguments(data, constructor, visitor);
- if (visitor != null) {
- visitor.popGroup();
- }
- try {
- return constructor.newInstance(arguments);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) {
- // This should never be reached as the logic in consume should prevent us from e.g. calling
- // constructors of abstract classes or private constructors.
- throw new AutofuzzError(getDebugSummary(constructor, null, arguments), e);
- } catch (InvocationTargetException e) {
- throw new AutofuzzInvocationException(e.getCause());
- }
- }
-
- @SuppressWarnings("unchecked")
- public static <T1> void autofuzz(FuzzedDataProvider data, Consumer1<T1> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Consumer1.class, func.getClass());
- func.accept((T1) consumeChecked(data, types, 0));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2> void autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Consumer2.class, func.getClass());
- func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3> void autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Consumer3.class, func.getClass());
- func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
- (T3) consumeChecked(data, types, 2));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, T4> void autofuzz(
- FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Consumer4.class, func.getClass());
- func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
- (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, T4, T5> void autofuzz(
- FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Consumer5.class, func.getClass());
- func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
- (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3),
- (T5) consumeChecked(data, types, 4));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, R> R autofuzz(FuzzedDataProvider data, Function1<T1, R> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Function1.class, func.getClass());
- return func.apply((T1) consumeChecked(data, types, 0));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, R> R autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Function2.class, func.getClass());
- return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, R> R autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Function3.class, func.getClass());
- return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
- (T3) consumeChecked(data, types, 2));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, T4, R> R autofuzz(
- FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Function4.class, func.getClass());
- return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
- (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3));
- }
-
- @SuppressWarnings("unchecked")
- public static <T1, T2, T3, T4, T5, R> R autofuzz(
- FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func) {
- Class<?>[] types = TypeResolver.resolveRawArguments(Function5.class, func.getClass());
- return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
- (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3),
- (T5) consumeChecked(data, types, 4));
- }
-
- public static Object consume(FuzzedDataProvider data, Class<?> type) {
- return consume(data, type, null);
- }
-
- // Invariant: The Java source code representation of the returned object visited by visitor must
- // represent an object of the same type as genericType. For example, a null value returned for
- // the genericType Class<java.lang.String> should lead to the generated code
- // "(java.lang.String) null", not just "null". This makes it possible to safely use consume in
- // recursive argument constructions.
- static Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor visitor) {
- Class<?> type = getRawType(genericType);
- if (type == byte.class || type == Byte.class) {
- byte result = data.consumeByte();
- if (visitor != null)
- visitor.pushElement(String.format("(byte) %s", result));
- return result;
- } else if (type == short.class || type == Short.class) {
- short result = data.consumeShort();
- if (visitor != null)
- visitor.pushElement(String.format("(short) %s", result));
- return result;
- } else if (type == int.class || type == Integer.class) {
- int result = data.consumeInt();
- if (visitor != null)
- visitor.pushElement(Integer.toString(result));
- return result;
- } else if (type == long.class || type == Long.class) {
- long result = data.consumeLong();
- if (visitor != null)
- visitor.pushElement(String.format("%sL", result));
- return result;
- } else if (type == float.class || type == Float.class) {
- float result = data.consumeFloat();
- if (visitor != null)
- visitor.pushElement(String.format("%sF", result));
- return result;
- } else if (type == double.class || type == Double.class) {
- double result = data.consumeDouble();
- if (visitor != null)
- visitor.pushElement(Double.toString(result));
- return result;
- } else if (type == boolean.class || type == Boolean.class) {
- boolean result = data.consumeBoolean();
- if (visitor != null)
- visitor.pushElement(Boolean.toString(result));
- return result;
- } else if (type == char.class || type == Character.class) {
- char result = data.consumeChar();
- if (visitor != null)
- visitor.addCharLiteral(result);
- return result;
- }
- // Sometimes, but rarely return null for non-primitive and non-boxed types.
- // TODO: We might want to return null for boxed types sometimes, but this is complicated by the
- // fact that TypeUtils can't distinguish between a primitive type and its wrapper and may
- // thus easily cause false-positive NullPointerExceptions.
- if (!type.isPrimitive() && data.consumeByte() == 0) {
- if (visitor != null) {
- if (type == Object.class) {
- visitor.pushElement("null");
- } else {
- visitor.pushElement(String.format("(%s) null", type.getCanonicalName()));
- }
- }
- return null;
- }
- if (type == String.class || type == CharSequence.class) {
- String result = data.consumeString(consumeArrayLength(data, 1));
- if (visitor != null)
- visitor.addStringLiteral(result);
- return result;
- } else if (type.isArray()) {
- if (type == byte[].class) {
- byte[] result = data.consumeBytes(consumeArrayLength(data, Byte.BYTES));
- if (visitor != null) {
- visitor.pushElement(IntStream.range(0, result.length)
- .mapToObj(i -> "(byte) " + result[i])
- .collect(Collectors.joining(", ", "new byte[]{", "}")));
- }
- return result;
- } else if (type == int[].class) {
- int[] result = data.consumeInts(consumeArrayLength(data, Integer.BYTES));
- if (visitor != null) {
- visitor.pushElement(Arrays.stream(result)
- .mapToObj(String::valueOf)
- .collect(Collectors.joining(", ", "new int[]{", "}")));
- }
- return result;
- } else if (type == short[].class) {
- short[] result = data.consumeShorts(consumeArrayLength(data, Short.BYTES));
- if (visitor != null) {
- visitor.pushElement(IntStream.range(0, result.length)
- .mapToObj(i -> "(short) " + result[i])
- .collect(Collectors.joining(", ", "new short[]{", "}")));
- }
- return result;
- } else if (type == long[].class) {
- long[] result = data.consumeLongs(consumeArrayLength(data, Long.BYTES));
- if (visitor != null) {
- visitor.pushElement(Arrays.stream(result)
- .mapToObj(e -> e + "L")
- .collect(Collectors.joining(", ", "new long[]{", "}")));
- }
- return result;
- } else if (type == boolean[].class) {
- boolean[] result = data.consumeBooleans(consumeArrayLength(data, 1));
- if (visitor != null) {
- visitor.pushElement(
- Arrays.toString(result).replace(']', '}').replace("[", "new boolean[]{"));
- }
- return result;
- } else {
- if (visitor != null) {
- visitor.pushGroup(
- String.format("new %s[]{", type.getComponentType().getName()), ", ", "}");
- }
- int remainingBytesBeforeFirstElementCreation = data.remainingBytes();
- Object firstElement = consume(data, type.getComponentType(), visitor);
- int remainingBytesAfterFirstElementCreation = data.remainingBytes();
- int sizeOfElementEstimate =
- remainingBytesBeforeFirstElementCreation - remainingBytesAfterFirstElementCreation;
- Object array = Array.newInstance(
- type.getComponentType(), consumeArrayLength(data, sizeOfElementEstimate));
- for (int i = 0; i < Array.getLength(array); i++) {
- if (i == 0) {
- Array.set(array, i, firstElement);
- } else {
- Array.set(array, i, consume(data, type.getComponentType(), visitor));
- }
- }
- if (visitor != null) {
- if (Array.getLength(array) == 0) {
- // We implicitly pushed the first element with the call to consume above, but it is not
- // part of the array.
- visitor.popElement();
- }
- visitor.popGroup();
- }
- return array;
- }
- } else if (type == ByteArrayInputStream.class || type == InputStream.class) {
- byte[] array = data.consumeBytes(consumeArrayLength(data, Byte.BYTES));
- if (visitor != null) {
- visitor.pushElement(IntStream.range(0, array.length)
- .mapToObj(i -> "(byte) " + array[i])
- .collect(Collectors.joining(
- ", ", "new java.io.ByteArrayInputStream(new byte[]{", "})")));
- }
- return new ByteArrayInputStream(array);
- } else if (type == Map.class) {
- ParameterizedType mapType = (ParameterizedType) genericType;
- if (mapType.getActualTypeArguments().length != 2) {
- throw new AutofuzzError(
- "Expected Map generic type to have two type parameters: " + mapType);
- }
- Type keyType = mapType.getActualTypeArguments()[0];
- Type valueType = mapType.getActualTypeArguments()[1];
- if (visitor != null) {
- // Do not use Collectors.toMap() since it cannot handle null values.
- // Also annotate the type of the entry stream since it might be empty, in which case type
- // inference on the accumulator could fail.
- visitor.pushGroup(
- String.format("java.util.stream.Stream.<java.util.AbstractMap.SimpleEntry<%s, %s>>of(",
- keyType.getTypeName(), valueType.getTypeName()),
- ", ",
- ").collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll)");
- }
- int remainingBytesBeforeFirstEntryCreation = data.remainingBytes();
- if (visitor != null) {
- visitor.pushGroup("new java.util.AbstractMap.SimpleEntry<>(", ", ", ")");
- }
- Object firstKey = consume(data, keyType, visitor);
- Object firstValue = consume(data, valueType, visitor);
- if (visitor != null) {
- visitor.popGroup();
- }
- int remainingBytesAfterFirstEntryCreation = data.remainingBytes();
- int sizeOfElementEstimate =
- remainingBytesBeforeFirstEntryCreation - remainingBytesAfterFirstEntryCreation;
- int mapSize = consumeArrayLength(data, sizeOfElementEstimate);
- Map<Object, Object> map = new HashMap<>(mapSize);
- for (int i = 0; i < mapSize; i++) {
- if (i == 0) {
- map.put(firstKey, firstValue);
- } else {
- if (visitor != null) {
- visitor.pushGroup("new java.util.AbstractMap.SimpleEntry<>(", ", ", ")");
- }
- map.put(consume(data, keyType, visitor), consume(data, valueType, visitor));
- if (visitor != null) {
- visitor.popGroup();
- }
- }
- }
- if (visitor != null) {
- if (mapSize == 0) {
- // We implicitly pushed the first entry with the call to consume above, but it is not
- // part of the array.
- visitor.popElement();
- }
- visitor.popGroup();
- }
- return map;
- } else if (type.isEnum()) {
- Enum<?> enumValue = (Enum<?>) data.pickValue(type.getEnumConstants());
- if (visitor != null) {
- visitor.pushElement(String.format("%s.%s", type.getName(), enumValue.name()));
- }
- return enumValue;
- } else if (type == Class.class) {
- if (visitor != null)
- visitor.pushElement(String.format("%s.class", YourAverageJavaClass.class.getName()));
- return YourAverageJavaClass.class;
- } else if (type == Method.class) {
- if (visitor != null) {
- throw new AutofuzzError("codegen has not been implemented for Method.class");
- }
- return data.pickValue(sortExecutables(YourAverageJavaClass.class.getMethods()));
- } else if (type == Constructor.class) {
- if (visitor != null) {
- throw new AutofuzzError("codegen has not been implemented for Constructor.class");
- }
- return data.pickValue(sortExecutables(YourAverageJavaClass.class.getConstructors()));
- } else if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) {
- List<Class<?>> implementingClasses = implementingClassesCache.get(type);
- if (implementingClasses == null) {
- ClassGraph classGraph =
- new ClassGraph().enableClassInfo().enableInterClassDependencies().rejectPackages(
- "jaz.*");
- if (!isTest()) {
- classGraph.rejectPackages("com.code_intelligence.jazzer.*");
- }
- try (ScanResult result = classGraph.scan()) {
- ClassInfoList children =
- type.isInterface() ? result.getClassesImplementing(type) : result.getSubclasses(type);
- implementingClasses =
- children.getStandardClasses().filter(cls -> !cls.isAbstract()).loadClasses();
- implementingClassesCache.put(type, implementingClasses);
- }
- }
- if (implementingClasses.isEmpty()) {
- if (isDebug()) {
- throw new AutofuzzConstructionException(String.format(
- "Could not find classes implementing %s on the classpath", type.getName()));
- } else {
- throw new AutofuzzConstructionException();
- }
- }
- if (visitor != null) {
- // This group will always have a single element: The instance of the implementing class.
- visitor.pushGroup(String.format("(%s) ", type.getName()), "", "");
- }
- Object result = consume(data, data.pickValue(implementingClasses), visitor);
- if (visitor != null) {
- visitor.popGroup();
- }
- return result;
- } else if (type.getConstructors().length > 0) {
- Constructor<?> constructor = data.pickValue(sortExecutables(type.getConstructors()));
- boolean applySetters = constructor.getParameterCount() == 0;
- if (visitor != null && applySetters) {
- // Embed the instance creation and setters into an immediately invoked lambda expression to
- // turn them into an expression.
- String uniqueVariableName = visitor.uniqueVariableName();
- visitor.pushGroup(String.format("((java.util.function.Supplier<%1$s>) (() -> {%1$s %2$s = ",
- type.getCanonicalName(), uniqueVariableName),
- String.format("; %s.", uniqueVariableName),
- String.format("; return %s;})).get()", uniqueVariableName));
- }
- Object obj = autofuzz(data, constructor, visitor);
- if (applySetters) {
- List<Method> potentialSetters = getPotentialSetters(type);
- if (!potentialSetters.isEmpty()) {
- List<Method> pickedSetters =
- data.pickValues(potentialSetters, data.consumeInt(0, potentialSetters.size()));
- for (Method setter : pickedSetters) {
- autofuzz(data, setter, obj, visitor);
- }
- }
- if (visitor != null) {
- visitor.popGroup();
- }
- }
- return obj;
- }
- // We are out of more or less canonical ways to construct an instance of this class and have to
- // resort to more heuristic approaches.
-
- // First, try to find nested classes with names ending in Builder and call a subset of their
- // chaining methods.
- List<Class<?>> nestedBuilderClasses = getNestedBuilderClasses(type);
- if (!nestedBuilderClasses.isEmpty()) {
- Class<?> pickedBuilder = data.pickValue(nestedBuilderClasses);
- List<Method> cascadingBuilderMethods = getCascadingBuilderMethods(pickedBuilder);
- List<Method> originalObjectCreationMethods = getOriginalObjectCreationMethods(pickedBuilder);
-
- int pickedMethodsNumber = data.consumeInt(0, cascadingBuilderMethods.size());
- List<Method> pickedMethods = data.pickValues(cascadingBuilderMethods, pickedMethodsNumber);
- Method builderMethod = data.pickValue(originalObjectCreationMethods);
-
- if (visitor != null) {
- // Group for the chain of builder methods.
- visitor.pushGroup("", ".", "");
- }
- Object builderObj =
- autofuzz(data, data.pickValue(sortExecutables(pickedBuilder.getConstructors())), visitor);
- for (Method method : pickedMethods) {
- builderObj = autofuzz(data, method, builderObj, visitor);
- }
-
- try {
- Object obj = autofuzz(data, builderMethod, builderObj, visitor);
- if (visitor != null) {
- visitor.popGroup();
- }
- return obj;
- } catch (Exception e) {
- throw new AutofuzzConstructionException(e);
- }
- }
-
- // We ran out of ways to construct an instance of the requested type. If in debug mode, report
- // more detailed information.
- if (!isDebug()) {
- throw new AutofuzzConstructionException();
- } else {
- String summary = String.format(
- "Failed to generate instance of %s:%nAccessible constructors: %s%nNested subclasses: %s%n",
- type.getName(),
- Arrays.stream(type.getConstructors())
- .map(Utils::getReadableDescriptor)
- .collect(Collectors.joining(", ")),
- Arrays.stream(type.getClasses()).map(Class::getName).collect(Collectors.joining(", ")));
- throw new AutofuzzConstructionException(summary);
- }
- }
-
- static void rescanClasspath() {
- implementingClassesCache.clear();
- }
-
- static boolean isTest() {
- String value = System.getenv("JAZZER_AUTOFUZZ_TESTING");
- return value != null && !value.isEmpty();
- }
-
- static boolean isDebug() {
- String value = System.getenv("JAZZER_AUTOFUZZ_DEBUG");
- return value != null && !value.isEmpty();
- }
-
- private static int consumeArrayLength(FuzzedDataProvider data, int sizeOfElement) {
- // Spend at most half of the fuzzer input bytes so that the remaining arguments that require
- // construction still have non-trivial data to work with.
- int bytesToSpend = data.remainingBytes() / 2;
- return bytesToSpend / Math.max(sizeOfElement, 1);
- }
-
- private static String getDebugSummary(
- Executable executable, Object thisObject, Object[] arguments) {
- return String.format("%nMethod: %s::%s%s%nthis: %s%nArguments: %s",
- executable.getDeclaringClass().getName(), executable.getName(),
- Utils.getReadableDescriptor(executable), thisObject,
- Arrays.stream(arguments)
- .map(arg -> arg == null ? "null" : arg.toString())
- .collect(Collectors.joining(", ")));
- }
-
- private static <T extends Executable> List<T> sortExecutables(T[] executables) {
- List<T> list = Arrays.asList(executables);
- sortExecutables(list);
- return list;
- }
-
- private static void sortExecutables(List<? extends Executable> executables) {
- executables.sort(Comparator.comparing(Executable::getName).thenComparing(Utils::getDescriptor));
- }
-
- private static void sortClasses(List<? extends Class<?>> classes) {
- classes.sort(Comparator.comparing(Class::getName));
- }
-
- private static List<Class<?>> getNestedBuilderClasses(Class<?> type) {
- List<Class<?>> nestedBuilderClasses = nestedBuilderClassesCache.get(type);
- if (nestedBuilderClasses == null) {
- nestedBuilderClasses = Arrays.stream(type.getClasses())
- .filter(cls -> cls.getName().endsWith("Builder"))
- .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty())
- .collect(Collectors.toList());
- sortClasses(nestedBuilderClasses);
- nestedBuilderClassesCache.put(type, nestedBuilderClasses);
- }
- return nestedBuilderClasses;
- }
-
- private static List<Method> getOriginalObjectCreationMethods(Class<?> builder) {
- List<Method> originalObjectCreationMethods = originalObjectCreationMethodsCache.get(builder);
- if (originalObjectCreationMethods == null) {
- originalObjectCreationMethods =
- Arrays.stream(builder.getMethods())
- .filter(m -> m.getReturnType() == builder.getEnclosingClass())
- .collect(Collectors.toList());
- sortExecutables(originalObjectCreationMethods);
- originalObjectCreationMethodsCache.put(builder, originalObjectCreationMethods);
- }
- return originalObjectCreationMethods;
- }
-
- private static List<Method> getCascadingBuilderMethods(Class<?> builder) {
- List<Method> cascadingBuilderMethods = cascadingBuilderMethodsCache.get(builder);
- if (cascadingBuilderMethods == null) {
- cascadingBuilderMethods = Arrays.stream(builder.getMethods())
- .filter(m -> m.getReturnType() == builder)
- .collect(Collectors.toList());
- sortExecutables(cascadingBuilderMethods);
- cascadingBuilderMethodsCache.put(builder, cascadingBuilderMethods);
- }
- return cascadingBuilderMethods;
- }
-
- private static List<Method> getPotentialSetters(Class<?> type) {
- List<Method> potentialSetters = new ArrayList<>();
- Method[] methods = type.getMethods();
- for (Method method : methods) {
- if (void.class.equals(method.getReturnType()) && method.getParameterCount() == 1
- && method.getName().startsWith("set")) {
- potentialSetters.add(method);
- }
- }
- sortExecutables(potentialSetters);
- return potentialSetters;
- }
-
- private static Object[] consumeArguments(
- FuzzedDataProvider data, Executable executable, AutofuzzCodegenVisitor visitor) {
- Object[] result;
- try {
- result = Arrays.stream(executable.getGenericParameterTypes())
- .map((type) -> consume(data, type, visitor))
- .toArray();
- return result;
- } catch (AutofuzzConstructionException e) {
- // Do not nest AutofuzzConstructionExceptions.
- throw e;
- } catch (AutofuzzInvocationException e) {
- // If an invocation fails while creating the arguments for another invocation, the exception
- // should not be reported, so we rewrap it.
- throw new AutofuzzConstructionException(e.getCause());
- } catch (Throwable t) {
- throw new AutofuzzConstructionException(t);
- }
- }
-
- private static Object consumeChecked(FuzzedDataProvider data, Class<?>[] types, int i) {
- if (types[i] == Unknown.class) {
- throw new AutofuzzError("Failed to determine type of argument " + (i + 1));
- }
- Object result;
- try {
- result = consume(data, types[i]);
- } catch (AutofuzzConstructionException e) {
- // Do not nest AutofuzzConstructionExceptions.
- throw e;
- } catch (AutofuzzInvocationException e) {
- // If an invocation fails while creating the arguments for another invocation, the exception
- // should not be reported, so we rewrap it.
- throw new AutofuzzConstructionException(e.getCause());
- } catch (Throwable t) {
- throw new AutofuzzConstructionException(t);
- }
- if (result != null && !types[i].isAssignableFrom(result.getClass())) {
- throw new AutofuzzError("consume returned " + result.getClass() + ", but need " + types[i]);
- }
- return result;
- }
-
- private static Class<?> getRawType(Type genericType) {
- if (genericType instanceof Class<?>) {
- return (Class<?>) genericType;
- } else if (genericType instanceof ParameterizedType) {
- return getRawType(((ParameterizedType) genericType).getRawType());
- } else if (genericType instanceof WildcardType) {
- // TODO: Improve this.
- return Object.class;
- } else if (genericType instanceof TypeVariable<?>) {
- throw new AutofuzzError("Did not expect genericType to be a TypeVariable: " + genericType);
- } else if (genericType instanceof GenericArrayType) {
- // TODO: Improve this;
- return Object[].class;
- } else {
- throw new AutofuzzError("Got unexpected class implementing Type: " + genericType);
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java b/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java
deleted file mode 100644
index 452ca878..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/autofuzz/YourAverageJavaClass.java
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.autofuzz;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-// Returned by Meta when asked to construct a Class object. Its purpose is to be a relatively
-// "interesting" Java data class that can serve as the target of methods that perform some kind of
-// reflection or deserialization.
-public class YourAverageJavaClass implements Cloneable, Closeable, Serializable {
- public byte aByte;
- public boolean aBoolean;
- public double aDouble;
- public float aFloat;
- public int anInt;
- public transient int transientInt;
- public long aLong;
- public short aShort;
- public volatile short volatileShort;
- public String string;
- public byte[] bytes;
- public List<YourAverageJavaClass> list;
- public Map<String, YourAverageJavaClass> map;
-
- // Everything below has been automatically generated (apart from a minor modification to clone());
-
- public YourAverageJavaClass(byte aByte, boolean aBoolean, double aDouble, float aFloat, int anInt,
- int transientInt, long aLong, short aShort, short volatileShort, String string) {
- this.aByte = aByte;
- this.aBoolean = aBoolean;
- this.aDouble = aDouble;
- this.aFloat = aFloat;
- this.anInt = anInt;
- this.transientInt = transientInt;
- this.aLong = aLong;
- this.aShort = aShort;
- this.volatileShort = volatileShort;
- this.string = string;
- }
-
- public YourAverageJavaClass() {}
-
- public YourAverageJavaClass(byte aByte, boolean aBoolean, double aDouble, float aFloat, int anInt,
- int transientInt, long aLong, short aShort, short volatileShort, String string, byte[] bytes,
- List<YourAverageJavaClass> list, Map<String, YourAverageJavaClass> map) {
- this.aByte = aByte;
- this.aBoolean = aBoolean;
- this.aDouble = aDouble;
- this.aFloat = aFloat;
- this.anInt = anInt;
- this.transientInt = transientInt;
- this.aLong = aLong;
- this.aShort = aShort;
- this.volatileShort = volatileShort;
- this.string = string;
- this.bytes = bytes;
- this.list = list;
- this.map = map;
- }
-
- public byte getaByte() {
- return aByte;
- }
-
- public void setaByte(byte aByte) {
- this.aByte = aByte;
- }
-
- public boolean isaBoolean() {
- return aBoolean;
- }
-
- public void setaBoolean(boolean aBoolean) {
- this.aBoolean = aBoolean;
- }
-
- public double getaDouble() {
- return aDouble;
- }
-
- public void setaDouble(double aDouble) {
- this.aDouble = aDouble;
- }
-
- public float getaFloat() {
- return aFloat;
- }
-
- public void setaFloat(float aFloat) {
- this.aFloat = aFloat;
- }
-
- public int getAnInt() {
- return anInt;
- }
-
- public void setAnInt(int anInt) {
- this.anInt = anInt;
- }
-
- public int getTransientInt() {
- return transientInt;
- }
-
- public void setTransientInt(int transientInt) {
- this.transientInt = transientInt;
- }
-
- public long getaLong() {
- return aLong;
- }
-
- public void setaLong(long aLong) {
- this.aLong = aLong;
- }
-
- public short getaShort() {
- return aShort;
- }
-
- public void setaShort(short aShort) {
- this.aShort = aShort;
- }
-
- public short getVolatileShort() {
- return volatileShort;
- }
-
- public void setVolatileShort(short volatileShort) {
- this.volatileShort = volatileShort;
- }
-
- public String getString() {
- return string;
- }
-
- public void setString(String string) {
- this.string = string;
- }
-
- public byte[] getBytes() {
- return bytes;
- }
-
- public void setBytes(byte[] bytes) {
- this.bytes = bytes;
- }
-
- public List<YourAverageJavaClass> getList() {
- return list;
- }
-
- public void setList(List<YourAverageJavaClass> list) {
- this.list = list;
- }
-
- public Map<String, YourAverageJavaClass> getMap() {
- return map;
- }
-
- public void setMap(Map<String, YourAverageJavaClass> map) {
- this.map = map;
- }
-
- @Override
- public YourAverageJavaClass clone() {
- try {
- YourAverageJavaClass clone = (YourAverageJavaClass) super.clone();
- clone.transientInt = transientInt + 1;
- clone.volatileShort = (short) (volatileShort - 1);
- return clone;
- } catch (CloneNotSupportedException e) {
- throw new AssertionError();
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (!(o instanceof YourAverageJavaClass))
- return false;
- YourAverageJavaClass that = (YourAverageJavaClass) o;
- return aByte == that.aByte && aBoolean == that.aBoolean
- && Double.compare(that.aDouble, aDouble) == 0 && Float.compare(that.aFloat, aFloat) == 0
- && anInt == that.anInt && transientInt == that.transientInt && aLong == that.aLong
- && aShort == that.aShort && volatileShort == that.volatileShort
- && Objects.equals(string, that.string) && Arrays.equals(bytes, that.bytes)
- && Objects.equals(list, that.list) && Objects.equals(map, that.map);
- }
-
- @Override
- public int hashCode() {
- int result = Objects.hash(aByte, aBoolean, aDouble, aFloat, anInt, transientInt, aLong, aShort,
- volatileShort, string, list, map);
- result = 31 * result + Arrays.hashCode(bytes);
- return result;
- }
-
- @Override
- public String toString() {
- return "YourAverageJavaClass{"
- + "aByte=" + aByte + ", aBoolean=" + aBoolean + ", aDouble=" + aDouble + ", aFloat="
- + aFloat + ", anInt=" + anInt + ", transientInt=" + transientInt + ", aLong=" + aLong
- + ", aShort=" + aShort + ", volatileShort=" + volatileShort + ", string='" + string + '\''
- + ", bytes=" + Arrays.toString(bytes) + ", list=" + list + ", map=" + map + '}';
- }
-
- @Override
- public void close() throws IOException {}
-}
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
deleted file mode 100644
index db93dcae..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/BUILD.bazel
+++ /dev/null
@@ -1,35 +0,0 @@
-load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
-load("@com_github_johnynek_bazel_jar_jar//:jar_jar.bzl", "jar_jar")
-
-kt_jvm_library(
- name = "instrumentor",
- srcs = [
- "ClassInstrumentor.kt",
- "CoverageRecorder.kt",
- "DescriptorUtils.kt",
- "DeterministicRandom.kt",
- "EdgeCoverageInstrumentor.kt",
- "Hook.kt",
- "HookInstrumentor.kt",
- "HookMethodVisitor.kt",
- "Hooks.kt",
- "Instrumentor.kt",
- "StaticMethodStrategy.java",
- "TraceDataFlowInstrumentor.kt",
- ],
- visibility = [
- "//agent/src/jmh/java/com/code_intelligence/jazzer/instrumentor:__pkg__",
- "//agent/src/main/java/com/code_intelligence/jazzer/agent:__pkg__",
- "//agent/src/test/java/com/code_intelligence/jazzer/instrumentor:__pkg__",
- "//driver/src/main/java/com/code_intelligence/jazzer/driver:__pkg__",
- ],
- deps = [
- "//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",
- "@jazzer_jacoco//:jacoco_internal",
- "@org_ow2_asm_asm//jar",
- "@org_ow2_asm_asm_commons//jar",
- ],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/ClassInstrumentor.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/ClassInstrumentor.kt
deleted file mode 100644
index 4c3eabcb..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/ClassInstrumentor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.runtime.CoverageMap
-
-fun extractClassFileMajorVersion(classfileBuffer: ByteArray): Int {
- return ((classfileBuffer[6].toInt() and 0xff) shl 8) or (classfileBuffer[7].toInt() and 0xff)
-}
-
-class ClassInstrumentor constructor(bytecode: ByteArray) {
-
- var instrumentedBytecode = bytecode
- private set
-
- fun coverage(initialEdgeId: Int): Int {
- val edgeCoverageInstrumentor = EdgeCoverageInstrumentor(
- defaultEdgeCoverageStrategy,
- defaultCoverageMap,
- initialEdgeId,
- )
- instrumentedBytecode = edgeCoverageInstrumentor.instrument(instrumentedBytecode)
- return edgeCoverageInstrumentor.numEdges
- }
-
- fun traceDataFlow(instrumentations: Set<InstrumentationType>) {
- instrumentedBytecode = TraceDataFlowInstrumentor(instrumentations).instrument(instrumentedBytecode)
- }
-
- fun hooks(hooks: Iterable<Hook>) {
- instrumentedBytecode = HookInstrumentor(
- hooks,
- java6Mode = extractClassFileMajorVersion(instrumentedBytecode) < 51
- ).instrument(instrumentedBytecode)
- }
-
- companion object {
- val defaultEdgeCoverageStrategy = StaticMethodStrategy()
- val defaultCoverageMap = CoverageMap::class.java
- }
-}
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
deleted file mode 100644
index 098cf389..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/CoverageRecorder.kt
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.runtime.CoverageMap
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.analysis.CoverageBuilder
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionData
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionDataStore
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionDataWriter
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.SessionInfo
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.data.CRC64
-import com.code_intelligence.jazzer.utils.ClassNameGlobber
-import io.github.classgraph.ClassGraph
-import java.io.File
-import java.io.FileOutputStream
-import java.io.OutputStream
-import java.time.Instant
-import java.util.UUID
-
-private data class InstrumentedClassInfo(
- val classId: Long,
- val initialEdgeId: Int,
- val nextEdgeId: Int,
- 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>()
-
- fun recordInstrumentedClass(internalClassName: String, bytecode: ByteArray, firstId: Int, numIds: Int) {
- if (startTimestamp == null)
- startTimestamp = Instant.now()
- instrumentedClassInfo[internalClassName] = InstrumentedClassInfo(
- CRC64.classId(bytecode), firstId, firstId + numIds, bytecode
- )
- }
-
- /**
- * Manually records coverage IDs based on the current state of [CoverageMap].
- * Should be called after static initializers have run.
- */
- @JvmStatic
- fun updateCoveredIdsWithCoverageMap() {
- additionalCoverage.addAll(CoverageMap.getCoveredIds())
- }
-
- /**
- * [dumpCoverageReport] dumps a human-readable coverage report of files using any [coveredIds] to [dumpFileName].
- */
- @JvmStatic
- fun dumpCoverageReport(coveredIds: IntArray, dumpFileName: String) {
- File(dumpFileName).bufferedWriter().use { writer ->
- writer.write(computeFileCoverage(coveredIds))
- }
- }
-
- private fun computeFileCoverage(coveredIds: IntArray): String {
- fun Double.format(digits: Int) = "%.${digits}f".format(this)
- val coverage = analyzeCoverage(coveredIds.toSet()) ?: return "No classes were instrumented"
- return coverage.sourceFiles.joinToString(
- "\n",
- prefix = "Branch coverage:\n",
- postfix = "\n\n"
- ) { fileCoverage ->
- val counter = fileCoverage.branchCounter
- val percentage = 100 * counter.coveredRatio
- "${fileCoverage.name}: ${counter.coveredCount}/${counter.totalCount} (${percentage.format(2)}%)"
- } + coverage.sourceFiles.joinToString(
- "\n",
- prefix = "Line coverage:\n",
- postfix = "\n\n"
- ) { fileCoverage ->
- val counter = fileCoverage.lineCounter
- val percentage = 100 * counter.coveredRatio
- "${fileCoverage.name}: ${counter.coveredCount}/${counter.totalCount} (${percentage.format(2)}%)"
- } + coverage.sourceFiles.joinToString(
- "\n",
- prefix = "Incompletely covered lines:\n",
- postfix = "\n\n"
- ) { fileCoverage ->
- "${fileCoverage.name}: " + (fileCoverage.firstLine..fileCoverage.lastLine).filter {
- val instructions = fileCoverage.getLine(it).instructionCounter
- instructions.coveredCount in 1 until instructions.totalCount
- }.toString()
- } + coverage.sourceFiles.joinToString(
- "\n",
- prefix = "Missed lines:\n",
- ) { fileCoverage ->
- "${fileCoverage.name}: " + (fileCoverage.firstLine..fileCoverage.lastLine).filter {
- val instructions = fileCoverage.getLine(it).instructionCounter
- instructions.coveredCount == 0 && instructions.totalCount > 0
- }.toString()
- }
- }
-
- /**
- * [dumpJacocoCoverage] dumps the JaCoCo coverage of files using any [coveredIds] to [dumpFileName].
- * JaCoCo only exports coverage for files containing at least one coverage data point. The dump
- * can be used by the JaCoCo report command to create reports also including not covered files.
- */
- @JvmStatic
- fun dumpJacocoCoverage(coveredIds: IntArray, dumpFileName: String) {
- FileOutputStream(dumpFileName).use { outStream ->
- dumpJacocoCoverage(coveredIds, outStream)
- }
- }
-
- /**
- * [dumpJacocoCoverage] dumps the JaCoCo coverage of files using any [coveredIds] to [outStream].
- */
- @JvmStatic
- fun dumpJacocoCoverage(coveredIds: IntArray, outStream: OutputStream) {
- // Return if no class has been instrumented.
- val startTimestamp = startTimestamp ?: return
-
- // Update the list of covered IDs with the coverage information for the current run.
- updateCoveredIdsWithCoverageMap()
-
- val dumpTimestamp = Instant.now()
- val outWriter = ExecutionDataWriter(outStream)
- outWriter.visitSessionInfo(
- SessionInfo(UUID.randomUUID().toString(), startTimestamp.epochSecond, dumpTimestamp.epochSecond)
- )
- analyzeJacocoCoverage(coveredIds.toSet()).accept(outWriter)
- }
-
- /**
- * Build up a JaCoCo [ExecutionDataStore] based on [coveredIds] containing the internally gathered coverage information.
- */
- private fun analyzeJacocoCoverage(coveredIds: Set<Int>): ExecutionDataStore {
- val executionDataStore = ExecutionDataStore()
- val sortedCoveredIds = (additionalCoverage + coveredIds).sorted().toIntArray()
- for ((internalClassName, info) in instrumentedClassInfo) {
- // Determine the subarray of coverage IDs in sortedCoveredIds that contains the IDs generated while
- // instrumenting the current class. Since the ID array is sorted, use binary search.
- var coveredIdsStart = sortedCoveredIds.binarySearch(info.initialEdgeId)
- if (coveredIdsStart < 0) {
- coveredIdsStart = -(coveredIdsStart + 1)
- }
- var coveredIdsEnd = sortedCoveredIds.binarySearch(info.nextEdgeId)
- if (coveredIdsEnd < 0) {
- coveredIdsEnd = -(coveredIdsEnd + 1)
- }
- if (coveredIdsStart == coveredIdsEnd) {
- // No coverage data for the class.
- continue
- }
- check(coveredIdsStart in 0 until coveredIdsEnd && coveredIdsEnd <= sortedCoveredIds.size) {
- "Invalid range [$coveredIdsStart, $coveredIdsEnd) with coveredIds.size=${sortedCoveredIds.size}"
- }
- // Generate a probes array for the current class only, i.e., mapping info.initialEdgeId to 0.
- val probes = BooleanArray(info.nextEdgeId - info.initialEdgeId)
- (coveredIdsStart until coveredIdsEnd).asSequence()
- .map {
- val globalEdgeId = sortedCoveredIds[it]
- globalEdgeId - info.initialEdgeId
- }
- .forEach { classLocalEdgeId ->
- probes[classLocalEdgeId] = true
- }
- executionDataStore.visitClassExecution(ExecutionData(info.classId, internalClassName, probes))
- }
- return executionDataStore
- }
-
- /**
- * Create a [CoverageBuilder] containing all classes matching the include/exclude pattern and their coverage statistics.
- */
- fun analyzeCoverage(coveredIds: Set<Int>): CoverageBuilder? {
- return try {
- val coverage = CoverageBuilder()
- analyzeAllUncoveredClasses(coverage)
- val executionDataStore = analyzeJacocoCoverage(coveredIds)
- for ((internalClassName, info) in instrumentedClassInfo) {
- EdgeCoverageInstrumentor(ClassInstrumentor.defaultEdgeCoverageStrategy, ClassInstrumentor.defaultCoverageMap, 0)
- .analyze(
- executionDataStore,
- coverage,
- info.bytecode,
- internalClassName
- )
- }
- coverage
- } catch (e: Exception) {
- e.printStackTrace()
- 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()
- 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 ->
- // ExecutionDataStore is used to look up existing coverage during analysis of the class files,
- // no entries are added during that. Passing in an empty store is fine for uncovered files.
- val emptyExecutionDataStore = ExecutionDataStore()
- result.allClasses
- .asSequence()
- .filter { classInfo -> classNameGlobber.includes(classInfo.name) }
- .filterNot { classInfo -> classInfo.name in coveredClassNames }
- .forEach { classInfo ->
- classInfo.resource.use { resource ->
- EdgeCoverageInstrumentor(ClassInstrumentor.defaultEdgeCoverageStrategy, ClassInstrumentor.defaultCoverageMap, 0).analyze(
- emptyExecutionDataStore,
- coverage,
- resource.load(),
- classInfo.name.replace('.', '/')
- )
- }
- }
- }
- return coverage
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DescriptorUtils.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DescriptorUtils.kt
deleted file mode 100644
index 80cbe80b..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DescriptorUtils.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-internal fun isPrimitiveType(typeDescriptor: String): Boolean {
- return typeDescriptor in arrayOf("B", "C", "D", "F", "I", "J", "S", "V", "Z")
-}
-
-private fun isPrimitiveType(typeDescriptor: Char) = isPrimitiveType(typeDescriptor.toString())
-
-internal fun getWrapperTypeDescriptor(typeDescriptor: String): String = when (typeDescriptor) {
- "B" -> "Ljava/lang/Byte;"
- "C" -> "Ljava/lang/Character;"
- "D" -> "Ljava/lang/Double;"
- "F" -> "Ljava/lang/Float;"
- "I" -> "Ljava/lang/Integer;"
- "J" -> "Ljava/lang/Long;"
- "S" -> "Ljava/lang/Short;"
- "V" -> "Ljava/lang/Void;"
- "Z" -> "Ljava/lang/Boolean;"
- else -> typeDescriptor
-}
-
-// Removes the 'L' and ';' prefix/suffix from signatures to get the full class name.
-// Note that array signatures '[Ljava/lang/String;' already have the correct form.
-internal fun extractInternalClassName(typeDescriptor: String): String {
- return if (typeDescriptor.startsWith("L") && typeDescriptor.endsWith(";")) {
- typeDescriptor.substring(1, typeDescriptor.length - 1)
- } else {
- typeDescriptor
- }
-}
-
-internal fun extractParameterTypeDescriptors(methodDescriptor: String): List<String> {
- require(methodDescriptor.startsWith('(')) { "Method descriptor must start with '('" }
- val endOfParameterPart = methodDescriptor.indexOf(')') - 1
- require(endOfParameterPart >= 0) { "Method descriptor must contain ')'" }
- var remainingDescriptorList = methodDescriptor.substring(1..endOfParameterPart)
- val parameterDescriptors = mutableListOf<String>()
- while (remainingDescriptorList.isNotEmpty()) {
- val nextDescriptor = extractNextTypeDescriptor(remainingDescriptorList)
- parameterDescriptors.add(nextDescriptor)
- remainingDescriptorList = remainingDescriptorList.removePrefix(nextDescriptor)
- }
- return parameterDescriptors
-}
-
-internal fun extractReturnTypeDescriptor(methodDescriptor: String): String {
- require(methodDescriptor.startsWith('(')) { "Method descriptor must start with '('" }
- val endBracketPos = methodDescriptor.indexOf(')')
- require(endBracketPos >= 0) { "Method descriptor must contain ')'" }
- val startOfReturnValue = endBracketPos + 1
- return extractNextTypeDescriptor(methodDescriptor.substring(startOfReturnValue))
-}
-
-private fun extractNextTypeDescriptor(input: String): String {
- require(input.isNotEmpty()) { "Type descriptor must not be empty" }
- // Skip over arbitrarily many '[' to support multi-dimensional arrays.
- val firstNonArrayPrefixCharPos = input.indexOfFirst { it != '[' }
- require(firstNonArrayPrefixCharPos >= 0) { "Array descriptor must contain type" }
- val firstTypeChar = input[firstNonArrayPrefixCharPos]
- return when {
- // Primitive type
- isPrimitiveType(firstTypeChar) -> {
- input.substring(0..firstNonArrayPrefixCharPos)
- }
- // Object type
- firstTypeChar == 'L' -> {
- val endOfClassNamePos = input.indexOf(';')
- require(endOfClassNamePos > 0) { "Class type indicated by L must end with ;" }
- input.substring(0..endOfClassNamePos)
- }
- // Invalid type
- else -> {
- throw IllegalArgumentException("Invalid type: $firstTypeChar")
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DeterministicRandom.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DeterministicRandom.kt
deleted file mode 100644
index d4210dc4..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/DeterministicRandom.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import java.security.MessageDigest
-import java.security.SecureRandom
-
-// This RNG is resistant to collisions (even under XOR) but fully deterministic.
-internal class DeterministicRandom(vararg contexts: String) {
- private val random = SecureRandom.getInstance("SHA1PRNG").apply {
- val contextHash = MessageDigest.getInstance("SHA-256").run {
- for (context in contexts) {
- update(context.toByteArray())
- }
- digest()
- }
- setSeed(contextHash)
- }
-
- fun nextInt(bound: Int) = random.nextInt(bound)
-
- fun nextInt() = random.nextInt()
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/EdgeCoverageInstrumentor.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/EdgeCoverageInstrumentor.kt
deleted file mode 100644
index 8fb3dc2b..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/EdgeCoverageInstrumentor.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.analysis.Analyzer
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.analysis.ICoverageVisitor
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.data.ExecutionDataStore
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.flow.ClassProbesAdapter
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.flow.ClassProbesVisitor
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.flow.IClassProbesAdapterFactory
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.instr.ClassInstrumenter
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.instr.IProbeArrayStrategy
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.instr.IProbeInserterFactory
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.instr.InstrSupport
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.instr.ProbeInserter
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.MethodVisitor
-import java.lang.invoke.MethodHandle
-import java.lang.invoke.MethodHandles.publicLookup
-import java.lang.invoke.MethodType.methodType
-import kotlin.math.max
-
-/**
- * A particular way to instrument bytecode for edge coverage using a coverage map class available to
- * hold the collected coverage data at runtime.
- */
-interface EdgeCoverageStrategy {
-
- /**
- * Inject bytecode instrumentation on a control flow edge with ID [edgeId], with access to the
- * local variable [variable] that is populated at the beginning of each method by the
- * instrumentation injected in [loadLocalVariable].
- */
- fun instrumentControlFlowEdge(
- mv: MethodVisitor,
- edgeId: Int,
- variable: Int,
- coverageMapInternalClassName: String
- )
-
- /**
- * The maximal number of stack elements used by [instrumentControlFlowEdge].
- */
- val instrumentControlFlowEdgeStackSize: Int
-
- /**
- * The type of the local variable used by the instrumentation in the format used by
- * [MethodVisitor.visitFrame]'s `local` parameter, or `null` if the instrumentation does not use
- * one.
- * @see https://asm.ow2.io/javadoc/org/objectweb/asm/MethodVisitor.html#visitFrame(int,int,java.lang.Object%5B%5D,int,java.lang.Object%5B%5D)
- */
- val localVariableType: Any?
-
- /**
- * Inject bytecode that loads the coverage counters of the coverage map class described by
- * [coverageMapInternalClassName] into the local variable [variable].
- */
- fun loadLocalVariable(mv: MethodVisitor, variable: Int, coverageMapInternalClassName: String)
-
- /**
- * The maximal number of stack elements used by [loadLocalVariable].
- */
- val loadLocalVariableStackSize: Int
-}
-
-// An instance of EdgeCoverageInstrumentor should only be used to instrument a single class as it
-// internally tracks the edge IDs, which have to be globally unique.
-class EdgeCoverageInstrumentor(
- private val strategy: EdgeCoverageStrategy,
- /**
- * The class must have the following public static member
- * - method enlargeIfNeeded(int nextEdgeId): Called before a new edge ID is emitted.
- */
- coverageMapClass: Class<*>,
- private val initialEdgeId: Int,
-) : Instrumentor {
- private var nextEdgeId = initialEdgeId
-
- private val coverageMapInternalClassName = coverageMapClass.name.replace('.', '/')
- private val enlargeIfNeeded: MethodHandle =
- publicLookup().findStatic(
- coverageMapClass,
- "enlargeIfNeeded",
- methodType(
- Void::class.javaPrimitiveType,
- Int::class.javaPrimitiveType
- )
- )
-
- override fun instrument(bytecode: ByteArray): ByteArray {
- val reader = InstrSupport.classReaderFor(bytecode)
- val writer = ClassWriter(reader, 0)
- val version = InstrSupport.getMajorVersion(reader)
- val visitor = EdgeCoverageClassProbesAdapter(
- ClassInstrumenter(edgeCoverageProbeArrayStrategy, edgeCoverageProbeInserterFactory, writer),
- InstrSupport.needsFrames(version)
- )
- reader.accept(visitor, ClassReader.EXPAND_FRAMES)
- return writer.toByteArray()
- }
-
- fun analyze(executionData: ExecutionDataStore, coverageVisitor: ICoverageVisitor, bytecode: ByteArray, internalClassName: String) {
- Analyzer(executionData, coverageVisitor, edgeCoverageClassProbesAdapterFactory).run {
- analyzeClass(bytecode, internalClassName)
- }
- }
-
- val numEdges
- get() = nextEdgeId - initialEdgeId
-
- private fun nextEdgeId(): Int {
- enlargeIfNeeded.invokeExact(nextEdgeId)
- return nextEdgeId++
- }
-
- /**
- * A [ProbeInserter] that injects bytecode instrumentation at every control flow edge and
- * modifies the stack size and number of local variables accordingly.
- */
- private inner class EdgeCoverageProbeInserter(
- access: Int,
- name: String,
- desc: String,
- mv: MethodVisitor,
- arrayStrategy: IProbeArrayStrategy,
- ) : ProbeInserter(access, name, desc, mv, arrayStrategy) {
- override fun insertProbe(id: Int) {
- strategy.instrumentControlFlowEdge(mv, id, variable, coverageMapInternalClassName)
- }
-
- override fun visitMaxs(maxStack: Int, maxLocals: Int) {
- val newMaxStack = max(maxStack + strategy.instrumentControlFlowEdgeStackSize, strategy.loadLocalVariableStackSize)
- val newMaxLocals = maxLocals + if (strategy.localVariableType != null) 1 else 0
- mv.visitMaxs(newMaxStack, newMaxLocals)
- }
-
- override fun getLocalVariableType() = strategy.localVariableType
- }
-
- private val edgeCoverageProbeInserterFactory =
- IProbeInserterFactory { access, name, desc, mv, arrayStrategy ->
- EdgeCoverageProbeInserter(access, name, desc, mv, arrayStrategy)
- }
-
- private inner class EdgeCoverageClassProbesAdapter(private val cpv: ClassProbesVisitor, trackFrames: Boolean) :
- ClassProbesAdapter(cpv, trackFrames) {
- override fun nextId(): Int = nextEdgeId()
-
- override fun visitEnd() {
- cpv.visitTotalProbeCount(numEdges)
- // Avoid calling super.visitEnd() as that invokes cpv.visitTotalProbeCount with an
- // incorrect value of `count`.
- cv.visitEnd()
- }
- }
-
- private val edgeCoverageClassProbesAdapterFactory = IClassProbesAdapterFactory { probesVisitor, trackFrames ->
- EdgeCoverageClassProbesAdapter(probesVisitor, trackFrames)
- }
-
- private val edgeCoverageProbeArrayStrategy = object : IProbeArrayStrategy {
- override fun storeInstance(mv: MethodVisitor, clinit: Boolean, variable: Int): Int {
- strategy.loadLocalVariable(mv, variable, coverageMapInternalClassName)
- return strategy.loadLocalVariableStackSize
- }
-
- override fun addMembers(cv: ClassVisitor, probeCount: Int) {}
- }
-}
-
-fun MethodVisitor.push(value: Int) {
- InstrSupport.push(this, value)
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt
deleted file mode 100644
index ff68ad94..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hook.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.api.HookType
-import com.code_intelligence.jazzer.api.MethodHook
-import com.code_intelligence.jazzer.utils.descriptor
-import java.lang.invoke.MethodHandle
-import java.lang.reflect.Method
-import java.lang.reflect.Modifier
-
-class Hook private constructor(
- private val targetClassName: String,
- val hookType: HookType,
- val targetMethodName: String,
- val targetMethodDescriptor: String?,
- val additionalClassesToHook: List<String>,
- val targetInternalClassName: String,
- private val targetReturnTypeDescriptor: String?,
- private val targetWrappedReturnTypeDescriptor: String?,
- private val hookClassName: String,
- val hookInternalClassName: String,
- val hookMethodName: String,
- val hookMethodDescriptor: String
-) {
-
- override fun toString(): String {
- return "$hookType $targetClassName.$targetMethodName: $hookClassName.$hookMethodName $additionalClassesToHook"
- }
-
- companion object {
- fun createAndVerifyHook(hookMethod: Method, hookData: MethodHook, className: String): Hook {
- return createHook(hookMethod, hookData, className).also {
- verify(hookMethod, it)
- }
- }
-
- private fun createHook(hookMethod: Method, annotation: MethodHook, targetClassName: String): Hook {
- val targetReturnTypeDescriptor = annotation.targetMethodDescriptor
- .takeIf { it.isNotBlank() }?.let { extractReturnTypeDescriptor(it) }
- val hookClassName: String = hookMethod.declaringClass.name
- return Hook(
- targetClassName = targetClassName,
- hookType = annotation.type,
- targetMethodName = annotation.targetMethod,
- targetMethodDescriptor = annotation.targetMethodDescriptor.takeIf { it.isNotBlank() },
- additionalClassesToHook = annotation.additionalClassesToHook.asList(),
- targetInternalClassName = targetClassName.replace('.', '/'),
- targetReturnTypeDescriptor = targetReturnTypeDescriptor,
- targetWrappedReturnTypeDescriptor = targetReturnTypeDescriptor?.let { getWrapperTypeDescriptor(it) },
- hookClassName = hookClassName,
- hookInternalClassName = hookClassName.replace('.', '/'),
- hookMethodName = hookMethod.name,
- hookMethodDescriptor = hookMethod.descriptor
- )
- }
-
- private fun verify(hookMethod: Method, potentialHook: Hook) {
- // Verify the hook method's modifiers (public static).
- require(Modifier.isPublic(hookMethod.modifiers)) { "$potentialHook: hook method must be public" }
- require(Modifier.isStatic(hookMethod.modifiers)) { "$potentialHook: hook method must be static" }
-
- // Verify the hook method's parameter count.
- val numParameters = hookMethod.parameters.size
- when (potentialHook.hookType) {
- HookType.BEFORE, HookType.REPLACE -> require(numParameters == 4) { "$potentialHook: incorrect number of parameters (expected 4)" }
- HookType.AFTER -> require(numParameters == 5) { "$potentialHook: incorrect number of parameters (expected 5)" }
- }
-
- // Verify the hook method's parameter types.
- val parameterTypes = hookMethod.parameterTypes
- require(parameterTypes[0] == MethodHandle::class.java) { "$potentialHook: first parameter must have type MethodHandle" }
- require(parameterTypes[1] == Object::class.java || parameterTypes[1].name == potentialHook.targetClassName) { "$potentialHook: second parameter must have type Object or ${potentialHook.targetClassName}" }
- require(parameterTypes[2] == Array<Object>::class.java) { "$potentialHook: third parameter must have type Object[]" }
- require(parameterTypes[3] == Int::class.javaPrimitiveType) { "$potentialHook: fourth parameter must have type int" }
-
- // Verify the hook method's return type if possible.
- when (potentialHook.hookType) {
- HookType.BEFORE, HookType.AFTER -> require(hookMethod.returnType == Void.TYPE) {
- "$potentialHook: return type must be void"
- }
- HookType.REPLACE -> if (potentialHook.targetReturnTypeDescriptor != null) {
- if (potentialHook.targetMethodName == "<init>") {
- require(hookMethod.returnType.name == potentialHook.targetClassName) { "$potentialHook: return type must be ${potentialHook.targetClassName} to match target constructor" }
- } else if (potentialHook.targetReturnTypeDescriptor == "V") {
- require(hookMethod.returnType.descriptor == "V") { "$potentialHook: return type must be void" }
- } else {
- require(
- hookMethod.returnType.descriptor in listOf(
- java.lang.Object::class.java.descriptor,
- potentialHook.targetReturnTypeDescriptor,
- potentialHook.targetWrappedReturnTypeDescriptor
- )
- ) {
- "$potentialHook: return type must have type Object or match the descriptors ${potentialHook.targetReturnTypeDescriptor} or ${potentialHook.targetWrappedReturnTypeDescriptor}"
- }
- }
- }
- }
-
- // AfterMethodHook only: Verify the type of the last parameter if known. Even if not
- // known, it must not be a primitive value.
- if (potentialHook.hookType == HookType.AFTER) {
- if (potentialHook.targetReturnTypeDescriptor != null) {
- require(
- parameterTypes[4] == java.lang.Object::class.java ||
- parameterTypes[4].descriptor == potentialHook.targetWrappedReturnTypeDescriptor
- ) {
- "$potentialHook: fifth parameter must have type Object or match the descriptor ${potentialHook.targetWrappedReturnTypeDescriptor}"
- }
- } else {
- require(!parameterTypes[4].isPrimitive) {
- "$potentialHook: fifth parameter must not be a primitive type, use a boxed type instead"
- }
- }
- }
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookInstrumentor.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookInstrumentor.kt
deleted file mode 100644
index 6db76605..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookInstrumentor.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.MethodVisitor
-
-internal class HookInstrumentor(private val hooks: Iterable<Hook>, private val java6Mode: Boolean) : Instrumentor {
-
- private lateinit var random: DeterministicRandom
-
- override fun instrument(bytecode: ByteArray): ByteArray {
- val reader = ClassReader(bytecode)
- val writer = ClassWriter(reader, ClassWriter.COMPUTE_MAXS)
- random = DeterministicRandom("hook", reader.className)
- val interceptor = object : ClassVisitor(Instrumentor.ASM_API_VERSION, writer) {
- override fun visitMethod(
- access: Int,
- name: String?,
- descriptor: String?,
- signature: String?,
- exceptions: Array<String>?,
- ): MethodVisitor? {
- val mv = cv.visitMethod(access, name, descriptor, signature, exceptions) ?: return null
- return if (shouldInstrument(access))
- makeHookMethodVisitor(access, descriptor, mv, hooks, java6Mode, random)
- else
- mv
- }
- }
- reader.accept(interceptor, ClassReader.EXPAND_FRAMES)
- return writer.toByteArray()
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookMethodVisitor.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookMethodVisitor.kt
deleted file mode 100644
index 1694be58..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/HookMethodVisitor.kt
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.api.HookType
-import org.objectweb.asm.Handle
-import org.objectweb.asm.MethodVisitor
-import org.objectweb.asm.Opcodes
-import org.objectweb.asm.Type
-import org.objectweb.asm.commons.LocalVariablesSorter
-import java.util.concurrent.atomic.AtomicBoolean
-
-internal fun makeHookMethodVisitor(
- access: Int,
- descriptor: String?,
- methodVisitor: MethodVisitor?,
- hooks: Iterable<Hook>,
- java6Mode: Boolean,
- random: DeterministicRandom,
-): MethodVisitor {
- return HookMethodVisitor(access, descriptor, methodVisitor, hooks, java6Mode, random).lvs
-}
-
-private class HookMethodVisitor(
- access: Int,
- descriptor: String?,
- methodVisitor: MethodVisitor?,
- hooks: Iterable<Hook>,
- private val java6Mode: Boolean,
- private val random: DeterministicRandom,
-) : MethodVisitor(Instrumentor.ASM_API_VERSION, methodVisitor) {
-
- companion object {
- private val showUnsupportedHookWarning = AtomicBoolean(true)
- }
-
- val lvs = object : LocalVariablesSorter(Instrumentor.ASM_API_VERSION, access, descriptor, this) {
- override fun updateNewLocals(newLocals: Array<Any>) {
- // The local variables involved in calling hooks do not need to outlive the current
- // basic block and should thus not appear in stack map frames. By requesting the
- // LocalVariableSorter to fill their entries in stack map frames with TOP, they will
- // be treated like an unused local variable slot.
- newLocals.fill(Opcodes.TOP)
- }
- }
-
- private val hooks = hooks.groupBy { hook ->
- var hookKey = "${hook.hookType}#${hook.targetInternalClassName}#${hook.targetMethodName}"
- if (hook.targetMethodDescriptor != null)
- hookKey += "#${hook.targetMethodDescriptor}"
- hookKey
- }
-
- override fun visitMethodInsn(
- opcode: Int,
- owner: String,
- methodName: String,
- methodDescriptor: String,
- isInterface: Boolean,
- ) {
- if (!isMethodInvocationOp(opcode)) {
- mv.visitMethodInsn(opcode, owner, methodName, methodDescriptor, isInterface)
- return
- }
- handleMethodInsn(opcode, owner, methodName, methodDescriptor, isInterface)
- }
-
- fun handleMethodInsn(
- opcode: Int,
- owner: String,
- methodName: String,
- methodDescriptor: String,
- isInterface: Boolean,
- ) {
- val matchingHooks = findMatchingHooks(owner, methodName, methodDescriptor)
-
- if (matchingHooks.isEmpty()) {
- mv.visitMethodInsn(opcode, owner, methodName, methodDescriptor, isInterface)
- return
- }
-
- val paramDescriptors = extractParameterTypeDescriptors(methodDescriptor)
- val localObjArr = storeMethodArguments(paramDescriptors)
- // If the method we're hooking is not static there is now a reference to
- // the object the method was invoked on at the top of the stack.
- // If the method is static, that object is missing. We make up for it by pushing a null ref.
- if (opcode == Opcodes.INVOKESTATIC) {
- mv.visitInsn(Opcodes.ACONST_NULL)
- }
-
- // Save the owner object to a new local variable
- val ownerDescriptor = "L$owner;"
- val localOwnerObj = lvs.newLocal(Type.getType(ownerDescriptor))
- mv.visitVarInsn(Opcodes.ASTORE, localOwnerObj) // consume objectref
- // We now removed all values for the original method call from the operand stack
- // and saved them to local variables.
-
- val returnTypeDescriptor = extractReturnTypeDescriptor(methodDescriptor)
- // Create a local variable to store the return value
- val localReturnObj = lvs.newLocal(Type.getType(getWrapperTypeDescriptor(returnTypeDescriptor)))
-
- matchingHooks.forEachIndexed { index, hook ->
- // The hookId is used to identify a call site.
- val hookId = random.nextInt()
-
- // Start to build the arguments for the hook method.
- if (methodName == "<init>") {
- // Constructor is invoked on an uninitialized object, and that's still on the stack.
- // In case of REPLACE pop it from the stack and replace it afterwards with the returned
- // one from the hook.
- if (hook.hookType == HookType.REPLACE) {
- mv.visitInsn(Opcodes.POP)
- }
- // Special case for constructors:
- // We cannot create a MethodHandle for a constructor, so we push null instead.
- mv.visitInsn(Opcodes.ACONST_NULL) // push nullref
- // Only pass the this object if it has been initialized by the time the hook is invoked.
- if (hook.hookType == HookType.AFTER) {
- mv.visitVarInsn(Opcodes.ALOAD, localOwnerObj)
- } else {
- mv.visitInsn(Opcodes.ACONST_NULL) // push nullref
- }
- } else {
- // Push a MethodHandle representing the hooked method.
- val handleOpcode = when (opcode) {
- Opcodes.INVOKEVIRTUAL -> Opcodes.H_INVOKEVIRTUAL
- Opcodes.INVOKEINTERFACE -> Opcodes.H_INVOKEINTERFACE
- Opcodes.INVOKESTATIC -> Opcodes.H_INVOKESTATIC
- Opcodes.INVOKESPECIAL -> Opcodes.H_INVOKESPECIAL
- else -> -1
- }
- if (java6Mode) {
- // MethodHandle constants (type 15) are not supported in Java 6 class files (major version 50).
- mv.visitInsn(Opcodes.ACONST_NULL) // push nullref
- } else {
- mv.visitLdcInsn(
- Handle(
- handleOpcode,
- owner,
- methodName,
- methodDescriptor,
- isInterface
- )
- ) // push MethodHandle
- }
- // Stack layout: ... | MethodHandle (objectref)
- // Push the owner object again
- mv.visitVarInsn(Opcodes.ALOAD, localOwnerObj)
- }
- // Stack layout: ... | MethodHandle (objectref) | owner (objectref)
- // Push a reference to our object array with the saved arguments
- mv.visitVarInsn(Opcodes.ALOAD, localObjArr)
- // Stack layout: ... | MethodHandle (objectref) | owner (objectref) | object array (arrayref)
- // Push the hook id
- mv.visitLdcInsn(hookId)
- // Stack layout: ... | MethodHandle (objectref) | owner (objectref) | object array (arrayref) | hookId (int)
- // How we proceed depends on the type of hook we want to implement
- when (hook.hookType) {
- HookType.BEFORE -> {
- // Call the hook method
- mv.visitMethodInsn(
- Opcodes.INVOKESTATIC,
- hook.hookInternalClassName,
- hook.hookMethodName,
- hook.hookMethodDescriptor,
- false
- )
-
- // Call the original method if this is the last BEFORE hook. If not, the original method will be
- // called by the next AFTER hook.
- if (index == matchingHooks.lastIndex) {
- // Stack layout: ...
- // Push the values for the original method call onto the stack again
- if (opcode != Opcodes.INVOKESTATIC) {
- mv.visitVarInsn(Opcodes.ALOAD, localOwnerObj) // push owner object
- }
- loadMethodArguments(paramDescriptors, localObjArr) // push all method arguments
- // Stack layout: ... | [owner (objectref)] | arg1 (primitive/objectref) | arg2 (primitive/objectref) | ...
- mv.visitMethodInsn(opcode, owner, methodName, methodDescriptor, isInterface)
- }
- }
- HookType.REPLACE -> {
- // Call the hook method
- mv.visitMethodInsn(
- Opcodes.INVOKESTATIC,
- hook.hookInternalClassName,
- hook.hookMethodName,
- hook.hookMethodDescriptor,
- false
- )
- // Stack layout: ... | [return value (primitive/objectref)]
- // Check if we need to process the return value
- if (returnTypeDescriptor != "V") {
- val hookMethodReturnType = extractReturnTypeDescriptor(hook.hookMethodDescriptor)
- // if the hook method's return type is primitive we don't need to unwrap or cast it
- if (!isPrimitiveType(hookMethodReturnType)) {
- // Check if the returned object type is different than the one that should be returned
- // If a primitive should be returned we check it's wrapper type
- val expectedType = getWrapperTypeDescriptor(returnTypeDescriptor)
- if (expectedType != hookMethodReturnType) {
- // Cast object
- mv.visitTypeInsn(Opcodes.CHECKCAST, extractInternalClassName(expectedType))
- }
- // Check if we need to unwrap the returned object
- unwrapTypeIfPrimitive(returnTypeDescriptor)
- }
- }
- }
- HookType.AFTER -> {
- // Call the original method before the first AFTER hook
- if (index == 0 || matchingHooks[index - 1].hookType != HookType.AFTER) {
- // Push the values for the original method call again onto the stack
- if (opcode != Opcodes.INVOKESTATIC) {
- mv.visitVarInsn(Opcodes.ALOAD, localOwnerObj) // push owner object
- }
- loadMethodArguments(paramDescriptors, localObjArr) // push all method arguments
- // Stack layout: ... | MethodHandle (objectref) | owner (objectref) | object array (arrayref) | hookId (int)
- // | [owner (objectref)] | arg1 (primitive/objectref) | arg2 (primitive/objectref) | ...
- mv.visitMethodInsn(opcode, owner, methodName, methodDescriptor, isInterface)
- if (returnTypeDescriptor == "V") {
- // If the method didn't return anything, we push a nullref as placeholder
- mv.visitInsn(Opcodes.ACONST_NULL) // push nullref
- }
- // Wrap return value if it is a primitive type
- wrapTypeIfPrimitive(returnTypeDescriptor)
- mv.visitVarInsn(Opcodes.ASTORE, localReturnObj) // consume objectref
- }
- mv.visitVarInsn(Opcodes.ALOAD, localReturnObj) // push objectref
-
- // Stack layout: ... | MethodHandle (objectref) | owner (objectref) | object array (arrayref) | hookId (int)
- // | return value (objectref)
- // Store the result value in a local variable (but keep it on the stack)
- // Call the hook method
- mv.visitMethodInsn(
- Opcodes.INVOKESTATIC,
- hook.hookInternalClassName,
- hook.hookMethodName,
- hook.hookMethodDescriptor,
- false
- )
- // Stack layout: ...
- // Push the return value on the stack after the last AFTER hook if the original method returns a value
- if (index == matchingHooks.size - 1 && returnTypeDescriptor != "V") {
- // Push the return value again
- mv.visitVarInsn(Opcodes.ALOAD, localReturnObj) // push objectref
- // Unwrap it, if it was a primitive value
- unwrapTypeIfPrimitive(returnTypeDescriptor)
- // Stack layout: ... | return value (primitive/objectref)
- }
- }
- }
- }
- }
-
- private fun isMethodInvocationOp(opcode: Int) = opcode in listOf(
- Opcodes.INVOKEVIRTUAL,
- Opcodes.INVOKEINTERFACE,
- Opcodes.INVOKESTATIC,
- Opcodes.INVOKESPECIAL
- )
-
- private fun findMatchingHooks(owner: String, name: String, descriptor: String): List<Hook> {
- val result = HookType.values().flatMap { hookType ->
- val withoutDescriptorKey = "$hookType#$owner#$name"
- val withDescriptorKey = "$withoutDescriptorKey#$descriptor"
- hooks[withDescriptorKey].orEmpty() + hooks[withoutDescriptorKey].orEmpty()
- }.sortedBy { it.hookType }
- val replaceHookCount = result.count { it.hookType == HookType.REPLACE }
- check(
- replaceHookCount == 0 ||
- (replaceHookCount == 1 && result.size == 1)
- ) {
- "For a given method, You can either have a single REPLACE hook or BEFORE/AFTER hooks. Found:\n $result"
- }
-
- return result
- .filter { !isReplaceHookInJava6mode(it) }
- .sortedByDescending { it.toString() }
- }
-
- private fun isReplaceHookInJava6mode(hook: Hook): Boolean {
- if (java6Mode && hook.hookType == HookType.REPLACE) {
- if (showUnsupportedHookWarning.getAndSet(false)) {
- println(
- """WARN: Some hooks could not be applied to class files built for Java 7 or lower.
- |WARN: Ensure that the fuzz target and its dependencies are compiled with
- |WARN: -target 8 or higher to identify as many bugs as possible.
- """.trimMargin()
- )
- }
- return true
- }
- return false
- }
-
- // Stores all arguments for a method call in a local object array.
- // paramDescriptors: The type descriptors for all method arguments
- private fun storeMethodArguments(paramDescriptors: List<String>): Int {
- // Allocate a new Object[] for the methods parameters.
- mv.visitIntInsn(Opcodes.SIPUSH, paramDescriptors.size)
- mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object")
- val localObjArr = lvs.newLocal(Type.getType("[Ljava/lang/Object;"))
- mv.visitVarInsn(Opcodes.ASTORE, localObjArr)
-
- // Loop over all arguments in reverse order (because the last argument is on top).
- for ((argIdx, argDescriptor) in paramDescriptors.withIndex().reversed()) {
- // If the argument is a primitive type, wrap it in it's wrapper class
- wrapTypeIfPrimitive(argDescriptor)
- // Store the argument in our object array, for that we need to shape the stack first.
- // Stack layout: ... | method argument (objectref)
- mv.visitVarInsn(Opcodes.ALOAD, localObjArr)
- // Stack layout: ... | method argument (objectref) | object array (arrayref)
- mv.visitInsn(Opcodes.SWAP)
- // Stack layout: ... | object array (arrayref) | method argument (objectref)
- mv.visitIntInsn(Opcodes.SIPUSH, argIdx)
- // Stack layout: ... | object array (arrayref) | method argument (objectref) | argument index (int)
- mv.visitInsn(Opcodes.SWAP)
- // Stack layout: ... | object array (arrayref) | argument index (int) | method argument (objectref)
- mv.visitInsn(Opcodes.AASTORE) // consume all three: arrayref, index, value
- // Stack layout: ...
- // Continue with the remaining method arguments
- }
-
- // Return a reference to the array with the parameters.
- return localObjArr
- }
-
- // Loads all arguments for a method call from a local object array.
- // argTypeSigs: The type signatures for all method arguments
- // localObjArr: Index of a local variable containing an object array where the arguments will be loaded from
- private fun loadMethodArguments(paramDescriptors: List<String>, localObjArr: Int) {
- // Loop over all arguments
- for ((argIdx, argDescriptor) in paramDescriptors.withIndex()) {
- // Push a reference to the object array on the stack
- mv.visitVarInsn(Opcodes.ALOAD, localObjArr)
- // Stack layout: ... | object array (arrayref)
- // Push the index of the current argument on the stack
- mv.visitIntInsn(Opcodes.SIPUSH, argIdx)
- // Stack layout: ... | object array (arrayref) | argument index (int)
- // Load the argument from the array
- mv.visitInsn(Opcodes.AALOAD)
- // Stack layout: ... | method argument (objectref)
- // Cast object to it's original type (or it's wrapper object)
- val wrapperTypeDescriptor = getWrapperTypeDescriptor(argDescriptor)
- mv.visitTypeInsn(Opcodes.CHECKCAST, extractInternalClassName(wrapperTypeDescriptor))
- // If the argument is a supposed to be a primitive type, unwrap the wrapped type
- unwrapTypeIfPrimitive(argDescriptor)
- // Stack layout: ... | method argument (primitive/objectref)
- // Continue with the remaining method arguments
- }
- }
-
- // Removes a primitive value from the top of the operand stack
- // and pushes it enclosed in its wrapper type (e.g. removes int, pushes Integer).
- // This is done by calling .valueOf(...) on the wrapper class.
- private fun wrapTypeIfPrimitive(unwrappedTypeDescriptor: String) {
- if (!isPrimitiveType(unwrappedTypeDescriptor) || unwrappedTypeDescriptor == "V") return
- val wrapperTypeDescriptor = getWrapperTypeDescriptor(unwrappedTypeDescriptor)
- val wrapperType = extractInternalClassName(wrapperTypeDescriptor)
- val valueOfDescriptor = "($unwrappedTypeDescriptor)$wrapperTypeDescriptor"
- mv.visitMethodInsn(Opcodes.INVOKESTATIC, wrapperType, "valueOf", valueOfDescriptor, false)
- }
-
- // Removes a wrapper object around a given primitive type from the top of the operand stack
- // and pushes the primitive value it contains (e.g. removes Integer, pushes int).
- // This is done by calling .intValue(...) / .charValue(...) / ... on the wrapper object.
- private fun unwrapTypeIfPrimitive(primitiveTypeDescriptor: String) {
- val (methodName, wrappedTypeDescriptor) = when (primitiveTypeDescriptor) {
- "B" -> Pair("byteValue", "java/lang/Byte")
- "C" -> Pair("charValue", "java/lang/Character")
- "D" -> Pair("doubleValue", "java/lang/Double")
- "F" -> Pair("floatValue", "java/lang/Float")
- "I" -> Pair("intValue", "java/lang/Integer")
- "J" -> Pair("longValue", "java/lang/Long")
- "S" -> Pair("shortValue", "java/lang/Short")
- "Z" -> Pair("booleanValue", "java/lang/Boolean")
- else -> return
- }
- mv.visitMethodInsn(
- Opcodes.INVOKEVIRTUAL,
- wrappedTypeDescriptor,
- methodName,
- "()$primitiveTypeDescriptor",
- false
- )
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hooks.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hooks.kt
deleted file mode 100644
index 66a21ee7..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Hooks.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.api.MethodHook
-import com.code_intelligence.jazzer.api.MethodHooks
-import com.code_intelligence.jazzer.utils.ClassNameGlobber
-import com.code_intelligence.jazzer.utils.descriptor
-import io.github.classgraph.ClassGraph
-import io.github.classgraph.ScanResult
-import java.lang.reflect.Method
-
-data class Hooks(
- val hooks: List<Hook>,
- val hookClasses: Set<Class<*>>,
- val additionalHookClassNameGlobber: ClassNameGlobber
-) {
-
- companion object {
- fun loadHooks(vararg hookClassNames: Set<String>): List<Hooks> {
- return ClassGraph()
- .enableClassInfo()
- .enableSystemJarsAndModules()
- .rejectPackages("jaz.*", "com.code_intelligence.jazzer.*")
- .scan()
- .use { scanResult ->
- // Capture scanResult in HooksLoader field to not pass it through
- // all internal hook loading methods.
- val loader = HooksLoader(scanResult)
- hookClassNames.map(loader::load)
- }
- }
-
- private class HooksLoader(private val scanResult: ScanResult) {
- fun load(hookClassNames: Set<String>): Hooks {
- val hooksWithHookClasses = hookClassNames.flatMap(::loadHooks)
- val hooks = hooksWithHookClasses.map { it.first }
- val hookClasses = hooksWithHookClasses.map { it.second }.toSet()
- val additionalHookClassNameGlobber = ClassNameGlobber(
- hooks.flatMap(Hook::additionalClassesToHook),
- emptyList()
- )
- return Hooks(hooks, hookClasses, additionalHookClassNameGlobber)
- }
-
- private fun loadHooks(hookClassName: String): List<Pair<Hook, Class<*>>> {
- return try {
- // Custom hook classes outside the agent jar can not be found by bootstrap
- // class loader, so use the system class loader as that will be the main application
- // class loader and can access jars on the classpath.
- // We let the static initializers of hook classes execute so that hooks can run
- // code before the fuzz target class has been loaded (e.g., register themselves
- // for the onFuzzTargetReady callback).
- val hookClass = Class.forName(hookClassName, true, ClassLoader.getSystemClassLoader())
- loadHooks(hookClass).also {
- println("INFO: Loaded ${it.size} hooks from $hookClassName")
- }.map {
- it to hookClass
- }
- } catch (e: ClassNotFoundException) {
- println("WARN: Failed to load hooks from $hookClassName: ${e.printStackTrace()}")
- emptyList()
- }
- }
-
- private fun loadHooks(hookClass: Class<*>): List<Hook> {
- val hooks = mutableListOf<Hook>()
- for (method in hookClass.methods.sortedBy { it.descriptor }) {
- method.getAnnotation(MethodHook::class.java)?.let {
- hooks.addAll(verifyAndGetHooks(method, it))
- }
- method.getAnnotation(MethodHooks::class.java)?.let {
- it.value.forEach { hookAnnotation ->
- hooks.addAll(verifyAndGetHooks(method, hookAnnotation))
- }
- }
- }
- return hooks
- }
-
- private fun verifyAndGetHooks(hookMethod: Method, hookData: MethodHook): List<Hook> {
- return lookupClassesToHook(hookData.targetClassName)
- .map { className ->
- Hook.createAndVerifyHook(hookMethod, hookData, className)
- }
- }
-
- private fun lookupClassesToHook(annotationTargetClassName: String): List<String> {
- // Allowing arbitrary exterior whitespace in the target class name allows for an easy workaround
- // for mangled hooks due to shading applied to hooks.
- val targetClassName = annotationTargetClassName.trim()
- val targetClassInfo = scanResult.getClassInfo(targetClassName) ?: return listOf(targetClassName)
- val additionalTargetClasses = when {
- targetClassInfo.isInterface -> scanResult.getClassesImplementing(targetClassName)
- targetClassInfo.isAbstract -> scanResult.getSubclasses(targetClassName)
- else -> emptyList()
- }
- return (listOf(targetClassName) + additionalTargetClasses.map { it.name }).sorted()
- }
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Instrumentor.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Instrumentor.kt
deleted file mode 100644
index 78793842..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/Instrumentor.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import org.objectweb.asm.Opcodes
-import org.objectweb.asm.tree.MethodNode
-
-enum class InstrumentationType {
- CMP,
- COV,
- DIV,
- GEP,
- INDIR,
- NATIVE,
-}
-
-internal interface Instrumentor {
- fun instrument(bytecode: ByteArray): ByteArray
-
- fun shouldInstrument(access: Int): Boolean {
- return (access and Opcodes.ACC_ABSTRACT == 0) &&
- (access and Opcodes.ACC_NATIVE == 0)
- }
-
- fun shouldInstrument(method: MethodNode): Boolean {
- return shouldInstrument(method.access) &&
- method.instructions.size() > 0
- }
-
- companion object {
- const val ASM_API_VERSION = Opcodes.ASM9
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/StaticMethodStrategy.java b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/StaticMethodStrategy.java
deleted file mode 100644
index 0512ec2a..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/StaticMethodStrategy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2022 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor;
-
-import com.code_intelligence.jazzer.third_party.org.jacoco.core.internal.instr.InstrSupport;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-
-public class StaticMethodStrategy implements EdgeCoverageStrategy {
- @Override
- public void instrumentControlFlowEdge(
- MethodVisitor mv, int edgeId, int variable, String coverageMapInternalClassName) {
- InstrSupport.push(mv, edgeId);
- mv.visitMethodInsn(
- Opcodes.INVOKESTATIC, coverageMapInternalClassName, "recordCoverage", "(I)V", false);
- }
-
- @Override
- public int getInstrumentControlFlowEdgeStackSize() {
- return 1;
- }
-
- @Override
- public Object getLocalVariableType() {
- return null;
- }
-
- @Override
- public void loadLocalVariable(
- MethodVisitor mv, int variable, String coverageMapInternalClassName) {}
-
- @Override
- public int getLoadLocalVariableStackSize() {
- return 0;
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentor.kt b/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentor.kt
deleted file mode 100644
index 65f11e52..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/instrumentor/TraceDataFlowInstrumentor.kt
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.instrumentor
-
-import com.code_intelligence.jazzer.runtime.TraceDataFlowNativeCallbacks
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.Opcodes
-import org.objectweb.asm.tree.AbstractInsnNode
-import org.objectweb.asm.tree.ClassNode
-import org.objectweb.asm.tree.InsnList
-import org.objectweb.asm.tree.InsnNode
-import org.objectweb.asm.tree.IntInsnNode
-import org.objectweb.asm.tree.LdcInsnNode
-import org.objectweb.asm.tree.LookupSwitchInsnNode
-import org.objectweb.asm.tree.MethodInsnNode
-import org.objectweb.asm.tree.MethodNode
-import org.objectweb.asm.tree.TableSwitchInsnNode
-
-internal class TraceDataFlowInstrumentor(private val types: Set<InstrumentationType>, callbackClass: Class<*> = TraceDataFlowNativeCallbacks::class.java) : Instrumentor {
-
- private val callbackInternalClassName = callbackClass.name.replace('.', '/')
- private lateinit var random: DeterministicRandom
-
- override fun instrument(bytecode: ByteArray): ByteArray {
- val node = ClassNode()
- val reader = ClassReader(bytecode)
- reader.accept(node, 0)
- random = DeterministicRandom("trace", node.name)
- for (method in node.methods) {
- if (shouldInstrument(method)) {
- addDataFlowInstrumentation(method)
- }
- }
-
- val writer = ClassWriter(ClassWriter.COMPUTE_MAXS)
- node.accept(writer)
- return writer.toByteArray()
- }
-
- @OptIn(ExperimentalUnsignedTypes::class)
- private fun addDataFlowInstrumentation(method: MethodNode) {
- loop@ for (inst in method.instructions.toArray()) {
- when (inst.opcode) {
- Opcodes.LCMP -> {
- if (InstrumentationType.CMP !in types) continue@loop
- method.instructions.insertBefore(inst, longCmpInstrumentation())
- method.instructions.remove(inst)
- }
- Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE,
- Opcodes.IF_ICMPLT, Opcodes.IF_ICMPLE,
- Opcodes.IF_ICMPGT, Opcodes.IF_ICMPGE -> {
- if (InstrumentationType.CMP !in types) continue@loop
- method.instructions.insertBefore(inst, intCmpInstrumentation())
- }
- Opcodes.IFEQ, Opcodes.IFNE,
- Opcodes.IFLT, Opcodes.IFLE,
- Opcodes.IFGT, Opcodes.IFGE -> {
- if (InstrumentationType.CMP !in types) continue@loop
- // The IF* opcodes are often used to branch based on the result of a compare
- // instruction for a type other than int. The operands of this compare will
- // already be reported via the instrumentation above (for non-floating point
- // numbers) and the follow-up compare does not provide a good signal as all
- // operands will be in {-1, 0, 1}. Skip instrumentation for it.
- if (inst.previous?.opcode in listOf(Opcodes.DCMPG, Opcodes.DCMPL, Opcodes.FCMPG, Opcodes.DCMPL) ||
- (inst.previous as? MethodInsnNode)?.name == "traceCmpLongWrapper"
- )
- continue@loop
- method.instructions.insertBefore(inst, ifInstrumentation())
- }
- Opcodes.LOOKUPSWITCH, Opcodes.TABLESWITCH -> {
- if (InstrumentationType.CMP !in types) continue@loop
- // Mimic the exclusion logic for small label values in libFuzzer:
- // https://github.com/llvm-mirror/compiler-rt/blob/69445f095c22aac2388f939bedebf224a6efcdaf/lib/fuzzer/FuzzerTracePC.cpp#L520
- // Case values are reported to libFuzzer via an array of unsigned long values and thus need to be
- // sorted by unsigned value.
- val caseValues = when (inst) {
- is LookupSwitchInsnNode -> {
- if (inst.keys.isEmpty() || (0 <= inst.keys.first() && inst.keys.last() < 256))
- continue@loop
- inst.keys
- }
- is TableSwitchInsnNode -> {
- if (0 <= inst.min && inst.max < 256)
- continue@loop
- (inst.min..inst.max).filter { caseValue ->
- val index = caseValue - inst.min
- // Filter out "gap cases".
- inst.labels[index].label != inst.dflt.label
- }.toList()
- }
- // Not reached.
- else -> continue@loop
- }.sortedBy { it.toUInt() }.map { it.toLong() }.toLongArray()
- method.instructions.insertBefore(inst, switchInstrumentation(caseValues))
- }
- Opcodes.IDIV -> {
- if (InstrumentationType.DIV !in types) continue@loop
- method.instructions.insertBefore(inst, intDivInstrumentation())
- }
- Opcodes.LDIV -> {
- if (InstrumentationType.DIV !in types) continue@loop
- method.instructions.insertBefore(inst, longDivInstrumentation())
- }
- Opcodes.AALOAD, Opcodes.BALOAD,
- Opcodes.CALOAD, Opcodes.DALOAD,
- Opcodes.FALOAD, Opcodes.IALOAD,
- Opcodes.LALOAD, Opcodes.SALOAD -> {
- if (InstrumentationType.GEP !in types) continue@loop
- if (!isConstantIntegerPushInsn(inst.previous)) continue@loop
- method.instructions.insertBefore(inst, gepLoadInstrumentation())
- }
- Opcodes.INVOKEINTERFACE, Opcodes.INVOKESPECIAL, Opcodes.INVOKESTATIC, Opcodes.INVOKEVIRTUAL -> {
- if (InstrumentationType.GEP !in types) continue@loop
- if (!isGepLoadMethodInsn(inst as MethodInsnNode)) continue@loop
- if (!isConstantIntegerPushInsn(inst.previous)) continue@loop
- method.instructions.insertBefore(inst, gepLoadInstrumentation())
- }
- }
- }
- }
-
- private fun InsnList.pushFakePc() {
- add(LdcInsnNode(random.nextInt(512)))
- }
-
- private fun longCmpInstrumentation() = InsnList().apply {
- pushFakePc()
- // traceCmpLong returns the result of the comparison as duplicating two longs on the stack
- // is not possible without local variables.
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceCmpLongWrapper", "(JJI)I", false))
- }
-
- private fun intCmpInstrumentation() = InsnList().apply {
- add(InsnNode(Opcodes.DUP2))
- pushFakePc()
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceCmpInt", "(III)V", false))
- }
-
- private fun ifInstrumentation() = InsnList().apply {
- add(InsnNode(Opcodes.DUP))
- // All if* instructions are compares to the constant 0.
- add(InsnNode(Opcodes.ICONST_0))
- add(InsnNode(Opcodes.SWAP))
- pushFakePc()
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceConstCmpInt", "(III)V", false))
- }
-
- private fun intDivInstrumentation() = InsnList().apply {
- add(InsnNode(Opcodes.DUP))
- pushFakePc()
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceDivInt", "(II)V", false))
- }
-
- private fun longDivInstrumentation() = InsnList().apply {
- add(InsnNode(Opcodes.DUP2))
- pushFakePc()
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceDivLong", "(JI)V", false))
- }
-
- private fun switchInstrumentation(caseValues: LongArray) = InsnList().apply {
- // duplicate {lookup,table}switch key for use as first function argument
- add(InsnNode(Opcodes.DUP))
- add(InsnNode(Opcodes.I2L))
- // Set up array with switch case values. The format libfuzzer expects is created here directly, i.e., the first
- // two entries are the number of cases and the bit size of values (always 32).
- add(IntInsnNode(Opcodes.SIPUSH, caseValues.size + 2))
- add(IntInsnNode(Opcodes.NEWARRAY, Opcodes.T_LONG))
- // Store number of cases
- add(InsnNode(Opcodes.DUP))
- add(IntInsnNode(Opcodes.SIPUSH, 0))
- add(LdcInsnNode(caseValues.size.toLong()))
- add(InsnNode(Opcodes.LASTORE))
- // Store bit size of keys
- add(InsnNode(Opcodes.DUP))
- add(IntInsnNode(Opcodes.SIPUSH, 1))
- add(LdcInsnNode(32.toLong()))
- add(InsnNode(Opcodes.LASTORE))
- // Store {lookup,table}switch case values
- for ((i, caseValue) in caseValues.withIndex()) {
- add(InsnNode(Opcodes.DUP))
- add(IntInsnNode(Opcodes.SIPUSH, 2 + i))
- add(LdcInsnNode(caseValue))
- add(InsnNode(Opcodes.LASTORE))
- }
- pushFakePc()
- // call the native callback function
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceSwitch", "(J[JI)V", false))
- }
-
- /**
- * Returns true if [node] represents an instruction that possibly pushes a valid, non-zero, constant array index
- * onto the stack.
- */
- private fun isConstantIntegerPushInsn(node: AbstractInsnNode?) = node?.opcode in CONSTANT_INTEGER_PUSH_OPCODES
-
- /**
- * Returns true if [node] represents a call to a method that performs an indexed lookup into an array-like
- * structure.
- */
- private fun isGepLoadMethodInsn(node: MethodInsnNode): Boolean {
- if (!node.desc.startsWith("(I)")) return false
- val returnType = node.desc.removePrefix("(I)")
- return MethodInfo(node.owner, node.name, returnType) in GEP_LOAD_METHODS
- }
-
- private fun gepLoadInstrumentation() = InsnList().apply {
- // Duplicate the index and convert to long.
- add(InsnNode(Opcodes.DUP))
- add(InsnNode(Opcodes.I2L))
- pushFakePc()
- add(MethodInsnNode(Opcodes.INVOKESTATIC, callbackInternalClassName, "traceGep", "(JI)V", false))
- }
-
- companion object {
- // Low constants (0, 1) are omitted as they create a lot of noise.
- val CONSTANT_INTEGER_PUSH_OPCODES = listOf(
- Opcodes.BIPUSH, Opcodes.SIPUSH,
- Opcodes.LDC,
- Opcodes.ICONST_2, Opcodes.ICONST_3, Opcodes.ICONST_4, Opcodes.ICONST_5
- )
-
- data class MethodInfo(val internalClassName: String, val name: String, val returnType: String)
-
- val GEP_LOAD_METHODS = setOf(
- MethodInfo("java/util/AbstractList", "get", "Ljava/lang/Object;"),
- MethodInfo("java/util/ArrayList", "get", "Ljava/lang/Object;"),
- MethodInfo("java/util/List", "get", "Ljava/lang/Object;"),
- MethodInfo("java/util/Stack", "get", "Ljava/lang/Object;"),
- MethodInfo("java/util/Vector", "get", "Ljava/lang/Object;"),
- MethodInfo("java/lang/CharSequence", "charAt", "C"),
- MethodInfo("java/lang/String", "charAt", "C"),
- MethodInfo("java/lang/StringBuffer", "charAt", "C"),
- MethodInfo("java/lang/StringBuilder", "charAt", "C"),
- MethodInfo("java/lang/String", "codePointAt", "I"),
- MethodInfo("java/lang/String", "codePointBefore", "I"),
- MethodInfo("java/nio/ByteBuffer", "get", "B"),
- MethodInfo("java/nio/ByteBuffer", "getChar", "C"),
- MethodInfo("java/nio/ByteBuffer", "getDouble", "D"),
- MethodInfo("java/nio/ByteBuffer", "getFloat", "F"),
- MethodInfo("java/nio/ByteBuffer", "getInt", "I"),
- MethodInfo("java/nio/ByteBuffer", "getLong", "J"),
- MethodInfo("java/nio/ByteBuffer", "getShort", "S"),
- )
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel
deleted file mode 100644
index 08bd7653..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/replay/BUILD.bazel
+++ /dev/null
@@ -1,17 +0,0 @@
-load("@fmeum_rules_jni//jni:defs.bzl", "java_jni_library")
-
-java_jni_library(
- name = "replay",
- srcs = ["Replayer.java"],
- native_libs = ["//driver/src/main/native/com/code_intelligence/jazzer/driver:fuzzed_data_provider_standalone"],
- deps = [
- "//agent/src/main/java/com/code_intelligence/jazzer/api",
- "//agent/src/main/java/com/code_intelligence/jazzer/runtime:fuzzed_data_provider",
- ],
-)
-
-java_binary(
- name = "Replayer",
- visibility = ["//visibility:public"],
- runtime_deps = [":replay"],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java b/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java
deleted file mode 100644
index 0a250d1a..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/replay/Replayer.java
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.replay;
-
-import com.code_intelligence.jazzer.api.FuzzedDataProvider;
-import com.code_intelligence.jazzer.runtime.FuzzedDataProviderImpl;
-import com.github.fmeum.rules_jni.RulesJni;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Arrays;
-
-public class Replayer {
- public static final int STATUS_FINDING = 77;
- public static final int STATUS_OTHER_ERROR = 1;
-
- static {
- System.setProperty("jazzer.is_replayer", "true");
- try {
- RulesJni.loadLibrary(
- "fuzzed_data_provider_standalone", "/com/code_intelligence/jazzer/driver");
- } catch (Throwable t) {
- t.printStackTrace();
- System.exit(STATUS_OTHER_ERROR);
- }
- }
-
- public static void main(String[] args) {
- if (args.length < 2) {
- System.err.println("Usage: <fuzz target class> <input file path> <fuzzerInitialize args>...");
- System.exit(STATUS_OTHER_ERROR);
- }
- ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
-
- Class<?> fuzzTargetClass;
- try {
- fuzzTargetClass = Class.forName(args[0]);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- System.exit(STATUS_OTHER_ERROR);
- // Without this return the compiler sees fuzzTargetClass as possibly uninitialized.
- return;
- }
-
- String inputFilePath = args[1];
- byte[] input = loadInput(inputFilePath);
-
- String[] fuzzTargetArgs = Arrays.copyOfRange(args, 2, args.length);
- executeFuzzerInitialize(fuzzTargetClass, fuzzTargetArgs);
- executeFuzzTarget(fuzzTargetClass, input);
- }
-
- private static byte[] loadInput(String inputFilePath) {
- try {
- return Files.readAllBytes(Paths.get(inputFilePath));
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(STATUS_OTHER_ERROR);
- // Without this return the compiler sees loadInput as possibly not returning a value.
- return null;
- }
- }
-
- private static void executeFuzzerInitialize(Class<?> fuzzTarget, String[] args) {
- // public static void fuzzerInitialize()
- try {
- Method fuzzerInitialize = fuzzTarget.getMethod("fuzzerInitialize");
- fuzzerInitialize.invoke(null);
- return;
- } catch (Exception e) {
- handleInvokeException(e, fuzzTarget);
- }
- // public static void fuzzerInitialize(String[] args)
- try {
- Method fuzzerInitialize = fuzzTarget.getMethod("fuzzerInitialize", String[].class);
- fuzzerInitialize.invoke(null, (Object) args);
- } catch (Exception e) {
- handleInvokeException(e, fuzzTarget);
- }
- }
-
- public static void executeFuzzTarget(Class<?> fuzzTarget, byte[] input) {
- // public static void fuzzerTestOneInput(byte[] input)
- try {
- Method fuzzerTestOneInput = fuzzTarget.getMethod("fuzzerTestOneInput", byte[].class);
- fuzzerTestOneInput.invoke(null, (Object) input);
- return;
- } catch (Exception e) {
- handleInvokeException(e, fuzzTarget);
- }
- // public static void fuzzerTestOneInput(FuzzedDataProvider data)
- try {
- Method fuzzerTestOneInput =
- fuzzTarget.getMethod("fuzzerTestOneInput", FuzzedDataProvider.class);
- try (FuzzedDataProviderImpl fuzzedDataProvider = FuzzedDataProviderImpl.withJavaData(input)) {
- fuzzerTestOneInput.invoke(null, fuzzedDataProvider);
- }
- return;
- } catch (Exception e) {
- handleInvokeException(e, fuzzTarget);
- }
- System.err.printf("%s must define exactly one of the following two functions:%n"
- + " public static void fuzzerTestOneInput(byte[] ...)%n"
- + " public static void fuzzerTestOneInput(FuzzedDataProvider ...)%n"
- + "Note: Fuzz targets returning boolean are no longer supported; exceptions should%n"
- + "be thrown instead of returning true.%n",
- fuzzTarget.getName());
- System.exit(STATUS_OTHER_ERROR);
- }
-
- private static void handleInvokeException(Exception e, Class<?> fuzzTarget) {
- if (e instanceof NoSuchMethodException)
- return;
- if (e instanceof InvocationTargetException) {
- filterOutOwnStackTraceElements(e.getCause(), fuzzTarget);
- e.getCause().printStackTrace();
- System.exit(STATUS_FINDING);
- } else {
- e.printStackTrace();
- System.exit(STATUS_OTHER_ERROR);
- }
- }
-
- private static void filterOutOwnStackTraceElements(Throwable t, Class<?> fuzzTarget) {
- if (t.getCause() != null)
- filterOutOwnStackTraceElements(t.getCause(), fuzzTarget);
- if (t.getStackTrace() == null || t.getStackTrace().length == 0)
- return;
- StackTraceElement lowestFrame = t.getStackTrace()[t.getStackTrace().length - 1];
- if (!lowestFrame.getClassName().equals(Replayer.class.getName())
- || !lowestFrame.getMethodName().equals("main"))
- return;
- for (int i = t.getStackTrace().length - 1; i >= 0; i--) {
- StackTraceElement frame = t.getStackTrace()[i];
- if (frame.getClassName().equals(fuzzTarget.getName())
- && frame.getMethodName().equals("fuzzerTestOneInput")) {
- t.setStackTrace(Arrays.copyOfRange(t.getStackTrace(), 0, i + 1));
- break;
- }
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
deleted file mode 100644
index 0d8162d5..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel
+++ /dev/null
@@ -1,88 +0,0 @@
-load("@fmeum_rules_jni//jni:defs.bzl", "java_jni_library")
-
-java_jni_library(
- name = "fuzzed_data_provider",
- srcs = [
- "FuzzedDataProviderImpl.java",
- ],
- visibility = [
- "//agent/src/main/java/com/code_intelligence/jazzer/replay:__pkg__",
- "//agent/src/test/java/com/code_intelligence/jazzer/runtime:__pkg__",
- "//driver/src/main/java/com/code_intelligence/jazzer/driver:__pkg__",
- "//driver/src/main/native/com/code_intelligence/jazzer/driver:__pkg__",
- ],
- deps = [
- ":unsafe_provider",
- "//agent/src/main/java/com/code_intelligence/jazzer/api",
- ],
-)
-
-java_jni_library(
- name = "coverage_map",
- srcs = ["CoverageMap.java"],
- visibility = [
- "//agent/src/jmh/java/com/code_intelligence/jazzer/instrumentor:__pkg__",
- "//agent/src/main/java/com/code_intelligence/jazzer/instrumentor:__pkg__",
- "//driver/src/main/java/com/code_intelligence/jazzer/driver:__pkg__",
- "//driver/src/main/native/com/code_intelligence/jazzer/driver:__pkg__",
- "//driver/src/test:__subpackages__",
- ],
- deps = [
- ":unsafe_provider",
- ],
-)
-
-java_jni_library(
- name = "signal_handler",
- srcs = ["SignalHandler.java"],
- native_libs = ["//agent/src/main/native/com/code_intelligence/jazzer/runtime:jazzer_signal_handler"],
- visibility = [
- "//agent/src/main/native/com/code_intelligence/jazzer/runtime:__pkg__",
- "//driver/src/main/java/com/code_intelligence/jazzer/driver:__pkg__",
- ],
-)
-
-java_jni_library(
- name = "trace_data_flow_native_callbacks",
- srcs = ["TraceDataFlowNativeCallbacks.java"],
- visibility = [
- "//driver/src/main/native/com/code_intelligence/jazzer/driver:__pkg__",
- ],
- deps = [
- "//agent/src/main/java/com/code_intelligence/jazzer/utils",
- ],
-)
-
-java_library(
- name = "unsafe_provider",
- srcs = ["UnsafeProvider.java"],
- visibility = [
- "//driver/src:__subpackages__",
- "//sanitizers/src/main/java:__subpackages__",
- ],
-)
-
-java_library(
- name = "runtime",
- srcs = [
- "HardToCatchError.java",
- "JazzerInternal.java",
- "NativeLibHooks.java",
- "RecordingFuzzedDataProvider.java",
- "TraceCmpHooks.java",
- "TraceDivHooks.java",
- "TraceIndirHooks.java",
- ],
- visibility = ["//visibility:public"],
- runtime_deps = [
- ":signal_handler",
- "//agent/src/main/java/com/code_intelligence/jazzer/autofuzz",
- ],
- deps = [
- ":coverage_map",
- ":fuzzed_data_provider",
- ":trace_data_flow_native_callbacks",
- "//agent/src/main/java/com/code_intelligence/jazzer/api",
- "//agent/src/main/java/com/code_intelligence/jazzer/utils",
- ],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java
deleted file mode 100644
index 4069d25a..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/CoverageMap.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.github.fmeum.rules_jni.RulesJni;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import sun.misc.Unsafe;
-
-/**
- * Represents the Java view on a libFuzzer 8 bit counter coverage map. By using a direct ByteBuffer,
- * the counters are shared directly with native code.
- */
-final public class CoverageMap {
- static {
- RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver");
- }
-
- private static final String ENV_MAX_NUM_COUNTERS = "JAZZER_MAX_NUM_COUNTERS";
-
- private static final int MAX_NUM_COUNTERS = System.getenv(ENV_MAX_NUM_COUNTERS) != null
- ? Integer.parseInt(System.getenv(ENV_MAX_NUM_COUNTERS))
- : 1 << 20;
-
- private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe();
-
- static {
- if (UNSAFE == null) {
- System.out.println("ERROR: Failed to get Unsafe instance for CoverageMap.%n"
- + " Please file a bug at:%n"
- + " https://github.com/CodeIntelligenceTesting/jazzer/issues/new");
- System.exit(1);
- }
- }
-
- /**
- * The collection of coverage counters directly interacted with by classes that are instrumented
- * for coverage. The instrumentation assumes that this is always one contiguous block of memory,
- * so it is allocated once at maximum size. Using a larger number here increases the memory usage
- * of all fuzz targets, but has otherwise no impact on performance.
- */
- public static final long countersAddress = UNSAFE.allocateMemory(MAX_NUM_COUNTERS);
-
- static {
- UNSAFE.setMemory(countersAddress, MAX_NUM_COUNTERS, (byte) 0);
- initialize(countersAddress);
- }
-
- private static final int INITIAL_NUM_COUNTERS = 1 << 9;
-
- static {
- registerNewCounters(0, INITIAL_NUM_COUNTERS);
- }
-
- /**
- * The number of coverage counters that are currently registered with libFuzzer. This number grows
- * dynamically as classes are instrumented and should be kept as low as possible as libFuzzer has
- * to iterate over the whole map for every execution.
- */
- private static int currentNumCounters = INITIAL_NUM_COUNTERS;
-
- // Called via reflection.
- @SuppressWarnings("unused")
- public static void enlargeIfNeeded(int nextId) {
- int newNumCounters = currentNumCounters;
- while (nextId >= newNumCounters) {
- newNumCounters = 2 * newNumCounters;
- if (newNumCounters > MAX_NUM_COUNTERS) {
- System.out.printf("ERROR: Maximum number (%s) of coverage counters exceeded. Try to%n"
- + " limit the scope of a single fuzz target as much as possible to keep the%n"
- + " fuzzer fast.%n"
- + " If that is not possible, the maximum number of counters can be increased%n"
- + " via the %s environment variable.",
- MAX_NUM_COUNTERS, ENV_MAX_NUM_COUNTERS);
- System.exit(1);
- }
- }
- if (newNumCounters > currentNumCounters) {
- registerNewCounters(currentNumCounters, newNumCounters);
- currentNumCounters = newNumCounters;
- System.out.println("INFO: New number of coverage counters: " + currentNumCounters);
- }
- }
-
- // Called by the coverage instrumentation.
- @SuppressWarnings("unused")
- public static void recordCoverage(final int id) {
- final long address = countersAddress + id;
- final byte counter = UNSAFE.getByte(address);
- UNSAFE.putByte(address, (byte) (counter == -1 ? 1 : counter + 1));
- }
-
- public static Set<Integer> getCoveredIds() {
- Set<Integer> coveredIds = new HashSet<>();
- for (int id = 0; id < currentNumCounters; id++) {
- if (UNSAFE.getByte(countersAddress + id) > 0) {
- coveredIds.add(id);
- }
- }
- return Collections.unmodifiableSet(coveredIds);
- }
-
- public static void replayCoveredIds(Set<Integer> coveredIds) {
- for (int id : coveredIds) {
- UNSAFE.putByte(countersAddress + id, (byte) 1);
- }
- }
-
- // Returns the IDs of all blocks that have been covered in at least one run (not just the current
- // one).
- public static native int[] getEverCoveredIds();
-
- private static native void initialize(long countersAddress);
-
- private static native void registerNewCounters(int oldNumCounters, int newNumCounters);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java
deleted file mode 100644
index b7aad33e..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/FuzzedDataProviderImpl.java
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.api.FuzzedDataProvider;
-import com.github.fmeum.rules_jni.RulesJni;
-import sun.misc.Unsafe;
-
-public class FuzzedDataProviderImpl implements FuzzedDataProvider, AutoCloseable {
- static {
- // The replayer loads a standalone version of the FuzzedDataProvider.
- if (System.getProperty("jazzer.is_replayer") == null) {
- RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver");
- }
- nativeInit();
- }
-
- private static native void nativeInit();
-
- private final boolean ownsNativeData;
- private long originalDataPtr;
- private int originalRemainingBytes;
-
- // Accessed in fuzzed_data_provider.cpp.
- private long dataPtr;
- private int remainingBytes;
-
- private FuzzedDataProviderImpl(long dataPtr, int remainingBytes, boolean ownsNativeData) {
- this.ownsNativeData = ownsNativeData;
- this.originalDataPtr = dataPtr;
- this.dataPtr = dataPtr;
- this.originalRemainingBytes = remainingBytes;
- this.remainingBytes = remainingBytes;
- }
-
- /**
- * Creates a {@link FuzzedDataProvider} that consumes bytes from an already existing native array.
- *
- * <ul>
- * <li>{@link #close()} <b>must</b> be called on instances created with this method to free the
- * native copy of the Java
- * {@code byte} array.
- * <li>{@link #setNativeData(long, int)} <b>must not</b> be called on instances created with this
- * method.
- *
- * @param data the raw bytes used as input
- * @return a {@link FuzzedDataProvider} backed by {@code data}
- */
- public static FuzzedDataProviderImpl withJavaData(byte[] data) {
- return new FuzzedDataProviderImpl(allocateNativeCopy(data), data.length, true);
- }
-
- /**
- * Creates a {@link FuzzedDataProvider} that consumes bytes from an already existing native array.
- *
- * <p>The backing array can be set at any time using {@link #setNativeData(long, int)} and is
- * initially empty.
- *
- * @return a {@link FuzzedDataProvider} backed by an empty array.
- */
- public static FuzzedDataProviderImpl withNativeData() {
- return new FuzzedDataProviderImpl(0, 0, false);
- }
-
- /**
- * Replaces the current native backing array.
- *
- * <p><b>Must not</b> be called on instances created with {@link #withJavaData(byte[])}.
- *
- * @param dataPtr a native pointer to the new backing array
- * @param dataLength the length of the new backing array
- */
- public void setNativeData(long dataPtr, int dataLength) {
- this.originalDataPtr = dataPtr;
- this.dataPtr = dataPtr;
- this.originalRemainingBytes = dataLength;
- this.remainingBytes = dataLength;
- }
-
- /**
- * Resets the FuzzedDataProvider state to read from the beginning to the end of its current
- * backing item.
- */
- public void reset() {
- dataPtr = originalDataPtr;
- remainingBytes = originalRemainingBytes;
- }
-
- /**
- * Releases native memory allocated for this instance (if any).
- *
- * <p>While the instance should not be used after this method returns, no usage of {@link
- * FuzzedDataProvider} methods can result in memory corruption.
- */
- @Override
- public void close() {
- if (originalDataPtr == 0) {
- return;
- }
- if (ownsNativeData) {
- UNSAFE.freeMemory(originalDataPtr);
- }
- // Prevent double-frees and use-after-frees by effectively making all methods no-ops after
- // close() has been called.
- originalDataPtr = 0;
- originalRemainingBytes = 0;
- dataPtr = 0;
- remainingBytes = 0;
- }
-
- private static final Unsafe UNSAFE = UnsafeProvider.getUnsafe();
- private static final long BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
-
- private static long allocateNativeCopy(byte[] data) {
- long nativeCopy = UNSAFE.allocateMemory(data.length);
- UNSAFE.copyMemory(data, BYTE_ARRAY_OFFSET, null, nativeCopy, data.length);
- return nativeCopy;
- }
-
- @Override public native boolean consumeBoolean();
-
- @Override public native boolean[] consumeBooleans(int maxLength);
-
- @Override public native byte consumeByte();
-
- @Override
- public byte consumeByte(byte min, byte max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %d, max: %d)", min, max));
- }
- return consumeByteUnchecked(min, max);
- }
-
- @Override public native short consumeShort();
-
- @Override
- public short consumeShort(short min, short max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %d, max: %d)", min, max));
- }
- return consumeShortUnchecked(min, max);
- }
-
- @Override public native short[] consumeShorts(int maxLength);
-
- @Override public native int consumeInt();
-
- @Override
- public int consumeInt(int min, int max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %d, max: %d)", min, max));
- }
- return consumeIntUnchecked(min, max);
- }
-
- @Override public native int[] consumeInts(int maxLength);
-
- @Override public native long consumeLong();
-
- @Override
- public long consumeLong(long min, long max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %d, max: %d)", min, max));
- }
- return consumeLongUnchecked(min, max);
- }
-
- @Override public native long[] consumeLongs(int maxLength);
-
- @Override public native float consumeFloat();
-
- @Override public native float consumeRegularFloat();
-
- @Override
- public float consumeRegularFloat(float min, float max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %f, max: %f)", min, max));
- }
- return consumeRegularFloatUnchecked(min, max);
- }
-
- @Override public native float consumeProbabilityFloat();
-
- @Override public native double consumeDouble();
-
- @Override
- public double consumeRegularDouble(double min, double max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %f, max: %f)", min, max));
- }
- return consumeRegularDoubleUnchecked(min, max);
- }
-
- @Override public native double consumeRegularDouble();
-
- @Override public native double consumeProbabilityDouble();
-
- @Override public native char consumeChar();
-
- @Override
- public char consumeChar(char min, char max) {
- if (min > max) {
- throw new IllegalArgumentException(
- String.format("min must be <= max (got min: %c, max: %c)", min, max));
- }
- return consumeCharUnchecked(min, max);
- }
-
- @Override public native char consumeCharNoSurrogates();
-
- @Override public native String consumeAsciiString(int maxLength);
-
- @Override public native String consumeString(int maxLength);
-
- @Override public native String consumeRemainingAsAsciiString();
-
- @Override public native String consumeRemainingAsString();
-
- @Override public native byte[] consumeBytes(int maxLength);
-
- @Override public native byte[] consumeRemainingAsBytes();
-
- @Override public native int remainingBytes();
-
- private native byte consumeByteUnchecked(byte min, byte max);
- private native short consumeShortUnchecked(short min, short max);
- private native char consumeCharUnchecked(char min, char max);
- private native int consumeIntUnchecked(int min, int max);
- private native long consumeLongUnchecked(long min, long max);
- private native float consumeRegularFloatUnchecked(float min, float max);
- private native double consumeRegularDoubleUnchecked(double min, double max);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java
deleted file mode 100644
index cf136051..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/HardToCatchError.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import java.io.PrintStream;
-import java.io.PrintWriter;
-
-/**
- * An Error that rethrows itself when any of its getters is invoked.
- */
-public class HardToCatchError extends Error {
- public HardToCatchError() {
- super();
- }
-
- @Override
- public String getMessage() {
- throw this;
- }
-
- @Override
- public String getLocalizedMessage() {
- throw this;
- }
-
- @Override
- public synchronized Throwable initCause(Throwable cause) {
- throw this;
- }
-
- @Override
- public String toString() {
- throw this;
- }
-
- @Override
- public void printStackTrace() {
- throw this;
- }
-
- @Override
- public void printStackTrace(PrintStream s) {
- throw this;
- }
-
- @Override
- public void printStackTrace(PrintWriter s) {
- throw this;
- }
-
- @Override
- public StackTraceElement[] getStackTrace() {
- throw this;
- }
-
- @Override
- public int hashCode() {
- throw this;
- }
-
- @Override
- public boolean equals(Object obj) {
- throw this;
- }
-
- @Override
- public Object clone() {
- throw this;
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java
deleted file mode 100644
index 79c851ad..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/JazzerInternal.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import java.util.ArrayList;
-
-final public class JazzerInternal {
- private static final ArrayList<Runnable> ON_FUZZ_TARGET_READY_CALLBACKS = new ArrayList<>();
-
- public static Throwable lastFinding;
-
- // Accessed from api.Jazzer via reflection.
- public static void reportFindingFromHook(Throwable finding) {
- lastFinding = finding;
- // Throw an Error that is hard to catch (short of outright ignoring it) in order to quickly
- // terminate the execution of the fuzz target. The finding will be reported as soon as the fuzz
- // target returns even if this Error is swallowed.
- throw new HardToCatchError();
- }
-
- public static void registerOnFuzzTargetReadyCallback(Runnable callback) {
- ON_FUZZ_TARGET_READY_CALLBACKS.add(callback);
- }
-
- public static void onFuzzTargetReady(String fuzzTargetClass) {
- ON_FUZZ_TARGET_READY_CALLBACKS.forEach(Runnable::run);
- ON_FUZZ_TARGET_READY_CALLBACKS.clear();
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java
deleted file mode 100644
index 495cad7c..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/NativeLibHooks.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.api.HookType;
-import com.code_intelligence.jazzer.api.MethodHook;
-import java.lang.invoke.MethodHandle;
-
-@SuppressWarnings("unused")
-final public class NativeLibHooks {
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Runtime",
- targetMethod = "loadLibrary", targetMethodDescriptor = "(Ljava/lang/String;)V")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.System",
- targetMethod = "loadLibrary", targetMethodDescriptor = "(Ljava/lang/String;)V")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Runtime", targetMethod = "load",
- targetMethodDescriptor = "(Ljava/lang/String;)V")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.System", targetMethod = "load",
- targetMethodDescriptor = "(Ljava/lang/String;)V")
- public static void
- loadLibraryHook(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
- TraceDataFlowNativeCallbacks.handleLibraryLoad();
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProvider.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProvider.java
deleted file mode 100644
index 4eb80222..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/RecordingFuzzedDataProvider.java
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.api.FuzzedDataProvider;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.util.ArrayList;
-import java.util.Base64;
-
-// Wraps the native FuzzedDataProviderImpl and serializes all its return values
-// into a Base64-encoded string.
-public final class RecordingFuzzedDataProvider implements FuzzedDataProvider {
- private final FuzzedDataProvider target;
- private final ArrayList<Object> recordedReplies = new ArrayList<>();
-
- private RecordingFuzzedDataProvider(FuzzedDataProvider target) {
- this.target = target;
- }
-
- public static FuzzedDataProvider makeFuzzedDataProviderProxy(FuzzedDataProvider target) {
- return new RecordingFuzzedDataProvider(target);
- }
-
- public static String serializeFuzzedDataProviderProxy(FuzzedDataProvider proxy)
- throws IOException {
- return ((RecordingFuzzedDataProvider) proxy).serialize();
- }
-
- private <T> T recordAndReturn(T object) {
- recordedReplies.add(object);
- return object;
- }
-
- private String serialize() throws IOException {
- byte[] rawOut;
- try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
- try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) {
- objectStream.writeObject(recordedReplies);
- }
- rawOut = byteStream.toByteArray();
- }
- return Base64.getEncoder().encodeToString(rawOut);
- }
-
- @Override
- public boolean consumeBoolean() {
- return recordAndReturn(target.consumeBoolean());
- }
-
- @Override
- public boolean[] consumeBooleans(int maxLength) {
- return recordAndReturn(target.consumeBooleans(maxLength));
- }
-
- @Override
- public byte consumeByte() {
- return recordAndReturn(target.consumeByte());
- }
-
- @Override
- public byte consumeByte(byte min, byte max) {
- return recordAndReturn(target.consumeByte(min, max));
- }
-
- @Override
- public byte[] consumeBytes(int maxLength) {
- return recordAndReturn(target.consumeBytes(maxLength));
- }
-
- @Override
- public byte[] consumeRemainingAsBytes() {
- return recordAndReturn(target.consumeRemainingAsBytes());
- }
-
- @Override
- public short consumeShort() {
- return recordAndReturn(target.consumeShort());
- }
-
- @Override
- public short consumeShort(short min, short max) {
- return recordAndReturn(target.consumeShort(min, max));
- }
-
- @Override
- public short[] consumeShorts(int maxLength) {
- return recordAndReturn(target.consumeShorts(maxLength));
- }
-
- @Override
- public int consumeInt() {
- return recordAndReturn(target.consumeInt());
- }
-
- @Override
- public int consumeInt(int min, int max) {
- return recordAndReturn(target.consumeInt(min, max));
- }
-
- @Override
- public int[] consumeInts(int maxLength) {
- return recordAndReturn(target.consumeInts(maxLength));
- }
-
- @Override
- public long consumeLong() {
- return recordAndReturn(target.consumeLong());
- }
-
- @Override
- public long consumeLong(long min, long max) {
- return recordAndReturn(target.consumeLong(min, max));
- }
-
- @Override
- public long[] consumeLongs(int maxLength) {
- return recordAndReturn(target.consumeLongs(maxLength));
- }
-
- @Override
- public float consumeFloat() {
- return recordAndReturn(target.consumeFloat());
- }
-
- @Override
- public float consumeRegularFloat() {
- return recordAndReturn(target.consumeRegularFloat());
- }
-
- @Override
- public float consumeRegularFloat(float min, float max) {
- return recordAndReturn(target.consumeRegularFloat(min, max));
- }
-
- @Override
- public float consumeProbabilityFloat() {
- return recordAndReturn(target.consumeProbabilityFloat());
- }
-
- @Override
- public double consumeDouble() {
- return recordAndReturn(target.consumeDouble());
- }
-
- @Override
- public double consumeRegularDouble() {
- return recordAndReturn(target.consumeRegularDouble());
- }
-
- @Override
- public double consumeRegularDouble(double min, double max) {
- return recordAndReturn(target.consumeRegularDouble(min, max));
- }
-
- @Override
- public double consumeProbabilityDouble() {
- return recordAndReturn(target.consumeProbabilityDouble());
- }
-
- @Override
- public char consumeChar() {
- return recordAndReturn(target.consumeChar());
- }
-
- @Override
- public char consumeChar(char min, char max) {
- return recordAndReturn(target.consumeChar(min, max));
- }
-
- @Override
- public char consumeCharNoSurrogates() {
- return recordAndReturn(target.consumeCharNoSurrogates());
- }
-
- @Override
- public String consumeString(int maxLength) {
- return recordAndReturn(target.consumeString(maxLength));
- }
-
- @Override
- public String consumeRemainingAsString() {
- return recordAndReturn(target.consumeRemainingAsString());
- }
-
- @Override
- public String consumeAsciiString(int maxLength) {
- return recordAndReturn(target.consumeAsciiString(maxLength));
- }
-
- @Override
- public String consumeRemainingAsAsciiString() {
- return recordAndReturn(target.consumeRemainingAsAsciiString());
- }
-
- @Override
- public int remainingBytes() {
- return recordAndReturn(target.remainingBytes());
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/SignalHandler.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/SignalHandler.java
deleted file mode 100644
index 49ee80c8..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/SignalHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.github.fmeum.rules_jni.RulesJni;
-import sun.misc.Signal;
-
-public final class SignalHandler {
- static {
- RulesJni.loadLibrary("jazzer_signal_handler", SignalHandler.class);
- Signal.handle(new Signal("INT"), sig -> handleInterrupt());
- }
-
- public static void initialize() {
- // Implicitly runs the static initializer.
- }
-
- private static native void handleInterrupt();
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java
deleted file mode 100644
index 37e8eaeb..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java
+++ /dev/null
@@ -1,347 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.api.HookType;
-import com.code_intelligence.jazzer.api.MethodHook;
-import java.lang.invoke.MethodHandle;
-import java.util.Arrays;
-import java.util.ConcurrentModificationException;
-import java.util.Map;
-import java.util.TreeMap;
-
-@SuppressWarnings("unused")
-final public class TraceCmpHooks {
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Byte", targetMethod = "compare",
- targetMethodDescriptor = "(BB)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Byte",
- targetMethod = "compareUnsigned", targetMethodDescriptor = "(BB)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Short", targetMethod = "compare",
- targetMethodDescriptor = "(SS)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Short",
- targetMethod = "compareUnsigned", targetMethodDescriptor = "(SS)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer",
- targetMethod = "compare", targetMethodDescriptor = "(II)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer",
- targetMethod = "compareUnsigned", targetMethodDescriptor = "(II)I")
- public static void
- integerCompare(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
- TraceDataFlowNativeCallbacks.traceCmpInt((int) arguments[0], (int) arguments[1], hookId);
- }
-
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Byte",
- targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Byte;)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Short",
- targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Short;)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer",
- targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Integer;)I")
- public static void
- integerCompareTo(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
- TraceDataFlowNativeCallbacks.traceCmpInt((int) thisObject, (int) arguments[0], hookId);
- }
-
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long", targetMethod = "compare",
- targetMethodDescriptor = "(JJ)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long",
- targetMethod = "compareUnsigned", targetMethodDescriptor = "(JJ)I")
- public static void
- longCompare(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
- TraceDataFlowNativeCallbacks.traceCmpLong((long) arguments[0], (long) arguments[1], hookId);
- }
-
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long",
- targetMethod = "compareTo", targetMethodDescriptor = "(Ljava/lang/Long;)I")
- public static void
- longCompareTo(MethodHandle method, Long thisObject, Object[] arguments, int hookId) {
- TraceDataFlowNativeCallbacks.traceCmpLong(thisObject, (long) arguments[0], hookId);
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "equals")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String",
- targetMethod = "equalsIgnoreCase")
- public static void
- equals(
- MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (arguments[0] instanceof String && !returnValue) {
- // The precise value of the result of the comparison is not used by libFuzzer as long as it is
- // non-zero.
- TraceDataFlowNativeCallbacks.traceStrcmp(thisObject, (String) arguments[0], 1, hookId);
- }
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.Object", targetMethod = "equals")
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.CharSequence", targetMethod = "equals")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.Number", targetMethod = "equals")
- public static void
- genericEquals(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (!returnValue && arguments[0] != null && thisObject.getClass() == arguments[0].getClass()) {
- TraceDataFlowNativeCallbacks.traceGenericCmp(thisObject, arguments[0], hookId);
- }
- }
-
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "compareTo")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String",
- targetMethod = "compareToIgnoreCase")
- public static void
- compareTo(
- MethodHandle method, String thisObject, Object[] arguments, int hookId, Integer returnValue) {
- if (arguments[0] instanceof String && returnValue != 0) {
- TraceDataFlowNativeCallbacks.traceStrcmp(
- thisObject, (String) arguments[0], returnValue, hookId);
- }
- }
-
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "contentEquals")
- public static void
- contentEquals(
- MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (arguments[0] instanceof CharSequence && !returnValue) {
- TraceDataFlowNativeCallbacks.traceStrcmp(
- thisObject, ((CharSequence) arguments[0]).toString(), 1, hookId);
- }
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String",
- targetMethod = "regionMatches", targetMethodDescriptor = "(ZILjava/lang/String;II)Z")
- public static void
- regionsMatches5(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (!returnValue) {
- int toffset = (int) arguments[1];
- String other = (String) arguments[2];
- int ooffset = (int) arguments[3];
- int len = (int) arguments[4];
- regionMatchesInternal((String) thisObject, toffset, other, ooffset, len, hookId);
- }
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String",
- targetMethod = "regionMatches", targetMethodDescriptor = "(ILjava/lang/String;II)Z")
- public static void
- regionMatches4(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (!returnValue) {
- int toffset = (int) arguments[0];
- String other = (String) arguments[1];
- int ooffset = (int) arguments[2];
- int len = (int) arguments[3];
- regionMatchesInternal((String) thisObject, toffset, other, ooffset, len, hookId);
- }
- }
-
- private static void regionMatchesInternal(
- String thisString, int toffset, String other, int ooffset, int len, int hookId) {
- if (toffset < 0 || ooffset < 0)
- return;
- int cappedThisStringEnd = Math.min(toffset + len, thisString.length());
- int cappedOtherStringEnd = Math.min(ooffset + len, other.length());
- String thisPart = thisString.substring(toffset, cappedThisStringEnd);
- String otherPart = other.substring(ooffset, cappedOtherStringEnd);
- TraceDataFlowNativeCallbacks.traceStrcmp(thisPart, otherPart, 1, hookId);
- }
-
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "contains")
- public static void
- contains(
- MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (arguments[0] instanceof CharSequence && !returnValue) {
- TraceDataFlowNativeCallbacks.traceStrstr(
- thisObject, ((CharSequence) arguments[0]).toString(), hookId);
- }
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "indexOf")
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "lastIndexOf")
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.StringBuffer", targetMethod = "indexOf")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.StringBuffer",
- targetMethod = "lastIndexOf")
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.StringBuilder", targetMethod = "indexOf")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.StringBuilder",
- targetMethod = "lastIndexOf")
- public static void
- indexOf(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer returnValue) {
- if (arguments[0] instanceof String && returnValue == -1) {
- TraceDataFlowNativeCallbacks.traceStrstr(
- thisObject.toString(), (String) arguments[0], hookId);
- }
- }
-
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "startsWith")
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "endsWith")
- public static void
- startsWith(
- MethodHandle method, String thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (!returnValue) {
- TraceDataFlowNativeCallbacks.traceStrstr(thisObject, (String) arguments[0], hookId);
- }
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.String", targetMethod = "replace",
- targetMethodDescriptor =
- "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;")
- public static void
- replace(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, String returnValue) {
- String original = (String) thisObject;
- // Report only if the replacement was not successful.
- if (original.equals(returnValue)) {
- String target = arguments[0].toString();
- TraceDataFlowNativeCallbacks.traceStrstr(original, target, hookId);
- }
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "equals",
- targetMethodDescriptor = "([B[B)Z")
- public static void
- arraysEquals(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (returnValue)
- return;
- byte[] first = (byte[]) arguments[0];
- byte[] second = (byte[]) arguments[1];
- TraceDataFlowNativeCallbacks.traceMemcmp(first, second, 1, hookId);
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "equals",
- targetMethodDescriptor = "([BII[BII)Z")
- public static void
- arraysEqualsRange(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean returnValue) {
- if (returnValue)
- return;
- byte[] first =
- Arrays.copyOfRange((byte[]) arguments[0], (int) arguments[1], (int) arguments[2]);
- byte[] second =
- Arrays.copyOfRange((byte[]) arguments[3], (int) arguments[4], (int) arguments[5]);
- TraceDataFlowNativeCallbacks.traceMemcmp(first, second, 1, hookId);
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "compare",
- targetMethodDescriptor = "([B[B)I")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays",
- targetMethod = "compareUnsigned", targetMethodDescriptor = "([B[B)I")
- public static void
- arraysCompare(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer returnValue) {
- if (returnValue == 0)
- return;
- byte[] first = (byte[]) arguments[0];
- byte[] second = (byte[]) arguments[1];
- TraceDataFlowNativeCallbacks.traceMemcmp(first, second, returnValue, hookId);
- }
-
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays", targetMethod = "compare",
- targetMethodDescriptor = "([BII[BII)I")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Arrays",
- targetMethod = "compareUnsigned", targetMethodDescriptor = "([BII[BII)I")
- public static void
- arraysCompareRange(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Integer returnValue) {
- if (returnValue == 0)
- return;
- byte[] first =
- Arrays.copyOfRange((byte[]) arguments[0], (int) arguments[1], (int) arguments[2]);
- byte[] second =
- Arrays.copyOfRange((byte[]) arguments[3], (int) arguments[4], (int) arguments[5]);
- TraceDataFlowNativeCallbacks.traceMemcmp(first, second, returnValue, hookId);
- }
-
- // The maximal number of elements of a non-TreeMap Map that will be sorted and searched for the
- // key closest to the current lookup key in the mapGet hook.
- private static final int MAX_NUM_KEYS_TO_ENUMERATE = 100;
-
- @SuppressWarnings({"rawtypes", "unchecked"})
- @MethodHook(type = HookType.AFTER, targetClassName = "java.util.Map", targetMethod = "get")
- public static void mapGet(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) {
- if (returnValue != null)
- return;
- if (thisObject == null)
- return;
- final Map map = (Map) thisObject;
- if (map.size() == 0)
- return;
- final Object currentKey = arguments[0];
- if (currentKey == null)
- return;
- // Find two valid map keys that bracket currentKey.
- // This is a generalization of libFuzzer's __sanitizer_cov_trace_switch:
- // https://github.com/llvm/llvm-project/blob/318942de229beb3b2587df09e776a50327b5cef0/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L564
- Object lowerBoundKey = null;
- Object upperBoundKey = null;
- try {
- if (map instanceof TreeMap) {
- final TreeMap treeMap = (TreeMap) map;
- try {
- lowerBoundKey = treeMap.floorKey(currentKey);
- upperBoundKey = treeMap.ceilingKey(currentKey);
- } catch (ClassCastException ignored) {
- // Can be thrown by floorKey and ceilingKey if currentKey is of a type that can't be
- // compared to the maps keys.
- }
- } else if (currentKey instanceof Comparable) {
- final Comparable comparableCurrentKey = (Comparable) currentKey;
- // Find two keys that bracket currentKey.
- // Note: This is not deterministic if map.size() > MAX_NUM_KEYS_TO_ENUMERATE.
- int enumeratedKeys = 0;
- for (Object validKey : map.keySet()) {
- if (!(validKey instanceof Comparable))
- continue;
- final Comparable comparableValidKey = (Comparable) validKey;
- // If the key sorts lower than the non-existing key, but higher than the current lower
- // bound, update the lower bound and vice versa for the upper bound.
- try {
- if (comparableValidKey.compareTo(comparableCurrentKey) < 0
- && (lowerBoundKey == null || comparableValidKey.compareTo(lowerBoundKey) > 0)) {
- lowerBoundKey = validKey;
- }
- if (comparableValidKey.compareTo(comparableCurrentKey) > 0
- && (upperBoundKey == null || comparableValidKey.compareTo(upperBoundKey) < 0)) {
- upperBoundKey = validKey;
- }
- } catch (ClassCastException ignored) {
- // Can be thrown by floorKey and ceilingKey if currentKey is of a type that can't be
- // compared to the maps keys.
- }
- if (enumeratedKeys++ > MAX_NUM_KEYS_TO_ENUMERATE)
- break;
- }
- }
- } catch (ConcurrentModificationException ignored) {
- // map was modified by another thread, skip this invocation
- return;
- }
- // Modify the hook ID so that compares against distinct valid keys are traced separately.
- if (lowerBoundKey != null) {
- TraceDataFlowNativeCallbacks.traceGenericCmp(
- currentKey, lowerBoundKey, hookId + lowerBoundKey.hashCode());
- }
- if (upperBoundKey != null) {
- TraceDataFlowNativeCallbacks.traceGenericCmp(
- currentKey, upperBoundKey, hookId + upperBoundKey.hashCode());
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java
deleted file mode 100644
index 821ade0d..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDataFlowNativeCallbacks.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.utils.Utils;
-import com.github.fmeum.rules_jni.RulesJni;
-import java.lang.reflect.Executable;
-import java.nio.charset.Charset;
-
-@SuppressWarnings("unused")
-final public class TraceDataFlowNativeCallbacks {
- static {
- RulesJni.loadLibrary("jazzer_driver", "/com/code_intelligence/jazzer/driver");
- }
-
- // Note that we are not encoding as modified UTF-8 here: The FuzzedDataProvider transparently
- // converts CESU8 into modified UTF-8 by coding null bytes on two bytes. Since the fuzzer is more
- // likely to insert literal null bytes, having both the fuzzer input and the reported string
- // comparisons be CESU8 should perform even better than the current implementation using modified
- // UTF-8.
- private static final Charset FUZZED_DATA_CHARSET = Charset.forName("CESU8");
-
- public static native void traceMemcmp(byte[] b1, byte[] b2, int result, int pc);
-
- public static void traceStrcmp(String s1, String s2, int result, int pc) {
- traceMemcmp(encodeForLibFuzzer(s1), encodeForLibFuzzer(s2), result, pc);
- }
-
- public static void traceStrstr(String s1, String s2, int pc) {
- traceStrstr0(encodeForLibFuzzer(s2), pc);
- }
-
- public static void traceReflectiveCall(Executable callee, int pc) {
- String className = callee.getDeclaringClass().getCanonicalName();
- String executableName = callee.getName();
- String descriptor = Utils.getDescriptor(callee);
- tracePcIndir(Utils.simpleFastHash(className, executableName, descriptor), pc);
- }
-
- public static int traceCmpLongWrapper(long arg1, long arg2, int pc) {
- traceCmpLong(arg1, arg2, pc);
- // Long.compare serves as a substitute for the lcmp opcode, which can't be used directly
- // as the stack layout required for the call can't be achieved without local variables.
- return Long.compare(arg1, arg2);
- }
-
- // The caller has to ensure that arg1 and arg2 have the same class.
- public static void traceGenericCmp(Object arg1, Object arg2, int pc) {
- if (arg1 instanceof CharSequence) {
- traceStrcmp(arg1.toString(), arg2.toString(), 1, pc);
- } else if (arg1 instanceof Integer) {
- traceCmpInt((int) arg1, (int) arg2, pc);
- } else if (arg1 instanceof Long) {
- traceCmpLong((long) arg1, (long) arg2, pc);
- } else if (arg1 instanceof Short) {
- traceCmpInt((short) arg1, (short) arg2, pc);
- } else if (arg1 instanceof Byte) {
- traceCmpInt((byte) arg1, (byte) arg2, pc);
- } else if (arg1 instanceof Character) {
- traceCmpInt((char) arg1, (char) arg2, pc);
- } else if (arg1 instanceof Number) {
- traceCmpLong(((Number) arg1).longValue(), ((Number) arg2).longValue(), pc);
- } else if (arg1 instanceof byte[]) {
- traceMemcmp((byte[]) arg1, (byte[]) arg2, 1, pc);
- }
- }
-
- /* trace-cmp */
- public static native void traceCmpInt(int arg1, int arg2, int pc);
- public static native void traceConstCmpInt(int arg1, int arg2, int pc);
- public static native void traceCmpLong(long arg1, long arg2, int pc);
- public static native void traceSwitch(long val, long[] cases, int pc);
- /* trace-div */
- public static native void traceDivInt(int val, int pc);
- public static native void traceDivLong(long val, int pc);
- /* trace-gep */
- public static native void traceGep(long val, int pc);
- /* indirect-calls */
- public static native void tracePcIndir(int callee, int caller);
-
- public static native void handleLibraryLoad();
-
- private static byte[] encodeForLibFuzzer(String str) {
- // libFuzzer string hooks only ever consume the first 64 bytes, so we can definitely cut the
- // string off after 64 characters.
- return str.substring(0, Math.min(str.length(), 64)).getBytes(FUZZED_DATA_CHARSET);
- }
-
- private static native void traceStrstr0(byte[] needle, int pc);
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java
deleted file mode 100644
index c4991eb5..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceDivHooks.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.api.HookType;
-import com.code_intelligence.jazzer.api.MethodHook;
-import java.lang.invoke.MethodHandle;
-
-@SuppressWarnings("unused")
-final public class TraceDivHooks {
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer",
- targetMethod = "divideUnsigned", targetMethodDescriptor = "(II)I")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Integer",
- targetMethod = "remainderUnsigned", targetMethodDescriptor = "(II)I")
- public static void
- intUnsignedDivide(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
- // Since the arguments are to be treated as unsigned integers we need a long to fit the
- // divisor.
- TraceDataFlowNativeCallbacks.traceDivLong(Integer.toUnsignedLong((int) arguments[1]), hookId);
- }
-
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long",
- targetMethod = "divideUnsigned", targetMethodDescriptor = "(JJ)J")
- @MethodHook(type = HookType.BEFORE, targetClassName = "java.lang.Long",
- targetMethod = "remainderUnsigned", targetMethodDescriptor = "(JJ)J")
- public static void
- longUnsignedDivide(MethodHandle method, Object thisObject, Object[] arguments, int hookId) {
- long divisor = (long) arguments[1];
- // Run the callback only if the divisor, which is regarded as an unsigned long, fits in a
- // signed long, i.e., does not have the sign bit set.
- if (divisor > 0) {
- TraceDataFlowNativeCallbacks.traceDivLong(divisor, hookId);
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java
deleted file mode 100644
index 897ede6c..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/TraceIndirHooks.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import com.code_intelligence.jazzer.api.HookType;
-import com.code_intelligence.jazzer.api.MethodHook;
-import java.lang.invoke.MethodHandle;
-import java.lang.reflect.Executable;
-
-@SuppressWarnings("unused")
-final public class TraceIndirHooks {
- // The reflection hook is of type AFTER as it should only report calls that did not fail because
- // of incorrect arguments passed.
- @MethodHook(
- type = HookType.AFTER, targetClassName = "java.lang.reflect.Method", targetMethod = "invoke")
- @MethodHook(type = HookType.AFTER, targetClassName = "java.lang.reflect.Constructor",
- targetMethod = "newInstance")
- public static void
- methodInvoke(
- MethodHandle method, Object thisObject, Object[] arguments, int hookId, Object returnValue) {
- TraceDataFlowNativeCallbacks.traceReflectiveCall((Executable) thisObject, hookId);
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/runtime/UnsafeProvider.java b/agent/src/main/java/com/code_intelligence/jazzer/runtime/UnsafeProvider.java
deleted file mode 100644
index 81f2a208..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/runtime/UnsafeProvider.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2022 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.runtime;
-
-import java.lang.reflect.Field;
-import sun.misc.Unsafe;
-
-public final class UnsafeProvider {
- private static final Unsafe UNSAFE = getUnsafeInternal();
-
- public static Unsafe getUnsafe() {
- return UNSAFE;
- }
-
- private static Unsafe getUnsafeInternal() {
- try {
- // The Java agent is loaded by the bootstrap class loader and should thus
- // pass the security checks in getUnsafe.
- return Unsafe.getUnsafe();
- } catch (Throwable unused) {
- // If not running as an agent, use the classical reflection trick to get an Unsafe instance,
- // taking into account that the private field may have a name other than "theUnsafe":
- // https://android.googlesource.com/platform/libcore/+/gingerbread/luni/src/main/java/sun/misc/Unsafe.java#32
- try {
- for (Field f : Unsafe.class.getDeclaredFields()) {
- if (f.getType() == Unsafe.class) {
- f.setAccessible(true);
- return (Unsafe) f.get(null);
- }
- }
- return null;
- } catch (Throwable t) {
- t.printStackTrace();
- return null;
- }
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/utils/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/utils/BUILD.bazel
deleted file mode 100644
index 10e3477c..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/utils/BUILD.bazel
+++ /dev/null
@@ -1,15 +0,0 @@
-load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
-
-kt_jvm_library(
- name = "utils",
- srcs = [
- "ClassNameGlobber.kt",
- "ExceptionUtils.kt",
- "ManifestUtils.kt",
- "Utils.kt",
- ],
- visibility = ["//visibility:public"],
- deps = [
- "//agent/src/main/java/com/code_intelligence/jazzer/api",
- ],
-)
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/utils/ClassNameGlobber.kt b/agent/src/main/java/com/code_intelligence/jazzer/utils/ClassNameGlobber.kt
deleted file mode 100644
index 44249c81..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/utils/ClassNameGlobber.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.utils
-
-private val BASE_INCLUDED_CLASS_NAME_GLOBS = listOf(
- "**", // everything
-)
-
-// We use both a strong indicator for running as a Bazel test together with an indicator for a
-// Bazel coverage run to rule out false positives.
-private val IS_BAZEL_COVERAGE_RUN = System.getenv("TEST_UNDECLARED_OUTPUTS_DIR") != null &&
- System.getenv("COVERAGE_DIR") != null
-
-private val ADDITIONAL_EXCLUDED_NAME_GLOBS_FOR_BAZEL_COVERAGE = listOf(
- "com.google.testing.coverage.**",
- "org.jacoco.**",
-)
-
-private val BASE_EXCLUDED_CLASS_NAME_GLOBS = listOf(
- // JDK internals
- "\\[**", // array types
- "java.**",
- "javax.**",
- "jdk.**",
- "sun.**",
- "com.sun.**", // package for Proxy objects
- // Azul JDK internals
- "com.azul.tooling.**",
- // Kotlin internals
- "kotlin.**",
- // Jazzer internals
- "com.code_intelligence.jazzer.**",
- "jaz.Ter", // safe companion of the honeypot class used by sanitizers
- "jaz.Zer", // honeypot class used by sanitizers
-) + if (IS_BAZEL_COVERAGE_RUN) ADDITIONAL_EXCLUDED_NAME_GLOBS_FOR_BAZEL_COVERAGE else listOf()
-
-class ClassNameGlobber(includes: List<String>, excludes: List<String>) {
- // If no include globs are provided, start with all classes.
- private val includeMatchers = includes.ifEmpty { BASE_INCLUDED_CLASS_NAME_GLOBS }
- .map(::SimpleGlobMatcher)
-
- // If no include globs are provided, additionally exclude stdlib classes as well as our own classes.
- private val excludeMatchers = (if (includes.isEmpty()) BASE_EXCLUDED_CLASS_NAME_GLOBS + excludes else excludes)
- .map(::SimpleGlobMatcher)
-
- fun includes(className: String): Boolean {
- return includeMatchers.any { it.matches(className) } && excludeMatchers.none { it.matches(className) }
- }
-}
-
-class SimpleGlobMatcher(val glob: String) {
- private enum class Type {
- // foo.bar (matches foo.bar only)
- FULL_MATCH,
- // foo.** (matches foo.bar and foo.bar.baz)
- PATH_WILDCARD_SUFFIX,
- // foo.* (matches foo.bar, but not foo.bar.baz)
- SEGMENT_WILDCARD_SUFFIX,
- }
-
- private val type: Type
- private val prefix: String
-
- init {
- // Remain compatible with globs such as "\\[" that use escaping.
- val pattern = glob.replace("\\", "")
- when {
- !pattern.contains('*') -> {
- type = Type.FULL_MATCH
- prefix = pattern
- }
- // Ends with "**" and contains no other '*'.
- pattern.endsWith("**") && pattern.indexOf('*') == pattern.length - 2 -> {
- type = Type.PATH_WILDCARD_SUFFIX
- prefix = pattern.removeSuffix("**")
- }
- // Ends with "*" and contains no other '*'.
- pattern.endsWith('*') && pattern.indexOf('*') == pattern.length - 1 -> {
- type = Type.SEGMENT_WILDCARD_SUFFIX
- prefix = pattern.removeSuffix("*")
- }
- else -> throw IllegalArgumentException(
- "Unsupported glob pattern (only foo.bar, foo.* and foo.** are supported): $pattern"
- )
- }
- }
-
- /**
- * Checks whether [maybeInternalClassName], which may be internal (foo/bar) or not (foo.bar), matches [glob].
- */
- fun matches(maybeInternalClassName: String): Boolean {
- val className = maybeInternalClassName.replace('/', '.')
- return when (type) {
- Type.FULL_MATCH -> className == prefix
- Type.PATH_WILDCARD_SUFFIX -> className.startsWith(prefix)
- Type.SEGMENT_WILDCARD_SUFFIX -> {
- // className starts with prefix and contains no further '.'.
- className.startsWith(prefix) &&
- className.indexOf('.', startIndex = prefix.length) == -1
- }
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/utils/ExceptionUtils.kt b/agent/src/main/java/com/code_intelligence/jazzer/utils/ExceptionUtils.kt
deleted file mode 100644
index 30f6fb30..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/utils/ExceptionUtils.kt
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-@file:JvmName("ExceptionUtils")
-
-package com.code_intelligence.jazzer.utils
-
-import com.code_intelligence.jazzer.api.FuzzerSecurityIssueLow
-import java.lang.management.ManagementFactory
-import java.nio.ByteBuffer
-import java.security.MessageDigest
-
-private fun hash(throwable: Throwable, passToRootCause: Boolean): ByteArray =
- MessageDigest.getInstance("SHA-256").run {
- // It suffices to hash the stack trace of the deepest cause as the higher-level causes only
- // contain part of the stack trace (plus possibly a different exception type).
- var rootCause = throwable
- if (passToRootCause) {
- while (true) {
- rootCause = rootCause.cause ?: break
- }
- }
- update(rootCause.javaClass.name.toByteArray())
- for (element in rootCause.stackTrace) {
- update(element.toString().toByteArray())
- }
- if (throwable.suppressed.isNotEmpty()) {
- update("suppressed".toByteArray())
- for (suppressed in throwable.suppressed) {
- update(hash(suppressed, passToRootCause))
- }
- }
- digest()
- }
-
-/**
- * Computes a hash of the stack trace of [throwable] without messages.
- *
- * The hash can be used to deduplicate stack traces obtained on crashes. By not including the
- * messages, this hash should not depend on the precise crashing input.
- */
-fun computeDedupToken(throwable: Throwable): Long {
- var passToRootCause = true
- if (throwable is FuzzerSecurityIssueLow && throwable.cause is StackOverflowError) {
- // Special handling for StackOverflowErrors as processed by preprocessThrowable:
- // Only consider the repeated part of the stack trace and ignore the original stack trace in
- // the cause.
- passToRootCause = false
- }
- return ByteBuffer.wrap(hash(throwable, passToRootCause)).long
-}
-
-/**
- * Annotates [throwable] with a severity and additional information if it represents a bug type
- * that has security content.
- */
-fun preprocessThrowable(throwable: Throwable): Throwable = when (throwable) {
- is StackOverflowError -> {
- // StackOverflowErrors are hard to deduplicate as the top-most stack frames vary wildly,
- // whereas the information that is most useful for deduplication detection is hidden in the
- // rest of the (truncated) stack frame.
- // We heuristically clean up the stack trace by taking the elements from the bottom and
- // stopping at the first repetition of a frame. The original error is returned as the cause
- // unchanged.
- val observedFrames = mutableSetOf<StackTraceElement>()
- val bottomFramesWithoutRepetition = throwable.stackTrace.takeLastWhile { frame ->
- (frame !in observedFrames).also { observedFrames.add(frame) }
- }
- FuzzerSecurityIssueLow("Stack overflow (use '${getReproducingXssArg()}' to reproduce)", throwable).apply {
- stackTrace = bottomFramesWithoutRepetition.toTypedArray()
- }
- }
- is OutOfMemoryError -> stripOwnStackTrace(
- FuzzerSecurityIssueLow(
- "Out of memory (use '${getReproducingXmxArg()}' to reproduce)", throwable
- )
- )
- is VirtualMachineError -> stripOwnStackTrace(FuzzerSecurityIssueLow(throwable))
- else -> throwable
-}
-
-/**
- * Strips the stack trace of [throwable] (e.g. because it was created in a utility method), but not
- * the stack traces of its causes.
- */
-private fun stripOwnStackTrace(throwable: Throwable) = throwable.apply {
- stackTrace = emptyArray()
-}
-
-/**
- * Returns a valid `-Xmx` JVM argument that sets the stack size to a value with which [StackOverflowError] findings can
- * be reproduced, assuming the environment is sufficiently similar (e.g. OS and JVM version).
- */
-private fun getReproducingXmxArg(): String? {
- val maxHeapSizeInMegaBytes = (getNumericFinalFlagValue("MaxHeapSize") ?: return null) shr 20
- val conservativeMaxHeapSizeInMegaBytes = (maxHeapSizeInMegaBytes * 0.9).toInt()
- return "-Xmx${conservativeMaxHeapSizeInMegaBytes}m"
-}
-
-/**
- * Returns a valid `-Xss` JVM argument that sets the stack size to a value with which [StackOverflowError] findings can
- * be reproduced, assuming the environment is sufficiently similar (e.g. OS and JVM version).
- */
-private fun getReproducingXssArg(): String? {
- val threadStackSizeInKiloBytes = getNumericFinalFlagValue("ThreadStackSize") ?: return null
- val conservativeThreadStackSizeInKiloBytes = (threadStackSizeInKiloBytes * 0.9).toInt()
- return "-Xss${conservativeThreadStackSizeInKiloBytes}k"
-}
-
-private fun getNumericFinalFlagValue(arg: String): Long? {
- val argPattern = "$arg\\D*(\\d*)".toRegex()
- return argPattern.find(javaFullFinalFlags ?: return null)?.groupValues?.get(1)?.toLongOrNull()
-}
-
-private val javaFullFinalFlags by lazy {
- readJavaFullFinalFlags()
-}
-
-private fun readJavaFullFinalFlags(): String? {
- val javaHome = System.getProperty("java.home") ?: return null
- val javaBinary = "$javaHome/bin/java"
- val currentJvmArgs = ManagementFactory.getRuntimeMXBean().inputArguments
- val javaPrintFlagsProcess = ProcessBuilder(
- listOf(javaBinary) + currentJvmArgs + listOf(
- "-XX:+PrintFlagsFinal",
- "-version"
- )
- ).start()
- return javaPrintFlagsProcess.inputStream.bufferedReader().useLines { lineSequence ->
- lineSequence
- .filter { it.contains("ThreadStackSize") || it.contains("MaxHeapSize") }
- .joinToString("\n")
- }
-}
-
-fun dumpAllStackTraces() {
- System.err.println("\nStack traces of all JVM threads:\n")
- for ((thread, stack) in Thread.getAllStackTraces()) {
- System.err.println(thread)
- // Remove traces of this method and the methods it calls.
- stack.asList()
- .asReversed()
- .takeWhile {
- !(
- it.className == "com.code_intelligence.jazzer.runtime.ExceptionUtils" &&
- it.methodName == "dumpAllStackTraces"
- )
- }
- .asReversed()
- .forEach { frame ->
- System.err.println("\tat $frame")
- }
- System.err.println()
- }
- System.err.println("Garbage collector stats:")
- System.err.println(
- ManagementFactory.getGarbageCollectorMXBeans().joinToString("\n", "\n", "\n") {
- "${it.name}: ${it.collectionCount} collections took ${it.collectionTime}ms"
- }
- )
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/utils/ManifestUtils.kt b/agent/src/main/java/com/code_intelligence/jazzer/utils/ManifestUtils.kt
deleted file mode 100644
index e7165e55..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/utils/ManifestUtils.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.code_intelligence.jazzer.utils
-
-import java.util.jar.Manifest
-
-object ManifestUtils {
-
- private const val FUZZ_TARGET_CLASS = "Jazzer-Fuzz-Target-Class"
- const val HOOK_CLASSES = "Jazzer-Hook-Classes"
-
- fun combineManifestValues(attribute: String): List<String> {
- val manifests = ClassLoader.getSystemResources("META-INF/MANIFEST.MF")
- return manifests.asSequence().mapNotNull { url ->
- url.openStream().use { inputStream ->
- val manifest = Manifest(inputStream)
- manifest.mainAttributes.getValue(attribute)
- }
- }.toList()
- }
-
- /**
- * Returns the value of the `Fuzz-Target-Class` manifest attribute if there is a unique one among all manifest
- * files in the classpath.
- */
- @JvmStatic
- fun detectFuzzTargetClass(): String? {
- val fuzzTargets = combineManifestValues(FUZZ_TARGET_CLASS)
- return when (fuzzTargets.size) {
- 0 -> null
- 1 -> fuzzTargets.first()
- else -> {
- println(
- """
- |WARN: More than one Jazzer-Fuzz-Target-Class manifest entry detected on the
- |classpath.""".trimMargin()
- )
- null
- }
- }
- }
-}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/utils/Utils.kt b/agent/src/main/java/com/code_intelligence/jazzer/utils/Utils.kt
deleted file mode 100644
index 1b399baf..00000000
--- a/agent/src/main/java/com/code_intelligence/jazzer/utils/Utils.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2021 Code Intelligence GmbH
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-@file:JvmName("Utils")
-
-package com.code_intelligence.jazzer.utils
-
-import java.lang.reflect.Executable
-import java.lang.reflect.Method
-import java.nio.ByteBuffer
-import java.nio.channels.FileChannel
-
-val Class<*>.descriptor: String
- get() = when {
- isPrimitive -> {
- when (this) {
- Boolean::class.javaPrimitiveType -> "Z"
- Byte::class.javaPrimitiveType -> "B"
- Char::class.javaPrimitiveType -> "C"
- Short::class.javaPrimitiveType -> "S"
- Int::class.javaPrimitiveType -> "I"
- Long::class.javaPrimitiveType -> "J"
- Float::class.javaPrimitiveType -> "F"
- Double::class.javaPrimitiveType -> "D"
- java.lang.Void::class.javaPrimitiveType -> "V"
- else -> throw IllegalStateException("Unknown primitive type: $name")
- }
- }
- isArray -> "[${componentType.descriptor}"
- java.lang.Object::class.java.isAssignableFrom(this) -> "L${name.replace('.', '/')};"
- else -> throw IllegalArgumentException("Unknown class type: $name")
- }
-
-val Class<*>.readableDescriptor: String
- get() = when {
- isPrimitive -> {
- when (this) {
- Boolean::class.javaPrimitiveType -> "boolean"
- Byte::class.javaPrimitiveType -> "byte"
- Char::class.javaPrimitiveType -> "char"
- Short::class.javaPrimitiveType -> "short"
- Int::class.javaPrimitiveType -> "int"
- Long::class.javaPrimitiveType -> "long"
- Float::class.javaPrimitiveType -> "float"
- Double::class.javaPrimitiveType -> "double"
- java.lang.Void::class.javaPrimitiveType -> "void"
- else -> throw IllegalStateException("Unknown primitive type: $name")
- }
- }
- isArray -> "${componentType.readableDescriptor}[]"
- java.lang.Object::class.java.isAssignableFrom(this) -> name
- else -> throw IllegalArgumentException("Unknown class type: $name")
- }
-
-val Executable.descriptor: String
- get() = parameterTypes.joinToString(separator = "", prefix = "(", postfix = ")") { parameterType ->
- parameterType.descriptor
- } + if (this is Method) returnType.descriptor else "V"
-
-// This does not include the return type as the parameter descriptors already uniquely identify the executable.
-val Executable.readableDescriptor: String
- get() = parameterTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { parameterType ->
- parameterType.readableDescriptor
- }
-
-fun simpleFastHash(vararg strings: String): Int {
- var hash = 0
- for (string in strings) {
- for (c in string) {
- hash = hash * 11 + c.code
- }
- }
- return hash
-}
-
-/**
- * Reads the [FileChannel] to the end as a UTF-8 string.
- */
-fun FileChannel.readFully(): String {
- check(size() <= Int.MAX_VALUE)
- val buffer = ByteBuffer.allocate(size().toInt())
- while (buffer.hasRemaining()) {
- when (read(buffer)) {
- 0 -> throw IllegalStateException("No bytes read")
- -1 -> break
- }
- }
- return String(buffer.array())
-}
-
-/**
- * Appends [string] to the end of the [FileChannel].
- */
-fun FileChannel.append(string: String) {
- position(size())
- write(ByteBuffer.wrap(string.toByteArray()))
-}