Why we need to clean up __proto__

Mark S. Miller erights at google.com
Mon Dec 26 18:53:29 PST 2011


Or, "Even Crock's Code Doesn't Overcome All The Bad Parts."


In this message, I explain why I believe we need to clean up __proto__.
I'll start a separate thread on how to clean it up -- which is just my
documenting a suggestion from Dave Herman.


>From <
http://eleventyone.done.hu/OReilly.JavaScript.The.Good.Parts.May.2008.pdf>
PDF page 119

   that.on = function (type, ....) {
      ....
      var registry = {};
      ....
      if (registry.hasOwnProperty(type)) {
          registry[type].push(handler);
      } else {
          registry[type] = [handler];
      }
      ....
  };

This "registry" is a classic use of an object as a string-to-value
map. Note that the "type" parameter comes in from the client of the
abstraction. The only validation performed on it is the "hasOwnProperty"
check above. Elsewhere Crock points out that this hasOwnProperty check does
not work if "hasOwnProperty" itself is ever used as a key, and so we should
instead say:

      if (Object.prototype.hasOwnProperty.call(registry, type)) {

This still doesn't yet work on most actual browsers for a reason explained
below.



<aside on why ES5 does not really help>

Going beyond "Good Parts", EcmaScript 5 gives us two new opportunities to
clean up this code further.

1) We can protect ourselves from other code monkey patching
away Object.prototype.hasOwnProperty
and/or Object.prototype.hasOwnProperty.call by using <
http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming>
at initialization time, assuming that this module gets to initialize itself
before such monkey patching may have occurred:

  var bind = Function.prototype.bind;
  var uncurryThis = bind.bind(bind.call);

  var hopFn = uncurryThis({}.hasOwnProperty);

So that we can say

      if (hopFn(registry, type)) {

2) We can replace the initialization of "registry" with

      var registry = Object.create(null);

or, if we wish the same protection from post-initialization monkey patching

  var create = Object.create;
  ...
      var registry = create(null);

It turns out that neither of these additional ES5-based paranoid steps
helps with the worst problem remaining with Crock's corrected code.
Likewise, nothing so far proposed for ES-next would help, except for Dave
Herman's suggestion which I will write up shortly.

</aside on why ES5 does not really help>



As you've probably already guessed from the title of this email, the
problem occurs on most platforms when the function is called with an
argument of "__proto__". Since we do a "hasOwnProperty" check first, the
way in which this goes wrong depends on the platform. Evaluating

    [{}.hasOwnProperty('__proto__'),
{}.__proto__.hasOwnProperty('__proto__')]

on various browsers gives:

false,false       // Chrome
true,true         // WebKit Nightly
false,true        // FF Nightly
false,false       // Opera 12 alpha
throws TypeError  // IE10 Preview 2

Since IE doesn't treat "__proto__" as special magic, no problem there.
WebKit Nightly will take the branch that happens to fail safe here, but
only for the reason that there's no "Object.prototype.push". All we can say
is that we got lucky this time. When Chrome, FF, and Opera evaluate

      registry['__proto__'] = [handler];

they change registry to inherit from [handler] rather than
Object.prototype. WebKit would do the same if it had executed this line.

Think this exercise is academic and doesn't arise in real systems?  As
observed at <
http://www.google.com/support/forum/p/Google+Docs/thread?tid=0cd4a00bd4aef9e4>,
until recently, on all non-IE browsers, if you typed "__proto__" at the
beginning of a new Google Doc, your Google Doc would hang. This was tracked
down to such a buggy use of an object as a string map. (To avoid such
problems, Caja is shifting to using <
http://code.google.com/p/es-lab/source/browse/trunk/src/ses/StringMap.js>,
which does seem safe on all platforms.)


--
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20111226/2fc88d8a/attachment.html>


More information about the es-discuss mailing list