Open Bug 1348341 Opened 7 years ago Updated 2 years ago

Implement OOP JIT

Categories

(Core :: JavaScript Engine: JIT, enhancement, P3)

enhancement

Tracking

()

People

(Reporter: ehsan.akhgari, Unassigned)

References

(Blocks 1 open bug, )

Details

(Keywords: parity-edge, sec-want)

If we had a way to run out JIT out of process, we could remove the permission to mark pages as executable from our content process and put an end to remote code execution vulnerabilities.  Edge has started to do this apparently.  More details to follow.
"put an end to remote code execution vulnerabilities" too optimistic, but it will certainly eliminate one path to getting there and I endorse it.

Does this bug need to be hidden? Microsoft's plans are public and this is not a vulnerability which would put people at risk by being public.
Flags: needinfo?(ehsan)
Group: core-security → javascript-core-security
Keywords: sec-want
This is definitely interesting and something we should think about, but it's going to be extremely difficult even for somebody familiar with all of our JITs.

* There are a bunch of different places that compile JIT code (Baseline, Ion, Wasm Baseline, irregexp, ICs). I think for this to be effective we would have to move each of them out of process for the most part, to make it harder for a hijacked content process to trick the JIT process into allocating malicious executable code.

* We patch JIT code for a number of reasons (less than before because W^X also has overhead, but still) and it seems we should stop doing that or make it happen OOP/async.

* The IPC layer would need to do a lot of validation to guard against compromised content processes.

As a first step we could check how Chakra handles these things.
Maybe Cretonne [0] is going to make this easier for Ion. If we do it at that level it might be slightly less difficult, but we probably won't use Cretonne for Baseline, Wasm Baseline, irregexp etc.

[0] https://github.com/stoklund/cretonne
Is this something we can see Microsoft forcing (by taking away the privilege to toggle page permissions) at some point in the future?
I am trying to dump some ideas in a small list form:

Assumptions:
 * The attacker has write access on the entire content process. (stack & heap)
 * The content process should not be able to re-protect pages as executable.

Some conclusions:
 - The JIT process should be the only one to have priviledges to re-protect pages as executable.
 - The JIT process should have a tiny set of priviledges. (to make it a hop and not a final target)
 - JIT code has to manipulate a stack and data coming from the content process.
   - maybe: Map content process pages as shared memory?
 - JIT code might call DOM functions.
   - Use IPC to make DOM calls (… extremely slow ?…)
 - Execution of JIT code should prevent access of memory pages which are not JIT code / content process memory.
   - (JIT code & content process memory is RX) XOR (CodeGenerator data & code are RX)  {safest approach, but slow}
   - Add a validation for doing …?
     - non-JIT code calls are within a pre-registered set of functions.
     - Fails if any non-JIT code is executed. (bound checks?)
     - Fails if a random start address is requested. (control flow integrity?)
     - Fails if the stack pointer try to hijack the returned value.
     - Note: The content process can generate any code which pass any validation.

If we want to do it as part of our current implementation, a validation pass should be at the MacroAssembler level.
To make it happen at the MacroAssembler level, the first steps would be:
 - To convert our MacroAssembler to provide no-feedback on the success/failures of operations (No more JumpOffset, BufferOffset, …)
 - To serialize our MacroAssembler in some-sort of RPC stream.
 - To validate the MacroAssembler stream for the above properties (reconstruct a CFG?)
 - And finally dispatch the stream of MacroAssembler call to the various functions.

(In reply to Jan de Mooij [:jandem] from comment #3)
> Maybe Cretonne [0] is going to make this easier for Ion.

Unfortunately it would make this simple for WASM-Ion, but not for JS-Ion, at least not with the intended set of features of Cretonne.  The latest plan discussed was to use Cretonne assembly to generate the Assembler used by our MacroAssembler, and to use Cretonne as a back-end for WASM-Ion code.
> Use IPC to make DOM calls (… extremely slow ?…)

This doesn't really seem viable.

The way I would expect this to work is that you have jitcode mapped into both processes, mapped writable in the JIT process and executable (but not wirtable) in the content process.  That is, I think the "attacker has write access to the entire heap" assumption isn't necessarily true.
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #6)
> The way I would expect this to work is that you have jitcode mapped into
> both processes, mapped writable in the JIT process and executable (but not
> wirtable) in the content process. […]

Then, the content process still have the ability to map pages as executable.  In which case an attacker could forge the content of a page with some content and get it mapped with the same function that we use for mapping JIT code in the content process.

The content process has the ability to call CreateFileMapping, and the attack would basically have to take the handle and forward it to the content process function which would use MapViewOfFile to map the page as executable.

(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #6)
> […] That is, I think the "attacker has write
> access to the entire heap" assumption isn't necessarily true.

Can you think of any attach which is able to break through the X^W protection, without requiring the attacker to get access over the entire heap of the process?

Without breaking X^W protection, you have to inject content while the JIT is generating code, but this still required to be able to detect the compilation buffer address and write into it.

Is there any other way to get custom code execution inside the content process?
  (jsctype, chromium-sandbox, xpcom/build/nsWindowsDllInterceptor.h)?
Hmm.  I guess for my setup to really make sense you would need a "can't mark pages X if they have _ever_ been W" and vice versa protection.  That is, not just "X^W" at any point in time, but across all time.  I don't know whether such a thing exists...
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #8)
> Hmm.  I guess for my setup to really make sense you would need a "can't mark
> pages X if they have _ever_ been W" and vice versa protection.  That is, not
> just "X^W" at any point in time, but across all time.  I don't know whether
> such a thing exists...

Windows has this, Arbitrary Code Guard(ACG). The kernel then enforces that a process can't allocate executable pages or change permissions of executable pages. Chakra's JIT was moved OOP so it still works with ACG: the JIT process is allowed to map executable pages in the address space of the content process.
(In reply to Daniel Veditz [:dveditz] from comment #1)
> "put an end to remote code execution vulnerabilities" too optimistic, but it
> will certainly eliminate one path to getting there and I endorse it.

I don't think this is too unrealistic, at least for the subset of users which won't be running any other JITs in the content process, we will be able to make the content process unable to mark pages as executable.

> Does this bug need to be hidden? Microsoft's plans are public and this is
> not a vulnerability which would put people at risk by being public.

I don't know what the right process is for our sec-want bugs.  What I wanted is to get the right eyes on this bug which has already happened.  If you think it's appropriate, please open it up!
Flags: needinfo?(ehsan)
(In reply to Jan de Mooij [:jandem] from comment #9)
> (In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no
> decent message, automatic r-) from comment #8)
> > Hmm.  I guess for my setup to really make sense you would need a "can't mark
> > pages X if they have _ever_ been W" and vice versa protection.  That is, not
> > just "X^W" at any point in time, but across all time.  I don't know whether
> > such a thing exists...
> 
> Windows has this, Arbitrary Code Guard(ACG). The kernel then enforces that a
> process can't allocate executable pages or change permissions of executable
> pages. Chakra's JIT was moved OOP so it still works with ACG: the JIT
> process is allowed to map executable pages in the address space of the
> content process.

BTW on iOS Safari also uses some kernel support for creating executable pages that aren't writable using a different trick, by losing the address of the writable mapping to the page: https://www.blackhat.com/docs/us-16/materials/us-16-Krstic.pdf.  (Not sure if the same facility is available for use on macOS but it would probably be interesting research material.)
Priority: -- → P3
(In reply to :Ehsan Akhgari (busy) from comment #11)
Yeah, that's a neat way to avoid the need to flip permissions (also probably an optimization by reducing mprotect() calls).  FWIW, I think plain mmap()/MapViewOfFile() are sufficient for this, no special kernel needed.
Group: javascript-core-security
On the topic of ACG, I have opened Bug 1381050 and performed some preliminary experimenting on how it behaves at https://github.com/tomrittervg/sandboxsandbox/blob/master/output/acg.txt
FYI, some info on Chakra and what Edge is doing - most of this is focused on a Chrome vulnerability, but it has info on the OOP JIT in Edge.

https://blogs.technet.microsoft.com/mmpc/2017/10/18/browser-security-beyond-sandboxing/
We will want to look at if we want one OOP JIT per Content (or Master) process, or one shared by all (or one for all Content Processes, and one for Master).  This is a tradeoff between security and memory use, and the overhead for each JIT process would be a primary question.  Also an analysis of the security impacts of sharing an OOP JIT process should be done.

Dan, who would be a good person to do the first rough evaluation of the security impacts here?  (Detailed evaluation can occur later I suspect).
Flags: needinfo?(dveditz)
tjr's interested in taking a crack at the evaluation.
Flags: needinfo?(dveditz)
Flags: needinfo?(tom)
(In reply to Randell Jesup [:jesup] from comment #15)
> We will want to look at if we want one OOP JIT per Content (or Master)
> process, or one shared by all (or one for all Content Processes, and one for
> Master).  This is a tradeoff between security and memory use, and the
> overhead for each JIT process would be a primary question.  Also an analysis
> of the security impacts of sharing an OOP JIT process should be done.
> 
> Dan, who would be a good person to do the first rough evaluation of the
> security impacts here?  (Detailed evaluation can occur later I suspect).

I've completed the evaluation here: https://docs.google.com/a/mozilla.com/document/d/13HwuPNxabIONDVTKInMkkIitXxcB_kmBjMTjA7dHtSE/edit?usp=sharing
Flags: needinfo?(tom)
This whitepaper from Google Project Zero is about bypassing Edge's JIT server mitigations: https://github.com/google/p0tools/blob/master/JITServer/JIT-Server-whitepaper.pdf
Blocks: 1474451
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.