0

From within my java application, I have a requirement to run some untrusted code which is uploaded by a user. This can be done in a similar way to here:

Java security: Sandboxing plugins loaded via URLClassLoader

However, let's assume I have many users (each with a thread), and many users uploading untrusted code at the same time. Using System.setSecurityManager once before their code is ran to lock down the permissions, and then once again after is no longer going to work, as it is possible that multiple threads are calling this method at the same time (For example, thread 2 just starts the method and sets the security manager to the sandbox version, just as thread 1 sets the security manager to the all allow all security manager), and we have a problem.

To make this a bit more tricky, there are several different objects, lets just call them User objects, so if I make a synchronised block:

synchonize(this){
    // set security manager lockdown permissions
    // run untrusted code
    // set security manager all permissions
}

This almost works apart from the fact that this is synchronized using the object (many threads cannot be inside this method, for this object particular at the same time).

What I ideally need to do is say, for all objects of this type (static?), synchronise this block. Therefore, locking out all users from using this block until it finishes. That's OK, these users can wait.

Therefore, I can imagine this working, but I don't know if it does exactly what I expect.

synchronized(User.class){

}

My question is, does all of this sound correct, and is it a sensible way to do it? I use timeouts etc (to avoid potentially the untrusted code hanging).

I would test this, but it is actually not very easy to test, and it seems rather complex (usually, when I find I am doing something overly complex, it's because it's not what I should be doing). So before I go rampaging in on this one, does the above seem correct, or am I missing something obvious?

Edit: More info:

@edharned. A little more about what is happening here may help. The server has a set of JUnit tests, the user uploads some work, the server executes the JUnit tests, but wants to do so safely. The JUnit tests are not called in a new process, but instead are ran from within the main (and only) JVM. This is so JUnitCore can be used and the calling method can work out how many tests there were, and which/how many failed using the Result object (http://junit.sourceforge.net/javadoc/org/junit/runner/Result.html) and the run listener. If these tests are ran from a new process using junit.textui.TestRunner then I believe it only shows which tests failed (and not which passed).

Perhaps the solution is to run each in it's own JVM which can have certain privileges set using a security manager which is set from within the JUnit test class itself. However, there is now a new problem which is retrieving the test results (Which passed, which failed and with reasons).

Community
  • 1
  • 1
ThePerson
  • 3,048
  • 8
  • 43
  • 69
  • Would it make sense to have another JVM with a security manager and pass each request to it with RMI (or some other Remote Procedure Call?) This way you segregate the sand-box in one JVM. Without doing a full analysis of your situation I really can't answer your question. – edharned Nov 03 '14 at 18:36
  • 1
    As I mentioned on the other thread, the untrusted code can run on a different thread, say finaliser, and therefore execute as if trusted. – Tom Hawtin - tackline Nov 04 '14 at 16:58
  • Thanks Tom, it took me a while to actually get this. So (and excuse me if i'm wrong), what I think you are saying is that their code could start a new thread and finish. The security manager gets set back to the allow all, and then the new, potentially malicious thread may still be alive and cause problems. Is that correct? If so, yes - that's something I hadn't thought of and I didn't quite understand it before, that's worrying and thank you for pointing it out! – ThePerson Nov 04 '14 at 17:40
  • @edhamed, thanks - you may be correct that each should be in it's own JVM. I've added more details to the question. – ThePerson Nov 04 '14 at 17:59
  • @ThePerson There really is too much here to give a good answer. When you run an RMI Server the method call to it returns a value: Object back = server.doWork(parm); You control what goes in and what comes back. You can also use a callback mechanism in addition to the return value. It's another JVM, you can do anything you like. – edharned Nov 04 '14 at 21:16

0 Answers0