Browse files

Added security manager for Java code. Can't quite remember how it

works; I think it's all based on the name of the thread group?  At any
rate it blocks calls to System.exit(1) and the spawning of threads.

Also refactored JUnit tests.
  • Loading branch information...
1 parent 1c0c685 commit ee9ca0d11bc81396e33bd296c901db01f0498ac0 @jspacco committed Dec 10, 2011
View
43 CloudCoderOutOfProcessSubmitService/src/org/cloudcoder/submitsvc/oop/builder/CTester.java
@@ -18,13 +18,10 @@
package org.cloudcoder.submitsvc.oop.builder;
import java.io.File;
-import java.io.IOException;
import java.util.Arrays;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import org.cloudcoder.app.server.submitsvc.oop.WorkerTask;
import org.cloudcoder.app.shared.model.Problem;
import org.cloudcoder.app.shared.model.TestCase;
import org.cloudcoder.app.shared.model.TestOutcome;
@@ -60,6 +57,33 @@ private String makeCTestFile(Problem problem,
return test.toString();
}
+ private void wait(ProcessRunner[] pool) {
+ int numPauses=7;
+ for (int i=1; i<=numPauses; i++) {
+ if (!pauseAndPoll(TIMEOUT_LIMIT/numPauses, pool)) {
+ // we can stop pausing
+ return;
+ }
+ }
+ }
+
+ private boolean pauseAndPoll(long time, ProcessRunner[] pool) {
+ try {
+ Thread.sleep(time);
+ for (ProcessRunner p : pool) {
+ if (p.isRunning()) {
+ return true;
+ }
+ }
+ } catch (InterruptedException e) {
+ // should never happen; to be safe, assume a thread may
+ // still be running.
+ return true;
+ }
+ // no threads are alive, so we can stop waiting
+ return false;
+ }
+
@Override
public List<TestResult> testSubmission(Problem problem,
List<TestCase> testCaseList, String programText)
@@ -90,12 +114,10 @@ private String makeCTestFile(Problem problem,
tests[i]=new ProcessRunner();
tests[i].runAsynchronous(workDir, getTestCommand(workDir.getAbsolutePath()+File.separatorChar+programName, testCaseList.get(i)));
}
+
// wait for the timeout limit
- try {
- Thread.sleep(TIMEOUT_LIMIT);
- } catch (InterruptedException e){
- // Ignore; never happens
- }
+ wait(tests);
+
for (ProcessRunner p : tests) {
if (p.isRunning()) {
p.killProcess();
@@ -111,6 +133,11 @@ private String makeCTestFile(Problem problem,
p.getStatusMessage(),
merge(p.getStdout()),
merge(p.getStderr())));
+ } else if (p.getExitCode()==6) {
+ results.add(new TestResult(TestOutcome.FAILED_WITH_EXCEPTION,
+ p.getStatusMessage(),
+ merge(p.getStdout()),
+ merge(p.getStderr())));
} else {
results.add(new TestResult(TestOutcome.FAILED_ASSERTION,
p.getStatusMessage(),
View
6 CloudCoderOutOfProcessSubmitService/src/org/cloudcoder/submitsvc/oop/builder/Compiler.java
@@ -48,10 +48,6 @@
public Compiler(String code, File workDir, String progName) {
this.progName = progName;
this.workDir = workDir;
- if (workDir.exists()) {
- logger.warn("work directory already exists! Going to compile in there without looking");
- //TODO: Crash compiler?
- }
this.code = code;
this.statusMessage = "";
this.compilerOutput = new LinkedList<String>();
@@ -107,8 +103,6 @@ private String getExeFileName() {
return progName;
}
-
-
private boolean runCommand(File tempDir, String[] cmd) {
ProcessRunner runner = new ProcessRunner();
if (!runner.runSynchronous(tempDir, cmd)) {
View
27 CloudCoderOutOfProcessSubmitService/src/org/cloudcoder/submitsvc/oop/builder/JavaTester.java
@@ -33,14 +33,21 @@
import org.cloudcoder.app.shared.model.TestCase;
import org.cloudcoder.app.shared.model.TestOutcome;
import org.cloudcoder.app.shared.model.TestResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class JavaTester implements ITester
{
+ private static final Logger logger=LoggerFactory.getLogger(JavaTester.class);
public static final long TIMEOUT_LIMIT=2000;
+ static {
+ System.setSecurityManager(new StudentCodeSecurityManager());
+ }
+
public List<TestResult> testSubmission(Problem problem,
List<TestCase> testCaseList,
- String programText)
+ final String programText)
{
List<TestResult> testResultList = new ArrayList<TestResult>();
@@ -51,10 +58,10 @@
// FIXME: this could be cached
String testerCode = createTesterClassSource(problem, testCaseList);
- System.out.println("Test code:");
- System.out.println(testCode);
- System.out.println("Tester code:");
- System.out.println(testerCode);
+ logger.trace("Test code:");
+ logger.trace(testCode);
+ logger.trace("Tester code:");
+ logger.trace(testerCode);
// Compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
@@ -91,11 +98,15 @@ public TestResult execute() {
return new TestResult(TestOutcome.FAILED_ASSERTION, "Failed for input=" + t.getInput() + ", expected=" + t.getOutput());
}
} catch (InvocationTargetException e) {
- return new TestResult(TestOutcome.FAILED_WITH_EXCEPTION, "Failed with "+e.getTargetException().getMessage());
+ if (e.getCause() instanceof SecurityException) {
+ logger.warn("Security exception with code: "+programText);
+ return new TestResult(TestOutcome.FAILED_BY_SECURITY_MANAGER, "Security exception while testing submission");
+ } else {
+ logger.warn("InvocationTargetException", e);
+ return new TestResult(TestOutcome.FAILED_WITH_EXCEPTION, "Failed with "+e.getTargetException().getMessage());
+ }
} catch (NoSuchMethodException e) {
return new TestResult(TestOutcome.INTERNAL_ERROR, "Method not found while testing submission");
- } catch (SecurityException e) {
- return new TestResult(TestOutcome.INTERNAL_ERROR, "Security exception while testing submission");
} catch (IllegalAccessException e) {
return new TestResult(TestOutcome.INTERNAL_ERROR, "Illegal access while testing submission");
}
View
105 ...essSubmitService/src/org/cloudcoder/submitsvc/oop/builder/StudentCodeSecurityManager.java
@@ -0,0 +1,105 @@
+package org.cloudcoder.submitsvc.oop.builder;
+
+import java.security.Permission;
+
+public class StudentCodeSecurityManager extends SecurityManager
+{
+ private ThreadGroup WORKER_THREAD_GROUP=KillableTaskManager.WORKER_THREAD_GROUP;
+
+ @Override
+ public void checkAccess(Thread t) {
+ if (isWorkerThread()) {
+ throw new SecurityException("Cannot access Thread");
+ }
+ }
+
+ @Override
+ public void checkAccess(ThreadGroup g) {
+ if (isWorkerThread()) {
+ throw new SecurityException("Cannot access ThreadGroup");
+ }
+ }
+
+ @Override
+ public void checkPermission(Permission perm) {
+ check(perm);
+
+ }
+
+ @Override
+ public void checkPermission(Permission perm, Object context) {
+ check(perm);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.SecurityManager#checkCreateClassLoader()
+ */
+ @Override
+ public void checkCreateClassLoader() {
+ if (isWorkerThread()) {
+ throw new SecurityException("Cannot create classloader");
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.SecurityManager#getThreadGroup()
+ */
+ @Override
+ public ThreadGroup getThreadGroup() {
+ return super.getThreadGroup();
+ }
+
+ private boolean isWorkerThread() {
+ ThreadGroup group=getThreadGroup();
+ if (group==WORKER_THREAD_GROUP) {
+ return true;
+ }
+ return false;
+ }
+
+ private void check(Permission perm) {
+ // allow reading the line separator
+ if (perm.getName().equals("line.separator") && perm.getActions().contains("read")) {
+ return;
+ }
+ if (perm.getName().equals("accessDeclaredMembers")) {
+ return;
+ }
+ if (isWorkerThread()) {
+ throw new SecurityException("Student code doing " +perm.getName());
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.SecurityManager#checkExit(int)
+ */
+ @Override
+ public void checkExit(int status) {
+ if (isWorkerThread()) {
+ throw new SecurityException("Student code should not call System.exit(int i)");
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.SecurityManager#checkExec(java.lang.String)
+ */
+ @Override
+ public void checkExec(String cmd) {
+ if (isWorkerThread()) {
+ throw new SecurityException("Student code cannot execute commands");
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.SecurityManager#checkPackageDefinition(java.lang.String)
+ */
+// @Override
+// public void checkPackageDefinition(String pkg) {
+// System.out.println("checkPackageDefinition");
+// if (pkg.equals("edu.ycp.cs.netcoder.server.compilers")) {
+// return;
+// }
+// }
+
+}
View
5 ...OutOfProcessSubmitService/test/org/cloudcoder/submitsvc/oop/builder/test/GenericTest.java
@@ -18,7 +18,6 @@
package org.cloudcoder.submitsvc.oop.builder.test;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
import junit.framework.Assert;
@@ -56,10 +55,10 @@ protected void before() {
testCaseNum=1;
}
- protected void addTestCase(String input, String output, TestOutcome outcome) {
+ protected void addTestCase(String testName, String input, String output, TestOutcome outcome) {
TestCase testCase=new TestCase();
testCase.setId(testCaseNum);
- testCase.setTestCaseName("test"+testCaseNum);
+ testCase.setTestCaseName(testName);
testCase.setInput(input);
testCase.setOutput(output);
testCase.setProblemId(problem.getProblemId());
View
17 ...OutOfProcessSubmitService/test/org/cloudcoder/submitsvc/oop/builder/test/TestCTester.java
@@ -15,10 +15,6 @@ public void before() {
problem=createGenericProblem();
problem.setProblemType(ProblemType.C_FUNCTION);
problem.setTestName("sq");
- addTestCase("5", "25", TestOutcome.FAILED_ASSERTION);
- addTestCase("9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
- addTestCase("-1", "1", TestOutcome.PASSED);
- addTestCase("10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
tester=new CTester();
programText="int sq(int x) { \n" +
@@ -32,23 +28,34 @@ public void before() {
@Test
public void test1() {
+ addTestCase("test1", "5", "25", TestOutcome.FAILED_ASSERTION);
runOneTest("test1");
}
@Test
public void test2() {
+ addTestCase("test2", "9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
runOneTest("test2");
}
@Test
public void test3() {
+ addTestCase("test3", "-1", "1", TestOutcome.PASSED);
runOneTest("test3");
}
@Test
public void test4() {
+ addTestCase("test4", "10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
runOneTest("test4");
}
-
+ @Test
+ public void runAllTests() {
+ addTestCase("test1", "5", "25", TestOutcome.FAILED_ASSERTION);
+ addTestCase("test2", "9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
+ addTestCase("test3", "-1", "1", TestOutcome.PASSED);
+ addTestCase("test4", "10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
+ super.runAllTests();
+ }
}
View
59 ...OfProcessSubmitService/test/org/cloudcoder/submitsvc/oop/builder/test/TestJavaTester.java
@@ -20,23 +20,62 @@ public void before() {
problem=createGenericProblem();
problem.setProblemType(ProblemType.JAVA_METHOD);
problem.setTestName("sq");
- addTestCase("5", "25", TestOutcome.FAILED_ASSERTION);
- addTestCase("9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
- addTestCase("-1", "1", TestOutcome.PASSED);
- addTestCase("10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
tester=new JavaTester();
programText="public int sq(int x) { \n" +
- " if (x==5) return 17; \n" +
- " if (x==9) throw new NullPointerException(); \n" +
- " if (x==10) while (true); \n" +
+ " if (x==1) return 17; \n" +
+ " if (x==2) throw new NullPointerException(); \n" +
+ " if (x==3) while (true); \n" +
+ " if (x==4) new Thread() { public void run() {} }.start(); \n" +
+ " if (x==5) return x*x; \n" +
+ " if (x==6) System.exit(1); \n" +
" return x*x; \n" +
"}";
}
@Test
- public void testSq() {
- // program text will fail two of our test cases
- runAllTests();
+ public void test1() {
+ addTestCase("test1", "1", "1", TestOutcome.FAILED_ASSERTION);
+ runOneTest("test1");
+ }
+
+ @Test
+ public void test2() {
+ addTestCase("test2", "2", "4", TestOutcome.FAILED_WITH_EXCEPTION);
+ runOneTest("test2");
+ }
+
+ @Test
+ public void test3() {
+ addTestCase("test3", "3", "9", TestOutcome.FAILED_FROM_TIMEOUT);
+ runOneTest("test3");
+ }
+
+ @Test
+ public void test4() {
+ addTestCase("test4", "4", "16", TestOutcome.FAILED_BY_SECURITY_MANAGER);
+ runOneTest("test4");
+ }
+
+ @Test
+ public void test5() {
+ addTestCase("test5", "5", "25", TestOutcome.PASSED);
+ runOneTest("test5");
+ }
+
+ @Test
+ public void test6() {
+ addTestCase("test6", "6", "36", TestOutcome.FAILED_BY_SECURITY_MANAGER);
+ runOneTest("test6");
+ }
+
+ @Test
+ public void runAllTests() {
+ addTestCase("test1", "1", "1", TestOutcome.FAILED_ASSERTION);
+ addTestCase("test2", "2", "4", TestOutcome.FAILED_WITH_EXCEPTION);
+ addTestCase("test3", "3", "9", TestOutcome.FAILED_FROM_TIMEOUT);
+ addTestCase("test4", "4", "16", TestOutcome.FAILED_BY_SECURITY_MANAGER);
+ addTestCase("test5", "5", "25", TestOutcome.PASSED);
+ super.runAllTests();
}
}
View
28 ...rOutOfProcessSubmitService/test/org/cloudcoder/submitsvc/oop/builder/test/TestJython.java
@@ -21,33 +21,5 @@ public void testJython() throws Exception {
System.out.println(True==terp.eval("Tester().test1()"));
System.out.println(False==terp.eval("Tester().test2()"));
-
- //System.out.println(terp.eval("Tester().test1()"));
-
}
-
-// private static String executeFunction(String body,
-// PythonInterpreter terp,
-// String input)
-// {
-// terp.exec("theRes=sq("+input+")");
-// return terp.get("theRes").toString();
-// }
-//
-// @Test
-// public void testInterpreter() throws Exception {
-// PythonInterpreter terp=new PythonInterpreter();
-// assertEquals("16", executeFunction(terp, "4"));
-// //PyCode pyCode=terp.compile(body);
-// //pyCode.invoke("sq", new PyObject[] {new PyOb"4"});
-// terp.exec(body);
-// terp.exec("res=sq(4)");
-// PyObject res=terp.get("res");
-// //System.out.println("type: "+res.getType());
-// //System.out.println("class: "+res.getClass());
-// //res._eq()
-// assertEquals("16", res.toString());
-// System.out.println(res);
-// }
-
}
View
48 ...ProcessSubmitService/test/org/cloudcoder/submitsvc/oop/builder/test/TestPythonTester.java
@@ -3,33 +3,20 @@
import org.cloudcoder.app.shared.model.ProblemType;
import org.cloudcoder.app.shared.model.TestOutcome;
import org.cloudcoder.submitsvc.oop.builder.PythonTester;
-import org.cloudcoder.submitsvc.oop.builder.SingleThreadPythonTester;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
public class TestPythonTester extends GenericTest
{
- @BeforeClass
- public static void createJavaTester() {
-
- }
-
@Before
public void before() {
super.before();
// create a problem
problem=createGenericProblem();
problem.setProblemType(ProblemType.PYTHON_FUNCTION);
-
- // specific to the sq method
problem.setTestName("sq");
- addTestCase("5", "25", TestOutcome.FAILED_ASSERTION);
- addTestCase("9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
- addTestCase("-1", "1", TestOutcome.PASSED);
- addTestCase("10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
-
+
tester=new PythonTester();
programText="def sq(x):\n" +
" if x==5:\n" +
@@ -43,26 +30,35 @@ public void before() {
}
@Test
- public void testSq() {
- // program text will fail two of our test cases
- runAllTests();
+ public void test1() {
+ addTestCase("test1", "5", "25", TestOutcome.FAILED_ASSERTION);
+ runOneTest("test1");
+ }
+
+ @Test
+ public void test2() {
+ addTestCase("test2", "9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
+ runOneTest("test2");
}
@Test
- public void singleThread0() {
- tester=new SingleThreadPythonTester();
- runOneTest(0);
+ public void test3() {
+ addTestCase("test3", "-1", "1", TestOutcome.PASSED);
+ runOneTest("test3");
}
@Test
- public void singleThread1() {
- tester=new SingleThreadPythonTester();
- runOneTest(1);
+ public void test4() {
+ addTestCase("test4", "10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
+ runOneTest("test4");
}
@Test
- public void singleThread2() {
- tester=new SingleThreadPythonTester();
- runOneTest(2);
+ public void runAllTests() {
+ addTestCase("test1", "5", "25", TestOutcome.FAILED_ASSERTION);
+ addTestCase("test2", "9", "81", TestOutcome.FAILED_WITH_EXCEPTION);
+ addTestCase("test3", "-1", "1", TestOutcome.PASSED);
+ addTestCase("test4", "10", "100", TestOutcome.FAILED_FROM_TIMEOUT);
+ super.runAllTests();
}
}

0 comments on commit ee9ca0d

Please sign in to comment.