Closed
Bug 630377
Opened 14 years ago
Closed 14 years ago
Assertion failure: obj->isDenseArray() with evil sharp-variable use, array comprehension
Categories
(Core :: JavaScript Engine, defect)
Tracking
()
RESOLVED
FIXED
| Tracking | Status | |
|---|---|---|
| blocking2.0 | --- | final+ |
People
(Reporter: jorendorff, Assigned: jorendorff)
Details
(Whiteboard: [sg:critical?][hardblocker][fixed-in-tracemonkey][has patch])
Attachments
(2 files, 2 obsolete files)
|
18.76 KB,
patch
|
Details | Diff | Splinter Review | |
|
4.06 KB,
patch
|
brendan
:
review+
|
Details | Diff | Splinter Review |
function f(x) {
x.q = 1;
}
#1=[f(#1#) for (i in [0])];
Assertion failure: obj->isDenseArray(), at js/src/jsarray.cpp:2032
The Right Way to fix this would be to make sharp-variable uses that expose the object before it's done SyntaxErrors. #1# should be legal only as a value in a plain ArrayLiteral or ObjectLiteral.
I would love to get that changed, but we don't know what it'll break. For now it'll be easier to weaken that assertion to a runtime check.
| Assignee | ||
Comment 1•14 years ago
|
||
Making this s-s for now. I'm not sure it's exploitable.
Group: core-security
Updated•14 years ago
|
blocking2.0: --- → final+
Whiteboard: [sg:critical?][softblocker]
Comment 2•14 years ago
|
||
If this truly is a sg:critical bug, we need to fix it for 2.0. There's a testcase, seems it reproduces.
Whiteboard: [sg:critical?][softblocker] → [sg:critical?][hardblocker]
| Assignee | ||
Comment 3•14 years ago
|
||
I'm going to take a shot at making this a SyntaxError. Leaking objects to script before they're ready is just wrong. And scary.
If I can't make it work before lunch tomorrow, I'll give up and weaken the assertion to a runtime check.
Assignee: general → jorendorff
| Assignee | ||
Comment 4•14 years ago
|
||
LOL, you can't
var x = [#1="", #1#];
but you can
var x = #1=Math.sin(#1#);
Clearly this should return the least fixed point of the function.
| Assignee | ||
Comment 5•14 years ago
|
||
Ah. The precedence there is (#1=Math).sin(#1#). If I parenthesize:
var x = #1=(Math.sin(#1#));
then it throws an error. Not a SyntaxError; the program still parses and starts to execute. The bytecode is like this:
00010: getgname "Math"
00013: callprop "sin"
00016: usesharp 0 1
00021: call 1
00024: defsharp 0 1
At runtime, JSOP_USESHARP throws an exception since the corresponding JSOP_DEFSHARP hasn't executed.
| Assignee | ||
Comment 6•14 years ago
|
||
Here are the syntax changes I am implementing:
1. Sharp variable uses (#1#) will only be legal in two contexts:
[HERE] and {x: HERE}
These are the only two places where obj_toSource can emit them,
in the absence of custom .toSource methods, as far as I can tell.
So all these are errors:
var v = #1#; // SyntaxError
var v = #1=[], w = #1#; // SyntaxError
var v = #1=[(#1#)]; // SyntaxError
var v = #1={x: 1, y: #1#.x}; // SyntaxError
var v = #1=[f(#1#)]; // SyntaxError
v = #1=['x', #1#[0] + 'y']; // SyntaxError
2. A sharp variable def (#1=) must be immediately followed by an
ArrayLiteral or ObjectLiteral.
var v = #1=([#1#]); // SyntaxError
var v = #1=[#1#]; // OK
var v = #1=(Math.sin(#1#)); // SyntaxError
v = [#1=function () {}, #1#]; // SyntaxError
Array comprehensions are excluded.
var v = #1=[#1# for (x in a)]; // SyntaxError
3. Sharp variable defs (#1=[]) are AssignmentExpressions,
not PrimaryExpressions. So:
x = #1=[#1#]; // OK
uneval(#1={next: #1#}); // OK
print(#1=[].length); // SyntaxError
print((#1=[]).length); // OK
x = #1=a == 0 ? [] : [#1#]; // SyntaxError
x = #1=(a == 0 ? [] : [#1#]); // SyntaxError
x = a == 0 ? [] : #1=[#1#]; // OK
Math.#1=PI; // SyntaxError and go directly to jail
I confidently claim nobody is using the syntax I'm banning. Maybe a fuzzer somewhere. We have nothing to lose but some attack surface.
If I had time, I would implement additional restrictions to make these SyntaxErrors too:
x = [#42#]; // wrong use - runtime Error for now
x = #1=[function () [#1#]]; // wrong use - function throws for now
[#1=["a"], #1#, #1=["b"], #1#] // multiple defs - OK for now
print(#1={}, [#1#]); // unrelated exprs - OK for now
x = #1=[f([#1#])]; // #1# escapes! - OK for now
The last one is evil and I really wish I could kill it off, but it looks like that would require more invasive changes.
| Assignee | ||
Comment 7•14 years ago
|
||
Attachment #511795 -
Flags: review?(brendan)
Updated•14 years ago
|
Whiteboard: [sg:critical?][hardblocker] → [sg:critical?][hardblocker][has patch]
| Assignee | ||
Comment 8•14 years ago
|
||
Comment on attachment 511795 [details] [diff] [review]
v1
Brendan asked me to look into a narrower fix. I'll try weakening the assertion to an if-statement.
Attachment #511795 -
Flags: review?(brendan)
| Assignee | ||
Comment 9•14 years ago
|
||
Attachment #512318 -
Flags: review?(brendan)
Comment 10•14 years ago
|
||
Comment on attachment 512318 [details] [diff] [review]
simpler
Looks like the v1 patch from bug 633637.
/be
Attachment #512318 -
Flags: review?(brendan)
Comment 11•14 years ago
|
||
(In reply to comment #8)
> Comment on attachment 511795 [details] [diff] [review]
> v1
>
> Brendan asked me to look into a narrower fix.
The idea is to avoid regressing bug 335051 and making overlarge incompatible changes so late in the cycle.
Bug 486643 is about "deprecating sharp variables". Deprecation precedes removal (obsolescence) so that'll have to be later. Harmony WeakMaps give users the ability to implement toSource and uneval efficiently in JS itself. The Common Lisp inspired sharp syntax could be ditched in favor of something else that preserves alpha equivalence. The parsing extensions to match could perhaps come about via the quasis extension mechanism.
But this is all way, wayyyyy beyond this bug, and it arguably wouldn't entail any restrictions on sharp def parsing, _per se_.
/be
| Assignee | ||
Comment 12•14 years ago
|
||
Sigh, how'd that get in there. Here's the right patch.
Attachment #512318 -
Attachment is obsolete: true
Attachment #512488 -
Flags: review?(brendan)
Comment 13•14 years ago
|
||
Comment on attachment 512488 [details] [diff] [review]
simpler
>+ uint32_t length = obj->getArrayLength();
uint32 not uint32_t -- match the return type and avoid the _t tax (I see it was pre-existing below).
>+ if (obj->isSlowArray()) {
>+ /* This can happen in one evil case. See bug 630377. */
>+ jsid id;
>+ return js_IndexToId(cx, length, &id) &&
>+ js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE);
>+ }
>+
> JS_ASSERT(obj->isDenseArray());
>- uint32_t length = obj->getArrayLength();
> JS_ASSERT(length <= obj->getDenseArrayCapacity());
>
It looks like jstracer.cpp needs to change as well.
/be
| Assignee | ||
Comment 14•14 years ago
|
||
Attachment #512488 -
Attachment is obsolete: true
Attachment #512577 -
Flags: review?(brendan)
Attachment #512488 -
Flags: review?(brendan)
Updated•14 years ago
|
Attachment #512577 -
Flags: review?(brendan) → review+
Comment 15•14 years ago
|
||
Is this [can land]?
| Assignee | ||
Comment 16•14 years ago
|
||
It went in last night, sorry.
http://hg.mozilla.org/tracemonkey/rev/a4594bca8dd2
Whiteboard: [sg:critical?][hardblocker][has patch] → [sg:critical?][hardblocker][fixed-in-tracemonkey]
Updated•14 years ago
|
Whiteboard: [sg:critical?][hardblocker][fixed-in-tracemonkey] → [sg:critical?][hardblocker][fixed-in-tracemonkey][has patch]
Comment 17•14 years ago
|
||
Status: NEW → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
Updated•13 years ago
|
Group: core-security
You need to log in
before you can comment on or make changes to this bug.
Description
•