Inverting Your Assumptions: A Guide to JIT ComparisonsApril 12, 2018 | Jasiel Spelman
Similar to many others that have spent an unhealthy amount of their life staring at a computer screen, I have back issues. Having an office setup with proper ergonomics is an obvious first step towards avoiding back pain, but I've also found that getting exercise can help quite a bit. Rock climbing is one of the things I like to do that helps my back, but on days where I don't even have the opportunity to go to an indoor rock gym, I like to use the gravity inversion bar I have at home.
What does this have to do with anything interesting, let alone anything involving security? Between doing upside-down crunches, I'll sometimes spend some time just catching up on various feeds on my phone. This past January, after I'd already found some WebKit JIT bugs and gotten a decent understanding of one of the engines, I came across a link to a Stack Overflow post about a ridiculous interview question:
==). The other type is the strict comparison operator, which is represented by three successive equals signs (
===). The main difference is that if the types of both values are different, the loose comparison operator will perform type coercion to see if they can become the same type before being compared, while the strict comparison operator will return that the two values are different.
As a simple example, let's compare the Integer 1 with the
true constant as well as a String containing the number 1:
Both return true despite not being equal at all from a visual inspection. Let's compare that against what happens if we use strict equality comparison:
Back to the post that spurred all of this, a curious thing happens when you compare an Object to something that isn't an Object:
DFGAbstractInterpreterInlines.h, which contains a method named
executeEffects responsible for changing program state based on the operation and arguments. The way to state that an operation is potentially dangerous to prevent later optimizations is to call a function called
clobberWorld which, among other things, will break all assumptions about the types of all Arrays within the graph.
The net result of this is that if an operation is improperly modelled, it is possible to trigger type confusion and have a Double interpreted as an Object to trigger code execution, or have an Object interpreted as a Double to trigger an information leak.
Here is a snippet of how
DFGAbstractInterpreterInlines.h handles the
Here's a simple proof-of-concept demonstrating the issue:
This PoC demonstrates that, when comparisons are just so, fundamental assumptions made by the JIT engine can be broken. In the benign case, 'a==1 && a==2 && a==3' can evaluate to true, but in the worst case this can result in a compromise of the renderer process. Apologies if we just messed up your interview question, but now you’ll potentially get a more interesting answer!
Here is what happens when you run the proof-of-concept:
We get a crash when dereferencing
0x686374696c6c while calling
0x686374696c6c comes from adding 5 to
0x686374696c67, which itself comes from the
5.67070584648226e-310 value being written in the proof-of-concept, since that is the Double representation of
The patch for this is rather simple – ensure
clobberWorld is called by properly modeling the loose comparison operators. You’ll still be able to have 'a==1 && a==2 && a==3' evaluate to true, however, now important checks will not get removed if you do.