Closed Bug 1433291 Opened 6 years ago Closed 6 years ago

JSStructuredCloneReader reenters interpreter to convert array to property id (causing long runtime)

Categories

(Core :: JavaScript Engine, defect, P1)

x86_64
Linux
defect

Tracking

()

RESOLVED FIXED
mozilla60
Tracking Status
firefox60 --- fixed

People

(Reporter: decoder, Assigned: jorendorff)

Details

(Keywords: hang, testcase)

Attachments

(1 file)

Assignee: nobody → jorendorff
Status: NEW → ASSIGNED
Christian, does this help?
Flags: needinfo?(choller)
Confirmed, this solves the problem for me :) Thanks!
Flags: needinfo?(choller)
Comment on attachment 8945902 [details] [diff] [review]
Don't allow serialized data to use objects as property keys

Review of attachment 8945902 [details] [diff] [review]:
-----------------------------------------------------------------

Oh! That's amusing.

It does make me wonder why this seems reasonably fast:

var proto = {};
for (var i = 0; i < 1000000; i++) {
  o = Object.create(proto);
  o[o] = i;
}

How many properties is this test case trying to set?

::: js/src/vm/StructuredClone.cpp
@@ +2680,5 @@
> +                JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
> +                                          JSMSG_SC_BAD_SERIALIZED_DATA,
> +                                          "property key expected");
> +                return false;
> +            }

I don't follow the isSymbol case. It appears that when you serialize, Symbols are TypeErrors if you serialize them directly, or are silently ignored if they are property keys. I attempted to determine this from the spec, but the spec makes no sense to me: it says to go through each key in value.[[GetOwnPropertyKeys]]() and do something with every key that is of type String. But in the es spec, GetOwnPropertyKeys (well, OrdinaryOwnPropertyKeys) just returns the property keys, so by that I would say that we should be dropping all integer-keyed properties. If I ignore the String type check, then it seems like the Symbol key should get serialized, which is not a thing that can be done. So it does appear that the String check is intended to drop Symbols.

So if you read in a Symbol, shouldn't that also be an error? Based on the expected and observed behavior of Symbol keys getting dropped on the floor, even if Enumerable. This code seems to happily accept the Symbol as a key. Which is not the worst thing in the world since it appears impossible in practice (reading in the key will never produce a Symbol, no matter how much you've hacked the binary data), but still.
Attachment #8945902 - Flags: review?(sphink) → review+
This isn't sec-anything. Revising my patch...
Group: javascript-core-security
Keywords: sec-moderate
(In reply to Steve Fink [:sfink] [:s:] from comment #4)
> Oh! That's amusing.
> 
> It does make me wonder why this seems reasonably fast:
> 
> var proto = {};
> for (var i = 0; i < 1000000; i++) {
>   o = Object.create(proto);
>   o[o] = i;
> }

The test case isn't creating lots of properties; it's stringifying `Array(415153856)`. The properties don't exist.
...as it clearly says right here (points at lines and lines of meaningless numbers in test case)
(In reply to Jason Orendorff [:jorendorff] from comment #6)
> (In reply to Steve Fink [:sfink] [:s:] from comment #4)
> > Oh! That's amusing.
> > 
> > It does make me wonder why this seems reasonably fast:
> > 
> > var proto = {};
> > for (var i = 0; i < 1000000; i++) {
> >   o = Object.create(proto);
> >   o[o] = i;
> > }
> 
> The test case isn't creating lots of properties; it's stringifying
> `Array(415153856)`. The properties don't exist.

Oh! This is slow because it's stringifying a huge Array. I thought it was a fast stringification run many many times, as in my test case that produces the property name "[object Object]" over and over.

Ok, that makes sense. I can accept that generating a string with 415 million commas might take a while. I'm somewhat curious what horrific Rope-flattening shenanigans it might be doing here, but not enough to find out.
(and my initial assumption was dumb; why would a small structured clone buffer do lots of stringifications? I guess I was thrown off by the "reentering the interpreter thing", which I immediately assumed meant we were doing tons of JS <-> C++ crossings.)

I just had to check this a little bit. It looks like stringifying a huge empty array does a GetArrayElement->NativeGetProperty on each index. Or at least, those are the top 2 functions in |perf report|. We could make stringifying lots of array holes massively faster! Or not.
https://hg.mozilla.org/integration/mozilla-inbound/rev/663a36bf36a34363e9e934abe63c586c8c370725
Bug 1433291 - Don't allow serialized data to use objects as property keys. r=sfink.
Priority: -- → P1
https://hg.mozilla.org/mozilla-central/rev/663a36bf36a3
Status: ASSIGNED → RESOLVED
Closed: 6 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla60
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: