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' baseName 'corda-djvm'
classifier '' classifier ''
relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' 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 assemble.dependsOn shadowJar

View File

@ -84,23 +84,25 @@ class ClassResolver(
* Reverse the resolution of a class name. * Reverse the resolution of a class name.
*/ */
fun reverse(resolvedClassName: String): String { fun reverse(resolvedClassName: String): String {
if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) { return if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) {
return resolvedClassName 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. * Reverse the resolution of a class name from a fully qualified normalized name.
*/ */
fun reverseNormalized(name: String): String { fun reverseNormalized(className: String): String {
return reverse(name.asResourcePath).asPackagePath 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. * 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.EmitterModule
import net.corda.djvm.code.emptyAsNull import net.corda.djvm.code.emptyAsNull
import net.corda.djvm.references.Member import net.corda.djvm.references.Member
import net.corda.djvm.source.AbstractSourceClassLoader
import net.corda.djvm.utilities.loggerFor import net.corda.djvm.utilities.loggerFor
import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor 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. * Functionality for rewriting parts of a class as it is being loaded.
* *
* @property configuration The configuration of the sandbox. * @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( open class ClassRewriter(
private val configuration: SandboxConfiguration, private val configuration: SandboxConfiguration,
private val classLoader: ClassLoader private val classLoader: AbstractSourceClassLoader
) { ) {
private val analysisConfig = configuration.analysisConfiguration private val analysisConfig = configuration.analysisConfiguration

View File

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

View File

@ -47,29 +47,21 @@ abstract class AbstractSourceClassLoader(
return try { return try {
logger.trace("Opening ClassReader for class {}...", originalName) logger.trace("Opening ClassReader for class {}...", originalName)
getResourceAsStream("$originalName.class")?.use { getResourceAsStream("$originalName.class")?.use(::ClassReader) ?: run(::throwClassLoadingError)
ClassReader(it)
} ?: run(::throwClassLoadingError)
} catch (exception: IOException) { } catch (exception: IOException) {
throwClassLoadingError() 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. * Load the class with the specified binary name.
*/ */
override fun loadClass(name: String, resolve: Boolean): Class<*> { @Throws(ClassNotFoundException::class)
logger.trace("Loading class {}, resolve={}...", name, resolve) fun loadSourceClass(name: String): Class<*> {
val originalName = classResolver.reverseNormalized(name).let { n -> 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 // A synthetic exception should be mapped back to its
// corresponding exception in the original hierarchy. // corresponding exception in the original hierarchy.
if (isDJVMException(n)) { if (isDJVMException(n)) {
@ -78,7 +70,7 @@ abstract class AbstractSourceClassLoader(
n n
} }
} }
return super.loadClass(originalName, resolve) return loadClass(originalName)
} }
protected companion object { protected companion object {