mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
* Replace SandboxedRunnable with Function interface. Remove DJVM from "Key Concepts" release notes. Update installation of shell tool. Fix broken sandbox package names. Make sure we only resolve each class once when loading. Also remove some unused default parameter values. Don't discard "bootstrap" sandbox.* classes because SourceClassLoader may need them. * Restore DJVM to the "Key Concepts" docs. * Remove all mention of "whitelisting" from the DJVM CLI.
This commit is contained in:
parent
9a1ac70959
commit
f9e07c0158
@ -51,22 +51,6 @@ 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
|
||||
|
||||
|
@ -32,14 +32,6 @@ abstract class ClassCommand : CommandBase() {
|
||||
@Option(names = ["--ignore-definition-providers"], description = ["Disable all definition providers."])
|
||||
var ignoreDefinitionProviders: Boolean = false
|
||||
|
||||
@Option(
|
||||
names = ["-w", "--whitelist"],
|
||||
description = ["Override the default whitelist. Use provided whitelist instead. If NONE is provided, the " +
|
||||
"whitelist will be ignored. If ALL is provided, all references will be whitelisted. LANG can be " +
|
||||
"used to only whitelist select classes and their members from the java.lang package."]
|
||||
)
|
||||
var whitelist: Path? = null
|
||||
|
||||
@Option(names = ["-c", "--classpath"], description = ["Additions to the default class path."], split = ":")
|
||||
var classPath: Array<Path> = emptyArray()
|
||||
|
||||
@ -65,8 +57,6 @@ abstract class ClassCommand : CommandBase() {
|
||||
|
||||
protected var executor = SandboxExecutor<Any, Any>(SandboxConfiguration.DEFAULT)
|
||||
|
||||
private var derivedWhitelist: Whitelist = Whitelist.MINIMAL
|
||||
|
||||
abstract fun processClasses(classes: List<Class<*>>)
|
||||
|
||||
open fun printSuccess(classes: List<Class<*>>) {}
|
||||
@ -74,8 +64,7 @@ abstract class ClassCommand : CommandBase() {
|
||||
override fun validateArguments() = filters.isNotEmpty()
|
||||
|
||||
override fun handleCommand(): Boolean {
|
||||
derivedWhitelist = whitelistFromPath(whitelist)
|
||||
val configuration = getConfiguration(derivedWhitelist)
|
||||
val configuration = getConfiguration(Whitelist.MINIMAL)
|
||||
classLoader = SourceClassLoader(getClasspath(), configuration.analysisConfiguration.classResolver)
|
||||
createExecutor(configuration)
|
||||
|
||||
|
@ -15,8 +15,7 @@ import picocli.CommandLine.Command
|
||||
NewCommand::class,
|
||||
RunCommand::class,
|
||||
ShowCommand::class,
|
||||
TreeCommand::class,
|
||||
WhitelistCommand::class
|
||||
TreeCommand::class
|
||||
]
|
||||
)
|
||||
@Suppress("KDocMissingDocumentation")
|
||||
|
@ -55,13 +55,13 @@ class NewCommand : CommandBase() {
|
||||
companion object {
|
||||
|
||||
val TEMPLATE = """
|
||||
|package net.corda.djvm;
|
||||
|package net.corda.sandbox;
|
||||
|
|
||||
|import net.corda.djvm.execution.SandboxedRunnable;
|
||||
|import java.util.function.Function;
|
||||
|
|
||||
|public class [NAME] implements SandboxedRunnable<[FROM], [TO]> {
|
||||
|public class [NAME] implements Function<[FROM], [TO]> {
|
||||
| @Override
|
||||
| public [TO] run([FROM] input) {
|
||||
| public [TO] apply([FROM] input) {
|
||||
| return [RETURN];
|
||||
| }
|
||||
|}
|
||||
|
@ -60,7 +60,7 @@ fun openOptions(force: Boolean) = if (force) {
|
||||
* Get the path of where any generated code will be placed. Create the directory if it does not exist.
|
||||
*/
|
||||
fun createCodePath(): Path {
|
||||
return Paths.get("tmp", "net", "corda", "djvm").let {
|
||||
return Paths.get("tmp", "net", "corda", "sandbox").let {
|
||||
Files.createDirectories(it)
|
||||
}
|
||||
}
|
||||
@ -92,7 +92,7 @@ val userClassPath: String = System.getProperty("java.class.path")
|
||||
/**
|
||||
* Get a reference of each concrete class that implements interface or class [T].
|
||||
*/
|
||||
inline fun <reified T> find(scanSpec: String = "net/corda/djvm"): List<Class<*>> {
|
||||
inline fun <reified T> find(scanSpec: String = "net/corda/sandbox"): List<Class<*>> {
|
||||
return ClassGraph()
|
||||
.whitelistPaths(scanSpec)
|
||||
.enableAllInfo()
|
||||
|
@ -1,14 +0,0 @@
|
||||
package net.corda.djvm.tools.cli
|
||||
|
||||
import picocli.CommandLine.Command
|
||||
|
||||
@Command(
|
||||
name = "whitelist",
|
||||
description = ["Utilities and commands related to the whitelist for the deterministic sandbox."],
|
||||
subcommands = [
|
||||
WhitelistGenerateCommand::class,
|
||||
WhitelistShowCommand::class
|
||||
]
|
||||
)
|
||||
@Suppress("KDocMissingDocumentation")
|
||||
class WhitelistCommand : CommandBase()
|
@ -1,92 +0,0 @@
|
||||
package net.corda.djvm.tools.cli
|
||||
|
||||
import net.corda.djvm.analysis.AnalysisConfiguration
|
||||
import net.corda.djvm.analysis.AnalysisContext
|
||||
import net.corda.djvm.analysis.ClassAndMemberVisitor
|
||||
import net.corda.djvm.references.ClassRepresentation
|
||||
import net.corda.djvm.references.Member
|
||||
import net.corda.djvm.source.ClassSource
|
||||
import picocli.CommandLine.*
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardOpenOption
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
@Command(
|
||||
name = "generate",
|
||||
description = ["Generate and export whitelist from the class and member declarations provided in one or more " +
|
||||
"JARs."]
|
||||
)
|
||||
@Suppress("KDocMissingDocumentation")
|
||||
class WhitelistGenerateCommand : CommandBase() {
|
||||
|
||||
@Parameters(description = ["The paths of the JARs that the whitelist is to be generated from."])
|
||||
var paths: Array<Path> = emptyArray()
|
||||
|
||||
@Option(
|
||||
names = ["-o", "--output"],
|
||||
description = ["The file to which the whitelist will be written. If not provided, STDOUT will be used."]
|
||||
)
|
||||
var output: Path? = null
|
||||
|
||||
override fun validateArguments() = paths.isNotEmpty()
|
||||
|
||||
override fun handleCommand(): Boolean {
|
||||
val entries = AnalysisConfiguration.createRoot().use { configuration ->
|
||||
val entries = mutableListOf<String>()
|
||||
val visitor = object : ClassAndMemberVisitor(configuration, null) {
|
||||
override fun visitClass(clazz: ClassRepresentation): ClassRepresentation {
|
||||
entries.add(clazz.name)
|
||||
return super.visitClass(clazz)
|
||||
}
|
||||
|
||||
override fun visitMethod(clazz: ClassRepresentation, method: Member): Member {
|
||||
visitMember(clazz, method)
|
||||
return super.visitMethod(clazz, method)
|
||||
}
|
||||
|
||||
override fun visitField(clazz: ClassRepresentation, field: Member): Member {
|
||||
visitMember(clazz, field)
|
||||
return super.visitField(clazz, field)
|
||||
}
|
||||
|
||||
private fun visitMember(clazz: ClassRepresentation, member: Member) {
|
||||
entries.add("${clazz.name}.${member.memberName}:${member.signature}")
|
||||
}
|
||||
}
|
||||
val context = AnalysisContext.fromConfiguration(configuration)
|
||||
for (path in paths) {
|
||||
ClassSource.fromPath(path).getStreamIterator().forEach {
|
||||
visitor.analyze(it, context)
|
||||
}
|
||||
}
|
||||
entries
|
||||
}
|
||||
output?.also {
|
||||
Files.newOutputStream(it, StandardOpenOption.CREATE).use { out ->
|
||||
GZIPOutputStream(out).use { gzip ->
|
||||
PrintStream(gzip).use { pout ->
|
||||
pout.println("""
|
||||
|java/.*
|
||||
|javax/.*
|
||||
|jdk/.*
|
||||
|com/sun/.*
|
||||
|sun/.*
|
||||
|---
|
||||
""".trimMargin().trim())
|
||||
printEntries(pout, entries)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: printEntries(System.out, entries)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun printEntries(stream: PrintStream, entries: List<String>) {
|
||||
for (entry in entries.sorted().distinct()) {
|
||||
stream.println(entry)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package net.corda.djvm.tools.cli
|
||||
|
||||
import picocli.CommandLine.*
|
||||
import java.nio.file.Path
|
||||
|
||||
@Command(
|
||||
name = "show",
|
||||
description = ["Print the whitelist used for the deterministic sandbox."]
|
||||
)
|
||||
@Suppress("KDocMissingDocumentation")
|
||||
class WhitelistShowCommand : CommandBase() {
|
||||
|
||||
@Option(
|
||||
names = ["-w", "--whitelist"],
|
||||
description = ["Override the default whitelist. Use provided whitelist instead."]
|
||||
)
|
||||
var whitelist: Path? = null
|
||||
|
||||
@Parameters(description = ["Words or phrases to use to filter down the result."])
|
||||
var filters: Array<String> = emptyArray()
|
||||
|
||||
override fun validateArguments() = true
|
||||
|
||||
override fun handleCommand(): Boolean {
|
||||
val whitelist = whitelistFromPath(whitelist)
|
||||
val filters = filters.map(String::toLowerCase)
|
||||
whitelist.items
|
||||
.filter { item -> filters.all { it in item.toLowerCase() } }
|
||||
.forEach { println(it) }
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,7 @@ file="${BASH_SOURCE[0]}"
|
||||
linked_file="$(test -L "$file" && readlink "$file" || echo "$file")"
|
||||
base_dir="$(cd "$(dirname "$linked_file")/../" && pwd)"
|
||||
version="$(cat $base_dir/../build.gradle | sed -n 's/^[ ]*ext\.corda_release_version[ =]*"\([^"]*\)".*$/\1/p')"
|
||||
jar_file="$base_dir/cli/build/libs/corda-djvm-$version-all.jar"
|
||||
jar_file="$base_dir/cli/build/libs/corda-djvm-$version-cli.jar"
|
||||
|
||||
CLASSPATH="${CLASSPATH:-}"
|
||||
|
||||
|
@ -10,7 +10,7 @@ cd "$base_dir/.."
|
||||
|
||||
# Generate auto-completion file for Bash and ZSH
|
||||
cd "$base_dir"
|
||||
java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-all.jar" \
|
||||
java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-cli.jar" \
|
||||
picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f
|
||||
|
||||
# Create a symbolic link to the `djvm` utility
|
||||
|
@ -131,7 +131,7 @@ class SandboxClassLoader private constructor(
|
||||
|
||||
if (!isSandboxClass || parent is SandboxClassLoader) {
|
||||
try {
|
||||
clazz = super.loadClass(name, resolve)
|
||||
clazz = super.loadClass(name, false)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
} catch (e: SandboxClassLoadingException) {
|
||||
e.messages.clearProvisional()
|
||||
|
@ -11,8 +11,8 @@ import java.nio.file.Path
|
||||
* @property origin The origin of the class source, if any.
|
||||
*/
|
||||
class ClassSource private constructor(
|
||||
val qualifiedClassName: String = "",
|
||||
val origin: String? = null
|
||||
val qualifiedClassName: String,
|
||||
val origin: String?
|
||||
) {
|
||||
val internalClassName: String = qualifiedClassName.asResourcePath
|
||||
|
||||
|
@ -59,8 +59,8 @@ Abstraction
|
||||
~~~~~~~~~~~
|
||||
|
||||
The sandbox is abstracted away as an executor which takes as input an implementation of the interface
|
||||
``SandboxedRunnable<in Input, out Output>``, dereferenced by a ``ClassSource``. This interface has a single method that
|
||||
needs implementing, namely ``run(Input): Output``.
|
||||
``Function<in Input, out Output>``, dereferenced by a ``ClassSource``. This interface has a single method that
|
||||
needs implementing, namely ``apply(Input): Output``.
|
||||
|
||||
A ``ClassSource`` object referencing such an implementation can be passed into the ``SandboxExecutor<in Input, out
|
||||
Output>`` together with an input of type ``Input``. The executor has operations for both execution and static
|
||||
@ -68,7 +68,7 @@ validation, namely ``run()`` and ``validate()``. These methods both return a sum
|
||||
|
||||
* In the case of execution, this summary object has information about:
|
||||
* Whether or not the runnable was successfully executed.
|
||||
* If successful, the return value of ``SandboxedRunnable.run()``.
|
||||
* If successful, the return value of ``Function.apply()``.
|
||||
* If failed, the exception that was raised.
|
||||
* And in both cases, a summary of all accrued costs during execution.
|
||||
|
||||
@ -80,7 +80,7 @@ validation, namely ``run()`` and ``validate()``. These methods both return a sum
|
||||
severity ``ERROR`` will prevent execution.
|
||||
|
||||
The sandbox has a configuration that applies to the execution of a specific runnable. This configuration, on a higher
|
||||
level, contains a set of rules, definition providers, emitters and a whitelist.
|
||||
level, contains a set of rules, definition providers and emitters.
|
||||
|
||||
.. image:: resources/djvm-overview.png
|
||||
|
||||
@ -107,7 +107,7 @@ work may well introduce additional constraints that we would want to place on th
|
||||
|
||||
.. note::
|
||||
It is worth noting that not only smart contract code is instrumented by the sandbox, but all code that it can
|
||||
transitively reach. In particular this means that the Java runtime classes (that have not been whitelisted) and any
|
||||
transitively reach. In particular this means that the Java runtime classes and any
|
||||
other library code used in the program are also instrumented and persisted ahead of time.
|
||||
|
||||
|
||||
@ -129,15 +129,6 @@ tries to catch such exceptions, as doing so would allow the user to bypass the t
|
||||
profile.
|
||||
|
||||
|
||||
Only Allow Explicitly Whitelisted Runtime API
|
||||
.............................................
|
||||
|
||||
Ensures that constant pool references are mapped against a verified subset of the Java runtime libraries. Said subset
|
||||
excludes functionality that contract code should not have access to, such as native code. This whitelist has been
|
||||
trimmed down to the bare minimum needed, a few classes in ``java.lang``, so that also the Java runtime libraries
|
||||
themselves are subjected to the same amount of scrutiny that the rest of the code is.
|
||||
|
||||
|
||||
Disallow Dynamic Invocation
|
||||
...........................
|
||||
|
||||
@ -154,9 +145,6 @@ network requests, general hardware interaction, threading, *etc.* These all cons
|
||||
allowing such code to be called arbitrarily from the JVM would require deterministic guarantees on the native machine
|
||||
code level. This falls out of scope for the DJVM.
|
||||
|
||||
Java runtime classes that call into native code and that are needed from within the sandbox environment, can be
|
||||
whitelisted explicitly.
|
||||
|
||||
|
||||
Disallow Finalizer Methods
|
||||
..........................
|
||||
@ -278,9 +266,8 @@ The loaded classes are further rewritten in two ways:
|
||||
Disable Synchronised Methods and Blocks
|
||||
.......................................
|
||||
|
||||
Since Java's multi-threading API has been excluded from the whitelist, synchronised methods and code blocks have little
|
||||
use in sandboxed code. Consequently, we log informational messages about occurrences of this in your sandboxed code and
|
||||
automatically transform them into ordinary methods and code blocks instead.
|
||||
The DJVM doesn't support multi-threading and so synchronised methods and code blocks have little
|
||||
use in sandboxed code. Consequently, we automatically transform them into ordinary methods and code blocks instead.
|
||||
|
||||
|
||||
Future Work
|
||||
@ -290,9 +277,6 @@ Further work is planned:
|
||||
|
||||
* To enable controlled use of reflection APIs.
|
||||
|
||||
* Strip out the dependency on the extensive whitelist of underlying Java
|
||||
runtime classes.
|
||||
|
||||
* Currently, dynamic invocation is disallowed. Allow specific lambda and
|
||||
string concatenation meta-factories used by Java code itself.
|
||||
|
||||
@ -351,7 +335,7 @@ This run will produce some output similar to this:
|
||||
|
||||
The output should be pretty self-explanatory, but just to summarise:
|
||||
|
||||
* It prints out the return value from the ``SandboxedRunnable<Object, Object>.run()`` method implemented in
|
||||
* It prints out the return value from the ``Function<Object, Object>.apply()`` method implemented in
|
||||
``net.corda.sandbox.Hello``.
|
||||
|
||||
* It also prints out the aggregated costs for allocations, invocations, jumps and throws.
|
||||
@ -363,5 +347,3 @@ Other commands to be aware of are:
|
||||
* ``djvm inspect`` which allows you to inspect what byte code modifications will be applied to a class.
|
||||
|
||||
* ``djvm show`` which displays the transformed byte code of a class, *i.e.*, the end result and not the difference.
|
||||
|
||||
* ``djvm whitelist`` which displays the content of the whitelist in use.
|
||||
|
Loading…
Reference in New Issue
Block a user