Skip to content

JS objects, wrappers, and cross origin concerns 2013 08 07

Josh Matthews edited this page Aug 7, 2013 · 2 revisions

attending

jdm, kmc, jst, bz, jack, bholley, pcwalton, mrbkap

discussion

  • jst: wanted to set this up to give servo guys more info about document.domain and same origin etc. bobby is going to run through some of this stuff. he works on xpconnect and js wrappers, and he's pretty much in charge of that at this point. i tihnk servo will need most of this if not all of it. document.domain is one tricky piece. document.adoptNode comes into play (called implicity when nodes move pages).
  • bholley: if you appendChild that will effectively be an adoptNode if needed
  • bholley: in terms of the big design decisions there's a couple of topics we want to cover. bz and i spent a lot of time discussing adoptNode. there's spec discussion about what happens to prototype chains when adoptNode is used. the easiest thing to start with is document.domain. it's kind of a crappy feature; preceding postMessage. allows for XO communication. it's one of the biggest pain points in the spec. ebay breaks if you don't support it. any window can set it's efffective script origin to any sub domain. a.b.com can set domain to b.com. can't go all the way to com. that allows it to collaborate with other scripts that set it to b.com. the effective script origin is tagged that it's explicitly set. we don't need it to be fast. i would suggest if you are doing one task per origin, you should do one task per transitive origin closure. the original idea of it is that it alters your effective script origin. but the spec is moving away from the effective script origin. so we try to ignore document.domain. in gecko, we recompute all security wrappers coming in/out of the compartments where domain is set.
  • jst: do we have a principal in servo at all?
  • pcwalton: there's no security code yet. there's an implicit concept of an origin closure.
  • jst: is window.open implemented?
  • jdm: no
  • kmc: when you compute the closure, do you need to use the public suffix list?
  • jst: we have a list of ETLDs in gecko. (co.uk, etc). i don't know where the list comes from.
  • kmc: you said we can't change window.domain without breaking the web. is that the case in the future? or is it around to stay?
  • bholley: it's around to stay.
  • jst: it's been around for two decades, and it's unlikely that anyone is going to be successful without supporting it.
  • bholley: sites depend on doing really janky things with document.domain. in gecko we store the domain on the principal, which means principal is mutable (almost only way it is mutable). origins sometimes have refs to other origins. there is an aliasing that happens in the spec. when aliasing happens is the ESO (effective script origin) alised as well?
  • bz: i believe right now they are always aliased together.
  • bholley: if not aliased, i would suggest setting a bit on the compartment for docuemnt.domain, but if state can be shared then you have to have a refcounted principal object like in gecko.
  • jst: the important part to consider is what we run in what tasks. it may not be SO (same origin) on creation but may become SO when document.domain is set.
  • pcwalton: foo.com and mozilla.com can never become SO. iframes can set it as well? (yes) the simplest thing is to treat everything that is under the same ETLD into the same script task. that's the easiest way to solve the problem.
  • jst: another case that we need to deal with (may not need to be fast)
  • bholley: document.domain can be as slow as you want
  • jst: iframe can navigate and become any domain
  • pcwalton: we anticipated that. pipeliens are just a window, so it's possible for an iframe window might point to the same pipeline as another one.
  • jack: we dont' have any of the ESO stuff yet, we need more sophisticated logic for that. we currently just check domain+port.
  • jst: another thing that is worth mentioning is adoptNode. (missed) in gecko today we have a compartment per global. and in the case where we move a node from one document to another we have to deal with that. the jsobject or the node will have to be recreated in the new compartment. it messes with expandos and gets into ??. maybe you want to talk about some of the types of wrappers we use
  • bholley: this is currently in flux with the spec. what options are implementable for gecko/servo. the basic idea is that you move a node from one doc to another, and there is some c++ stuff that gets fixed up. let's take the simple case of appendChild of a node from a diff doc. what happens to the js visible reflector. this is most observable in terms of its prototype chain. does the chain stay the way it was pointing to old window, or new window. what we do in gecko is a transplant. we take the old js reflector and create a temp object, copy expandos off, and create a new js refelctors in new compartment. copy expandos over, and replace old js reflector with a cross-compartment wrapper proxy. for us the prorotype chain ends up in the new compartment. a great example of this is node.style. what happens with those? they are lazily created, so when you move a node what happens to this stuff? it's generally black magic. the other ways to do it are kind of rough becuase a lot of it makes GC behavior observable. for example in safari these things change after a GC. another proposal that bz and i were talking about is to maintain two seperate identities to the same thing. the tricky bit with servo is that wrappers are fused. it gets tricky because you need the same rust object and a different js object. bz suggested you allocate to make them end up in the same zone somehow. that gets pretty tricky. there's not a whole lot of answers here.
  • jdm: remind me how zones works again?
  • bholley: they are meta compartments. zones are where they are in memeory (the gc unit) and compartments are security containers.
  • bz: the canonical example is strings. if you want to move strings you put it in a zone and then it doesn't have to be copied if you move it in the same zone.
  • bholley: we invented compartments for gc problems but they were useful for other things so we ended up creating zones just for the gc bits.
  • jst: we had per-compartment GC and that was the first step. there was a lot of problems with cross-compartment wrappers. and an answer to that was zones. we still had cross compartment wrappers but none that reached out of the zone. one potential option for rust is to create a new node in the adopting document. rust's pointers to that node would not be preserved and that may be a problem. how big of a problem i don't know
  • bholley: the thing that servo needs to do that is the concept of a rust proxy. we kind of skrited around the identity problem in js because you can't detect when identity has changed. but unless you have a compartment model on the rust side, identity checks are goign to get confused. rust object + js object, and you can X that out and say it's not there and you must chase another poitner to get there.
  • pcwalton: my concern is it might slow down property access. how will this interact with the JIT getting properties off of rust objects? we'll need to add an extra check every time.
  • (missed)
  • pcwalton: maybe we could just fold this into COW DOM stuff. we already have this check (in theory) to see if we're on the fast path. maybe it's not a performance concern. it's a bit of a complexity concern i guess.
  • jack: how widely used is adoptNode?
  • bholley: we don't need it to be dromeao fast for this. there are a few times we transplate. hopping around with windowproxy is one place where we transplant. so is adoptNode. the other place is document.open. the basic idea is if you document.write before load fires it injects text into the html stream. if you do a document.open or document.write after the load happens, we eject all the content of the document and then we transplant the document to a new window and then make the existing document the document for the new window. in gecko we give you a new window, but webkit gives you the old window. we move the document over and that involves a transplant. and the question is what do we do for all the other crap that used to be in the doc. there's a question of which scope they live in. in the old world we had to iterate over the whole scope and transplant all the stuff. but with the new bindings we just leave this stuff behind in the old scope. that's mostly fine but we run into weirdnesses with .style.
  • jdm: need to transplant because you get a new compartment with the new window?
  • bholley: yes.
  • bz: chrome's behavior is sub-optimal with document.open.
  • bholley: the spec currently says to do what gecko does. if we found simplifications that we could make... adoptNode has to be a little performant but for document.open we don't care. adoptNode is a bigger concern and if you figure that out document.open will probably be fine. do you think the pointer chasing trick could work?
  • pcwalton: we already bought into this a bit with COW dom. if layout is busy then you have to do a pointer chase to find the dirty node. you can imagine a tagged pointer scheme. pointer to diryt node for layout, pointer to adoptNode, witha flag that says which kind it is. i want to make sure that the fast path (script only thing running and you are accessing a property) remains fast. and i don't think that changes the number of guards that you need if we do it right.
  • bholley: if none of these sounds bad bz and i can work on spec and see what happens.
  • pcwalton: i don't think this causes any issues with servo's design.
  • jst: should we give a short overview of xray wrappers?
  • pcwalton: does servo need xray wrappers?
  • bz; xray wrappers are needed any time you have privileged javascript touching other javascript. the basic problem they are trying to solve is that web pages can modify their objects. they can change things around so that appendChild is different. in the gecko world if you have extensions that touch nodes from script, they need to see the canonical methods because otherwise it becomes easy to cause extensions to misbehave (eg. ends up invoking window.open, it's not a good thing). so there are various ways to solve this problem: for example chrome just has separate js reflectors in separate worlds. the extensions and web pages get different reflectors that don't share expandos or anything.
  • bholley: the underlying object needs to be the same but the reflectors need to be different
  • bz: xrays have the ability to reach out and touch the untrusted object if you really have to.
  • jst: that's a feature that's importnat to extensions in FF. it's probably not important to our own chrome code.
  • bobby: i'm going to grep and see how many uses of wrappedJSObject there are
  • bz: you will need isolation between scripts touching webpage dom for browser and the other stuff.
  • jst: (missed) if we had started with no expandos in xrays, no way to punch through, we would have ended up in a different place, and we wouldn't have quite as complicated a path as we had
  • blake: any time we've had js objects that dind't behave exactly like js objects you end up in an uncanny valley (ed. in reference to not allowing expandos on xrays).
  • jst: you have a balance between annoyance of platform folks and users
  • bz: other place for xrays is XO (cross-origin) object reference in web pages. in practice that should only be window and location objects.
  • bholley: depending on how spec ends up there are vastly different implementation details. it may have important implications for servo, but i'm hoping i can fix that.
  • jst: back when we started, more things were XO accessible. so it wasn't just an easy hack for a small set of properties. i think we can do better in servo not having had that history to deal with.
  • bholley: the one thing that is really important to me is that servo makes sure that hte default is safe. in webkit if you get an object XO that is not window or location they don't have a security wrapper and you can pwn your way to whatever. in gecko anything that moves across scopes (compartments) get wrapped, we can make it opaque if it's not window or location. we should make sure not to do that. because they (webkit) don't have security wrappers they just do explicit security checks on window and location objects and not anywhere else because of performance. this is why our static invariants from the wrappers is helpful.
  • pcwalton: the specific implementation will probably need to be deferred until we start thinking about extensions.
  • bholley: a.com opens b.com in an iframe, a.com expects to be able to touch b.com via xray or chrome style seprate world.
  • pcwalton: i guess we can do the same thing that gecko does, but when running separate tasks then it will be more like a cpows.
  • bholley: one of teh things that E10s needs is xray cpows.
  • pcwalton: my dream here is an abstracted model where different rust tasks can be in differnet processes.
  • bholley: that's great. keep XO stuff as far away as possible. the interesting thing with the spec for location is whether you can set expandos on the location object and have them be visible XO. that will be a total nightmare for cross process.
  • jst: another thing is chrome object wrappers.
  • bobby: we don't want those in servo
  • jst: fair enough. depends on the app that surrounds servo. one of the things that's important in gecko is that if we leak a chrome object into untrusted code it needs to be able to do nothing with it. and cross origin policy alone is probably not enough.
  • bholley: in my opinion, in gecko we've allowed people to do this for a while. if servo just says that priveleged js objects are totally opaque from content, that's totally find. gabor in the jetpack team are working on apis that if yo uare doing privileged js we give you tools to do that without moving objects across the boundary.
  • jst: we can do better withou the history of baggage that gecko has.
  • jdm: wouldnt' we be able to do the chrome reflectors with the same mechanism as the wrappers (JS object + redirection pointer to rust object)?
  • bholley: the separate world thing is doable but xrays are more flexible. we already have the js proxy machinery to do them and gecko already uses that heavily so we might as well use that.
  • bz: as a counterpoint xrays are complicated. they have weird looking proto chains.
  • bobby: how are they weird?
  • bz: if i have a xray fora dom element. is the chrome side element.prototype on the chain of hte xray?
  • bobby: it should be. actually we should have an xray wrapper to the prototype in the content scope.
  • bz: there are some design tradeoffs here in the sense of how you want it to work.
  • jst: it's tricky
  • bz: the point is there are a lot more questions about how xrays should work. we think we've answered them but i can't guarantee that.
Clone this wiki locally