diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4feab65..f5f551a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,10 +5,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- - name: Set up JDK 1.7
+ - name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
- java-version: 1.7
+ java-version: 1.8
- name: Build with Maven
run: mvn package --file pom.xml
- name: Upload Artifacts
diff --git a/.gitignore b/.gitignore
index e8acb36..841137e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,6 @@ hs_err_pid*
# IntelliJ
.idea/
+
+# result of build
+target/
diff --git a/README.md b/README.md
index 6ddf3b3..94f4b20 100644
--- a/README.md
+++ b/README.md
@@ -21,3 +21,4 @@ Supported fields for the `option` list are:
* `nobigmath` — disables `BigInteger` and `BigDecimal`, raising [appropriate exceptions](https://github.com/DMOJ/java-sandbox-agent/blob/master/src/main/java/ca/dmoj/java/BigIntegerDisallowedException.java) if they are used
* `unicode` — encodes `System.out` as UTF-8 instead of ASCII, sacrificing performance for Unicode support
* `nobuf` — sets `System.out` as being line-buffered, for interactive problems
+* `unsafe` — enables `sun.misc.Unsafe`, which is disabled by default
diff --git a/pom.xml b/pom.xml
index 9b666f4..d3d37ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,8 +20,8 @@
org.apache.maven.plugins
maven-compiler-plugin
-
- 1.7
+
+ 1.8
@@ -39,4 +39,4 @@
-
\ No newline at end of file
+
diff --git a/src/main/java/ca/dmoj/java/DisallowedClassRule.java b/src/main/java/ca/dmoj/java/DisallowedClassRule.java
new file mode 100644
index 0000000..6caf0a3
--- /dev/null
+++ b/src/main/java/ca/dmoj/java/DisallowedClassRule.java
@@ -0,0 +1,23 @@
+package ca.dmoj.java;
+
+public class DisallowedClassRule {
+ protected interface ExceptionFactory {
+ Exception create();
+ }
+
+ protected String className;
+ protected ExceptionFactory exception;
+
+ public DisallowedClassRule(String className, ExceptionFactory exception) {
+ this.className = className;
+ this.exception = exception;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public Exception getException() {
+ return exception.create();
+ }
+}
diff --git a/src/main/java/ca/dmoj/java/DisallowedClassesClassFileTransformer.java b/src/main/java/ca/dmoj/java/DisallowedClassesClassFileTransformer.java
new file mode 100644
index 0000000..253a3aa
--- /dev/null
+++ b/src/main/java/ca/dmoj/java/DisallowedClassesClassFileTransformer.java
@@ -0,0 +1,34 @@
+package ca.dmoj.java;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+public class DisallowedClassesClassFileTransformer implements ClassFileTransformer {
+ protected DisallowedClassRule[] rules;
+
+ public DisallowedClassesClassFileTransformer(DisallowedClassRule... rules) {
+ this.rules = rules;
+ }
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer)
+ throws IllegalClassFormatException {
+ if (className == null) return classfileBuffer;
+
+ Exception disallowed = null;
+ for (DisallowedClassRule rule : rules) {
+ // If the class ever loaded it's because a submission used it
+ if (className.startsWith(rule.getClassName())) {
+ disallowed = rule.getException();
+ break;
+ }
+ }
+
+ if (disallowed != null) ExceptionHandler.dumpExceptionAndExit(disallowed);
+
+ // Don't actually retransform anything
+ return classfileBuffer;
+ }
+}
diff --git a/src/main/java/ca/dmoj/java/ExceptionHandler.java b/src/main/java/ca/dmoj/java/ExceptionHandler.java
new file mode 100644
index 0000000..8f8989b
--- /dev/null
+++ b/src/main/java/ca/dmoj/java/ExceptionHandler.java
@@ -0,0 +1,9 @@
+package ca.dmoj.java;
+
+class ExceptionHandler {
+ public static void dumpExceptionAndExit(Throwable exception) {
+ System.err.print("7257b50d-e37a-4664-b1a5-b1340b4206c0: ");
+ exception.printStackTrace();
+ System.exit(1);
+ }
+}
diff --git a/src/main/java/ca/dmoj/java/SubmissionAgent.java b/src/main/java/ca/dmoj/java/SubmissionAgent.java
index cbd09d1..363e439 100644
--- a/src/main/java/ca/dmoj/java/SubmissionAgent.java
+++ b/src/main/java/ca/dmoj/java/SubmissionAgent.java
@@ -1,19 +1,19 @@
package ca.dmoj.java;
import java.io.*;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
+import java.util.ArrayList;
public class SubmissionAgent {
public static void premain(String argv, Instrumentation inst) throws UnsupportedEncodingException {
+ boolean unsafe = false;
boolean unicode = false;
boolean noBigMath = false;
boolean noBuf = false;
if (argv != null) {
for (String opt : argv.split(",")) {
+ if (opt.equals("unsafe")) unsafe = true;
if (opt.equals("unicode")) unicode = true;
if (opt.equals("nobigmath")) noBigMath = true;
if (opt.equals("nobuf")) noBuf = true;
@@ -22,30 +22,17 @@ public static void premain(String argv, Instrumentation inst) throws Unsupported
final Thread selfThread = Thread.currentThread();
+ ArrayList disallowedClassRules = new ArrayList<>();
+ if (!unsafe) {
+ disallowedClassRules.add(new DisallowedClassRule("sun/reflect/Unsafe", UnsafeDisallowedException::new));
+ disallowedClassRules.add(new DisallowedClassRule("sun/misc/Unsafe", UnsafeDisallowedException::new));
+ }
if (noBigMath) {
- inst.addTransformer(new ClassFileTransformer() {
- @Override
- public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
- ProtectionDomain protectionDomain, byte[] classfileBuffer)
- throws IllegalClassFormatException {
- if (className == null) return classfileBuffer;
-
- RuntimeException disallowed = null;
- // If the class ever loaded it's because a submission used it
- if (className.startsWith("java/math/BigInteger") ||
- className.startsWith("java/math/MutableBigInteger")) {
- disallowed = new BigIntegerDisallowedException();
- } else if (className.startsWith("java/math/BigDecimal")) {
- disallowed = new BigDecimalDisallowedException();
- }
-
- if (disallowed != null) dumpExceptionAndExit(disallowed);
-
- // Don't actually retransform anything
- return classfileBuffer;
- }
- });
+ disallowedClassRules.add(new DisallowedClassRule("java/math/BigInteger", BigIntegerDisallowedException::new));
+ disallowedClassRules.add(new DisallowedClassRule("java/math/MutableBigInteger", BigIntegerDisallowedException::new));
+ disallowedClassRules.add(new DisallowedClassRule("java/math/BigDecimal", BigDecimalDisallowedException::new));
}
+ inst.addTransformer(new DisallowedClassesClassFileTransformer(disallowedClassRules.toArray(new DisallowedClassRule[0])));
if (noBuf) {
// Create output PrintStream set to autoflush:
@@ -61,7 +48,7 @@ public byte[] transform(ClassLoader loader, String className, Class> classBein
selfThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable error) {
- dumpExceptionAndExit(error);
+ ExceptionHandler.dumpExceptionAndExit(error);
}
});
@@ -75,10 +62,4 @@ public void run() {
}
}));
}
-
- private static void dumpExceptionAndExit(Throwable exception) {
- System.err.print("7257b50d-e37a-4664-b1a5-b1340b4206c0: ");
- exception.printStackTrace();
- System.exit(1);
- }
}
diff --git a/src/main/java/ca/dmoj/java/UnsafeDisallowedException.java b/src/main/java/ca/dmoj/java/UnsafeDisallowedException.java
new file mode 100644
index 0000000..665c4df
--- /dev/null
+++ b/src/main/java/ca/dmoj/java/UnsafeDisallowedException.java
@@ -0,0 +1,4 @@
+package ca.dmoj.java;
+
+public class UnsafeDisallowedException extends RuntimeException {
+}