mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
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:
committed by
Tommy Lillehagen
parent
3a89bc774e
commit
136f6a6be6
@ -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
|
||||||
|
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user