Java Puzzle 3: Car - Solution
Mar 04, 2012Let's see how we can get the upgraded car to break the speed limit. Take a closer look at the car:
Lesson to all you racers out there: We want the car to accelerate a lot (speed += acceleration
must execute), but not do that thing where it hits the tree and comes to a full stop (speed = 0
must not execute).
What can happen between those two statements? The if (speed > MAX_SPEED)
doesn't help us any further, because speed
really needs to be higher than MAX_SPEED
. So all that's left is the call to the crash()
method. That one has to fail: We want crash()
to crash!
We can do that by making sure there's not enough space left on the stack to be able to call any method. If the stack is almost full when we call accelerate
, we get a StackOverflowError
on the call to crash()
. How do we get the stack to be almost full? One way is to just brute-force it: an infinitely recursing method that just keeps trying.
And that works!... On some systems at least. A problem here is that if you're trying to accelerate so much and keep on crashing (after 1500 crashes on my system), the JVM does something no car maker ever would: it optimizes the car to ensure it can crash really fast. It in-lines the call to crash()
, reducing it to essentially if (speed > MAX_SPEED) speed = 0;
. That eliminates the possibility of a StackOverflowError
.
We can work around that by being just a little gentler on the car: crash it a bit less, so the JVM doesn't decide to optimise that. First grow the stack until it's getting close to where it needs to be, and only then add the car into the equation. But the only way to know you're getting close to the limit is by hitting it. Therefor we recurse without the car until we hit the stack overflow, then take a few steps back, and then apply the brute-forcing solution from above:
An alternative solution is to use Thread.stop. But it's quite hard to time it right; it doesn't work that reliable across systems. At first I expected it to be impossible in practice, but I was proven wrong. You can see the code in the comments below.
You do need a special permission to call that method, but that's one of the very few things included in the default security policy file. The only other permissions in there are opening listening sockets on high ports, and reading some standard system properties.
Conclusion
Not many succeeded in solving this one. The ones who did, and their solutions can be admired below as time-travelling comments that were posted before this blog post appeared.
The fact that this hack is possible makes me wonder. Is all privileged code in Java itself really prepared to crash at any time? Including all native code? If an applet makes the loading of a class fail in this way; does that cause NoClassDefFoundError
s in other applets? How else could this be abused?...
Please, don't try this at home. Car insurance usually doesn't cover the failing of the crashing of the crash. Even if you first tried crashing without the car; running with your head straight into the tree. Blowing up the car right before it hits the tree is not appreciated either.
In the next puzzle we'll sneak some goods passed airport security, so stay tuned!
26 comments
The solution was quite simple - recurse util stack overflows - then the crach method will not have been called
Had me stumped for a while (and made me look in Thread Javadoc for some clues).
Looking at this with javap (
javap -c -v -private 'driver.Driver$1'
) shows that calcStackDepth has a stack size of 3, but fillStackThenAccelerate only 2. That’s because it keeps “this”, “depth” and “1” (to increment depth++ by one) on the stack at the same time. So it doesn’t predict the stack depth entirely correctly. I’m not sure it’s off by enough to make a difference, but Heinz told me your solution didn’t work on his system (while my version did), so I guess it does matter.Ok, this one was tough. I did not manage to solve it before, so this is a new one for me too. I’ve solved it, as you can see in the log, but this does not always run. Another way I thought might work would be to almost exhaust the stack space and then call the accelerate method, making it crash when it is about to call crash(). But that did not work.
Nice one :)
This one works reliably on my machine (gives me between 2 and 7 Vroom! on every execution), but does not work at all on your server (tried it a few times). But I guess it just needs some more tweaking :-)
BTW I was surprised that Thread#stop() is not blocked by the security manager.
I had a similar solution, but it wasn’t very reliable.
t.stop() is known to be a misfeature that most should not use. The posted solution is way smarter and more reliable. I didn’t thought about it.
I didn’t consider I had a solution either with that one, as it rely on luck.
Most things here in the Java puzzlers is about exploiting misfeatures (like recursing in hashCode() or calling wait() without a loop. And maybe the reason I posted it anyway was (1) that hint on the webpage that it might not be possible to do this reliably on some environments, and (2) the fact that I am too much involved in information security, where you are “the hero” if you find an exploit that exploits a memory corruption vulnerability or a race condition like this one in 10% of the cases (although that depends on luck too) - even if there is another way to exploit it with 100% reliability like in this case …
The other problem might be that once I find a solution I usually don’t try to find a better one and since the security manager allowed me to stop() that thread, I submitted it like this.
Just to make it clear, I also believe that Wouter’s solution is far superior to this one.
Works on mine machine and on test page. Pretty sure it won’t work somewhere else, though by using initial probe and minimum count of iterations it could work on couple of other machines too ;)
To be more specific, we want to avoid crash() being inlined by JIT, so it should not be called many times, ideally just once and have stackoverflow exactly at that point.. though that is most likely really platform specific so getting exactly the point is not so easy that’s why we have the second iteration there with hope we’re +/- around the right depth and hence we can hit SOE before JIT.
The below works pretty reliable on my box. But of course it’s kind of ‘luck based’ and your sandbox puts limit on duration so can’t increase iterations.
The obvious idea is to stop thread after it increases the speed, but before it does crash().
It probably can be finessed more (with putting high load on CPU via other threads and setting low priority on executing thread and introducing some waits and stuff). But overall this solution is still luck-based and silly.
I’ll wait and see if someone’ll come with non-luck-based solution :)
Couldn’t get it to work reliably on my machine But this could work because of Thread.stop() releasing the monitors… The trick is to stop the thread right after adding to speed but before crash() beeing invoked.
Not really nice and reliable, but seems to work better with you server:
Here is my solution. It works only if you disable all optimization from the JVM, by using the -Xint parameter:
Here is my solution. You may have to change T and N depending on the machine to get better success rate :).
It looks to me like this is mostly dead code: deepAccelerate first calls accelerate0 and that calls deepAccelerate again and so on until it hits the StackOverFlowError. accelerate0() never returns normally (only with the error). so the part after that in deepAccelerate (accelerate1, accelerate2 and accelerate3) will never be called.
My fault. This is funny: my first solution never crashed my machine, whereas this solution always does. I’ll have to investigate. Could be that the larger number of methods somehow makes the JVM inline the crash() method too late?.
What I really wanted to achieve here is what you get when you remove the superfluous “deepAccelerate()” from the accelerate0-3 methods: using only one word more on the stack for each invocation of crash(). My assumption was that if we fill the stack too fast, it might overflow the call to accelerate instead of the inner call to crash(). Because this worked immediately for me, I did not find the buggy superfluous recursive call.
Found a solution that bets on a race condition which will only occur in multi-core cpu systems and also there not reliably (sigh).
This one was hard. First thought of stopping a tread using stop(), but that was too hard to time. The solution below works, but still have to find out why it cannot be done simpler. I could not remove the array that is not used at all. It will probably stop the stack from expanding or so. Well, fine. Having a solution, but still no clue…
Thanks for the fun!
Gerrit
================================================== Comments above this one were added on the original puzzle. Comments below when the solution was already available
This gets the result pretty reliably on my machine, though needs tweaking for other environments. Basic principle is as with the other Thread.stop() solutions, but also, it exhausts CPU with auxiliary threads and provides multiple stoppers.
That “including all native code” comment puzzles me. As far as I know, a switch to native code will switch the stack to a “native stack” (that has its own fixed size), and then switch the stack back on exit. That way, errors in native methods cannot that easily unintentionally corrupt the Java stack, and make the hs_err log files that are created on a SIGSEGV in native code (or on a SEH exception on Windows) more reliable. But the last time I really debugged a native method I did not check if that is still the case (in all cases - maybe it gets switched of when the execution counter of a method reaches a certain limit or when the calling method is JITed?). If it is, stack overflow in native methods should not be triggerable this way.
I must admit I don’t know much about how the invocation of native methods works. It made me wonder how that stuff deals with this problem, but I never really looked into that. I’m just throwing out some ideas hoping that maybe it might inspire someone else to discover something.
In short: I don’t know what I’m talking about; that’s why it’s posed as an open question.
That was a great puzzle, I tried this trick for the second puzzle but not for this one, very disappointed :P. Excellent work, keep it coming.
I do not understand the reason for being “gentler” to the car. There are not so many crashes, there is only one. All other calls to car.accelerate() are performed w/o overflow, only the deepest one fails, and after that we are done. I found out that on my system it was more sensible to the exact amount of stack available. So in my second solution I played with a growing number of arguments, to raise the stack by only 1 word for each try. This one worked reliable for my, but it still only “crashes the crash” only once.
There is only one “crash of the crash” (StackOverflowError). But all other calls still “crash”: they call the crash() method setting the speed to 0. It is by calling that method so many times (and not by it throwing a StackOverflowError) that it gets in-lined.
If you have trouble reproducing that, you could add this at the start of your program:
After doing that it will become impossible to still make it throw a StackOverflowError at the right place (unless your JVM really refuses to optimise anything).
In fact, the crash() method gets inlined because accelerate() is called that often. For more details, see the documentation of the -XX:CompileThreshold JVM option (or just reduce it to 1 which means every function gets optimized when it is exited the first time). Setting –XX:CompileTHreshold=1000000 should make it much easier to reproduce, but then you can also just say -Xint and disable all optimization.
Of course you are right. I misinterpreted “crash it a bit less, so the JVM doesn’t decide to optimise that”. I thought it meant “crash crash() less often” where you meant “crash() less often”.
Very nice puzzle! I was looking at
and kept thinking it should be
but couldn’t find a way to abuse that.
volatile creates a memory barrier, so it doesn’t need to be synchronized.
Nice one.
I’ve posted Thread.stop() ‘solution’ (which I didn’t really like), but stack overflow is really a clever one – didn’t think of it myself and hopefully will keep it in mind in the future :)
Although now thinking about it – how do you make sure it runs out of stack on crash() invocation and not, say, on recurse() or accelerate() [neither of which will produce the desired result… I think…]?
Our thread.stop solution
It is surprising that most JVM seems not to do tail queue recursion : http://en.wikipedia.org/wiki/Tail_call
What does in means about Scala ? Anyway, we must prevent it in this exercise, so it would be nice to add a dumb instruction after recurse to be sure we prevent it.
As we know now, system libraries are (or used to be) not always prepared to crash:
http://icedtea.classpath.org/hg/release/icedtea7-forest-2.3/jdk/rev/e46d557465da#l1.28
Ah, that’s an interesting one! Is there any reference to that change recognizing it as a security vulnerability?