CORDA-2667: Fix DJVM SourceClassLoader so that ASM will use it only to load classes from outside the sandbox. (#4804)

This commit is contained in:
Chris Rankin 2019-02-22 14:03:54 +00:00 committed by Tommy Lillehagen
parent 3a89bc774e
commit 136f6a6be6
5 changed files with 68 additions and 34 deletions

View File

@ -51,6 +51,22 @@ shadowJar {
baseName 'corda-djvm'
classifier ''
relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm'
// These particular classes are only needed to "bootstrap"
// the compilation of the other sandbox classes. At runtime,
// we will generate better versions from deterministic-rt.jar.
exclude 'sandbox/java/lang/Appendable.class'
exclude 'sandbox/java/lang/CharSequence.class'
exclude 'sandbox/java/lang/Character\$Subset.class'
exclude 'sandbox/java/lang/Character\$Unicode*.class'
exclude 'sandbox/java/lang/Comparable.class'
exclude 'sandbox/java/lang/Enum.class'
exclude 'sandbox/java/lang/Iterable.class'
exclude 'sandbox/java/lang/StackTraceElement.class'
exclude 'sandbox/java/lang/StringBuffer.class'
exclude 'sandbox/java/lang/StringBuilder.class'
exclude 'sandbox/java/nio/**'
exclude 'sandbox/java/util/**'
}
assemble.dependsOn shadowJar

View File

@ -84,23 +84,25 @@ class ClassResolver(
* Reverse the resolution of a class name.
*/
fun reverse(resolvedClassName: String): String {
if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) {
return resolvedClassName
return if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) {
resolvedClassName
} else {
removeSandboxPrefix(resolvedClassName)
}
if (resolvedClassName.startsWith(sandboxPrefix)) {
val nameWithoutPrefix = resolvedClassName.drop(sandboxPrefix.length)
if (resolve(nameWithoutPrefix) == resolvedClassName) {
return nameWithoutPrefix
}
}
return resolvedClassName
}
/**
* Reverse the resolution of a class name from a fully qualified normalized name.
*/
fun reverseNormalized(name: String): String {
return reverse(name.asResourcePath).asPackagePath
fun reverseNormalized(className: String): String {
return reverse(className.asResourcePath).asPackagePath
}
/**
* Generates the equivalent class name outside the sandbox from a fully qualified normalized name.
*/
fun toSourceNormalized(className: String): String {
return toSource(className.asResourcePath).asPackagePath
}
/**
@ -114,6 +116,28 @@ class ClassResolver(
}
}
/**
* Maps a class name to its equivalent class outside the sandbox.
* Needed by [net.corda.djvm.source.AbstractSourceClassLoader].
*/
private fun toSource(className: String): String {
return if (className in pinnedClasses) {
className
} else {
removeSandboxPrefix(className)
}
}
private fun removeSandboxPrefix(className: String): String {
if (className.startsWith(sandboxPrefix)) {
val nameWithoutPrefix = className.drop(sandboxPrefix.length)
if (resolve(nameWithoutPrefix) == className) {
return nameWithoutPrefix
}
}
return className
}
/**
* Check if class is whitelisted or pinned.
*/

View File

@ -7,6 +7,7 @@ import net.corda.djvm.code.ClassMutator
import net.corda.djvm.code.EmitterModule
import net.corda.djvm.code.emptyAsNull
import net.corda.djvm.references.Member
import net.corda.djvm.source.AbstractSourceClassLoader
import net.corda.djvm.utilities.loggerFor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
@ -17,11 +18,11 @@ import org.objectweb.asm.MethodVisitor
* Functionality for rewriting parts of a class as it is being loaded.
*
* @property configuration The configuration of the sandbox.
* @property classLoader The class loader used to load the classes that are to be rewritten.
* @property classLoader The class loader used to load the source classes that are to be rewritten.
*/
open class ClassRewriter(
private val configuration: SandboxConfiguration,
private val classLoader: ClassLoader
private val classLoader: AbstractSourceClassLoader
) {
private val analysisConfig = configuration.analysisConfiguration

View File

@ -1,6 +1,7 @@
package net.corda.djvm.rewiring
import net.corda.djvm.code.asPackagePath
import net.corda.djvm.source.AbstractSourceClassLoader
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES
@ -22,28 +23,28 @@ import org.objectweb.asm.Type
*/
open class SandboxClassWriter(
classReader: ClassReader,
private val cloader: ClassLoader,
private val cloader: AbstractSourceClassLoader,
flags: Int = COMPUTE_FRAMES or COMPUTE_MAXS
) : ClassWriter(classReader, flags) {
override fun getClassLoader(): ClassLoader = cloader
override fun getClassLoader(): AbstractSourceClassLoader = cloader
/**
* Get the common super type of [type1] and [type2].
*/
override fun getCommonSuperClass(type1: String, type2: String): String {
// Need to override [getCommonSuperClass] to ensure that we use ClassLoader.loadClass().
// Need to override [getCommonSuperClass] to ensure that we use SourceClassLoader.loadSourceClass().
when {
type1 == OBJECT_NAME -> return type1
type2 == OBJECT_NAME -> return type2
}
val class1 = try {
classLoader.loadClass(type1.asPackagePath)
classLoader.loadSourceClass(type1.asPackagePath)
} catch (exception: Exception) {
throw TypeNotPresentException(type1, exception)
}
val class2 = try {
classLoader.loadClass(type2.asPackagePath)
classLoader.loadSourceClass(type2.asPackagePath)
} catch (exception: Exception) {
throw TypeNotPresentException(type2, exception)
}

View File

@ -47,29 +47,21 @@ abstract class AbstractSourceClassLoader(
return try {
logger.trace("Opening ClassReader for class {}...", originalName)
getResourceAsStream("$originalName.class")?.use {
ClassReader(it)
} ?: run(::throwClassLoadingError)
getResourceAsStream("$originalName.class")?.use(::ClassReader) ?: run(::throwClassLoadingError)
} catch (exception: IOException) {
throwClassLoadingError()
}
}
/**
* Find and load the class with the specified name from the search path.
*/
override fun findClass(name: String): Class<*> {
logger.trace("Finding class {}...", name)
val originalName = classResolver.reverseNormalized(name)
return super.findClass(originalName)
}
/**
* Load the class with the specified binary name.
*/
override fun loadClass(name: String, resolve: Boolean): Class<*> {
logger.trace("Loading class {}, resolve={}...", name, resolve)
val originalName = classResolver.reverseNormalized(name).let { n ->
@Throws(ClassNotFoundException::class)
fun loadSourceClass(name: String): Class<*> {
logger.trace("Loading source class {}...", name)
// We need the name of the equivalent class outside of the sandbox.
// This class is expected to belong to the application classloader.
val originalName = classResolver.toSourceNormalized(name).let { n ->
// A synthetic exception should be mapped back to its
// corresponding exception in the original hierarchy.
if (isDJVMException(n)) {
@ -78,7 +70,7 @@ abstract class AbstractSourceClassLoader(
n
}
}
return super.loadClass(originalName, resolve)
return loadClass(originalName)
}
protected companion object {