0

I'm trying to set up my java code such that it can be potentially stopped after any point of code execution.

I was thinking of putting all of my code inside a thread and calling Thread.interrupt() on it when I want it to stop. Note that this method will only cause the code to throw an InterruptedException if any thread blocking method is being run (Like sleep, join, wait, etc.). Otherwise it will just set the interrupted flag and we have to check it via the isInterrupted() after every line.

So now all I need to do is insert the following code...

if (myThread.isInterrupted()) {
    System.exit(0);
}

after every line of code. If my java code was stored in a String, how can I insert this line of code after every point of execution of my code?

I was thinking of using the split method on semicolons and inserting the thread code between every element in the resulting array but it doesn't work because, for example, for loops have semicolons that don't represent the end of a statement. I think I would also have to split on closing curly braces too because the also represent the end of a code statement.

EDIT: solution attempt:

final String javaCode = "if (myString.contains(\"foo\")) { return true; } int i = 0;";
final String threadDelimiter = "if (thisThread.isInterrupted()) { System.exit(0); }";
final StringBuffer sb = new StringBuffer();

for (int i = 0; i < javaCode.length(); i++) {
    final char currChar = javaCode.charAt(i);

    sb.append(currChar);
    if ("{};".contains(currChar + "")) {
        sb.append(threadDelimiter);
    }
}

System.out.println(sb);

This code is almost correct but it would not work for any sort of loops that use semicolon. It also wouldn't work for for loops that don't have braces.

Ogen
  • 6,499
  • 7
  • 58
  • 124
  • 6
    "*I'm trying to set up my java code such that it can be potentially stopped after any point of code execution.*" - What?? Why? Are you trying to debug something? If so, you can pause and step through your program line by line with a debugger. Otherwise, what is your goal here; simply sending a `SIGINT` is enough to cause your program to exit. – dimo414 Jun 25 '16 at 00:57
  • 1
    Why on earth would you want to do that after *every* line of code. Code runs fast, so just do it a crucial points, e.g. at the beginning of a long-running loop, or such. Whether it take another 0.000001 seconds before it is interrupted, that doesn't matter, does it? *FYI: My machine can run 200 simple statements in 0.000001 seconds.* – Andreas Jun 25 '16 at 00:58
  • I want java code that is interrupt-able just incase I am given long-running java code that I want to be able to stop as soon as a certain amount of time goes by. Also, I'm not trying to debug code. I am given code to run and I need to be able to interrupt it at any point. – Ogen Jun 25 '16 at 01:00
  • 1
    @Andreas You have no idea what statements I will be given to run. I might be given code that makes numerous api calls, database queries, etc. The speed of your machine is irrelevant. – Ogen Jun 25 '16 at 01:12
  • Send your program a `SIGINT` or `SIGTERM` and it will promptly exit. – dimo414 Jun 25 '16 at 01:20
  • Remote debugging exists as part of the JVM – OneCricketeer Jun 25 '16 at 01:27
  • In any case, trying to pause compiled code using the source as a reference doesn't seem like it should work – OneCricketeer Jun 25 '16 at 01:29
  • Rather than trying to manipulate Java source as a string (it's not a [regular language](https://en.wikipedia.org/wiki/Regular_language), so doing so is unwise), use [bytecode injection](http://stackoverflow.com/q/3470949/113632). Again however, if all you want to do is terminate an execution, simply do so at the process level. – dimo414 Jun 25 '16 at 01:30
  • @dimo414 So if I run the code in its own process and keep a reference to that process and send it a kill signal - it will INSTANTLY stop no matter what? – Ogen Jun 25 '16 at 01:35
  • @Ogen Yes, if you *kill* a process, it will instantly stop running. That is what kill means. – Andreas Jun 25 '16 at 01:39
  • When you send the JVM a `SIGINT`, `SIGTERM`, or `SIGHUP` it calls `Shutdown.exit()` (what `System.exit()` calls after some security checks). At JVM shutdown time any registered [shutdown hooks](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook-java.lang.Thread-) are executed, and then the process terminates. As long as the code you're trying to execute does not register any malicious shutdown hooks (which can be prevented by a security manager) the process will terminate quickly - not "instantly". – dimo414 Jun 25 '16 at 01:40
  • If you send a `SIGKILL` the operating system will promptly and violently kill the process without letting it do any sort of cleanup. – dimo414 Jun 25 '16 at 01:40
  • @dimo414 Isn't that kind of dangerous? I feel like my java code injection is safer. – Ogen Jun 25 '16 at 01:42
  • Dangerous how? You plan is to cause a running program to stop executing at any arbitrary line, without regard for locks, resources, or anything else. Terminating or killing the process accomplishes the same goal but in a much simpler, more standard way. – dimo414 Jun 25 '16 at 01:44

1 Answers1

4

First off, if your goal is to trigger System.exit() there's no need to inject such calls into another thread and then interrupt that thread; just call System.exit() where you would have called otherThread.interrupt(); and the process will exit just as quickly.

Second, your plan will not accomplish your goal. Suppose you added your interrupt-then-exit code around this statement:

new Scanner(System.in).next();

If nothing is being written to the program's stdin this statement will block indefinitely, and will not respect thread interruption. There are countless other examples of similar code snippets that will cause a thread to block. Your interrupted check may never be reached, no matter how granularity you inject it.

Third, manipulating source code as a string is a path down which madness lies. There exist standard tools to parse Java syntax into structured data types that you can safely manipulate. If you must do source code manipulation use the right equipment.

Fourth, there's no particular reason you need to interact with source code to simply inject additional commands; compile the source and use bytecode injection to inject your commands directly. Bytecode has a much more limited syntax than Java source, therefore it's easier to reason about and mutate safely.

All of that aside, however, you simply don't need to do any of this

It sounds like your goal is to execute some unknown snippet of Java code but be able to cause the process to stop at any time. This is exactly what your operating system is designed to do - manage process executions. To that end POSIX provides several standard signals you can send to a process to gracefully (or not-so-gracefully) cause that process to terminate. Sending a Java process a SIGINT (Ctrl-C), SIGTERM, or SIGHUP will cause the JVM to initiate its shutdown sequence, effectively triggering a System.exit().

There are two key steps that occur when the JVM shuts down:

In the first phase all registered shutdown hooks, if any, are started in some unspecified order and allowed to run concurrently until they finish. In the second phase all uninvoked finalizers are run if finalization-on-exit has been enabled. Once this is done the virtual machine halts.

In other words, as long as you prevent the code you're running from registering malicious shutdown hooks or calling runFinalizersOnExit() sending these signals will cause the JVM to terminate promptly.

If even that isn't acceptable for your use case send a SIGKILL instead; the OS will immediately cause the process to terminate, without giving it a chance to run shutdown hooks or finalizers. You can also call Runtime.halt() from within the JVM to accomplish roughly the same thing.

Community
  • 1
  • 1
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • So this won't stop the main thread right? Because I don't want my entire application to stop running - I want to just move on and execute the next java code snippet I'm given. Does this mean I need to run these snippets in their own processes? – Ogen Jun 25 '16 at 02:13
  • @Ogen In addition to the answer i think you should check [How do you dynamically compile and load external java classes?](http://stackoverflow.com/a/21544850) and [Sandbox against malicious code in a Java application](http://stackoverflow.com/a/502388) also there is a [java sandbox library](https://sourceforge.net/p/dw-sandbox/wiki/Java-Sandbox%200.3%20Documentation/) with a configurable maximum run time. – Onur Jun 25 '16 at 02:16
  • @Ogen Yes, you should run each snippet in an isolated JVM (note your original example called `System.exit()` which would similarly kill your main thread). This is a standard practice called [sandboxing](https://en.wikipedia.org/wiki/Sandbox_(computer_security)). With truly untrusted code you'd be better off running each snippet in an isolated virtual machine, not just a process. – dimo414 Jun 25 '16 at 02:22