> When an X-ray wrapper is used to access an object in another compartment,
we run the C++ code without switching to the object's compartment. So
whenever a C++ XPCOM method uses the JSAPI, it has to switch compartments,
just in case it's being called from chrome.

It's not just chrome. It's anyone who uses Xray behavior. This includes all
cross-origin access, as well as any access by sandboxes that haven't set
wantXrays to false. A big consumer of this is jetpack content scripts.

> bholley thinks the nub of the problem is that C++ code shouldn't use the
JSAPI directly; instead it should use C++ helper classes that automatically
switch compartments as needed. I disagree. This strikes me as sweeping the
problem under the rug. The root of all evil here is the x-ray wrapper
behavior. An object's methods should never be called in a state where
GetCurrentJSContext() returns a cx that's in some other compartment.

Well, it's not really that simple. The "object" referred to in the above
paragraph is a C++ object, not a JS object. It generally knows nothing
about its JS reflection. This is why this Xray behavior has generally
worked fine for years. The problem happens when people try to find and
access said reflection, usually by way of the wrapper cache. A related
problem is that people are creating typed arrays with the cx compartment,
stashing them (and presumably CCing them) on the C++ object, and (later)
expecting them to be same-compartment with the object in the wrapper cache.
This has become more of a problem recently because more DOM APIs do that
sort of thing. But it's still not the common case.

So I agree that the Xray behavior exacerbates this problem. But from a
theoretical perspective, it's not an Xray problem. C++ code could equally
end up in those methods with some differently-compartmented cx on the
stack. The problem is that C++ code is assuming that the compartments of
GetCurrentJSContext() and xpcomObj->GetWrapper() are guaranteed to be
same-compartment, when they're not.

> Part 1 - Fix bug 751926 exactly as bz originally suggested, over
bholley's objections in comment 3. Make x-ray wrappers always change
compartments to the wrapped object's compartment before calling its methods.

My objections in comment 3 were not to the proposal you're offering. They
were to bz's proposal to have two compartments per context. I still believe
that is a bad approach.

>
> (We will have to use a hack or two to keep GetSubjectPrincipal working as
it currently does, since it currently relies on js::GetContextCompartment.
This doesn't sound bad to me. The status quo is much worse.)

This seriously understates the significance of this problem. Indeed, this
is the _entire_ problem. The reason Xrays don't enter the target
compartment is so that they can execute with the privileges and context of
the caller. Let me elaborate on why this is important.

First, being able to pull the principal directly off the cx compartment (a
long struggle that I finally got landed at the beginning of the summer) is
huge. It fixed a gigantic class of security bugs that had plagued us (and
kept certain security researchers busy) for years. Keeping track of the
subject principal is conceptually and practically analogous to making sure
we're in the right compartment. So divorcing the two just means that we're
bound to get the former wrong, except that we don't get any handy
assertions when that happens. They're also hard to diagnose. Any JS hacker
can diagnose (and fix) a compartment mismatch. Diagnosing an incorrect
principal is much harder.

Moreover, I've been overhauling a lot of the security architecture to
simplify security checks. One of the basic premises here is that C++ can
infer its security relationship to an object being manipulated just by
inspecting it. Once we know that the cx compartment (and thus principal)
matches the object principal, the presence or lack of security wrappers in
our "view" of the object tells us everything we need to know. This was the
foundation for the 100k patch I landed removing all the cx-passing around
the DOM bindings (sfink and bz were quite pleased).

In general, the idea of a.com script entering b.com's compartment while
retaining a.com's principal terrifies me, in both directions. Especially in
the cross-origin case, I think the two domains have no business entering
each others' compartments.

bholley
_______________________________________________
dev-tech-js-engine-internals mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals

Reply via email to