Closed Bug 570352 Opened 15 years ago Closed 15 years ago

js_SuppressDeletedProperty must check for iteration mutations under lookupProperty

Categories

(Core :: JavaScript Engine, defect)

Other Branch
defect
Not set
normal

Tracking

()

RESOLVED FIXED
Tracking Status
blocking2.0 --- betaN+
status1.9.2 --- unaffected
status1.9.1 --- unaffected

People

(Reporter: igor, Assigned: gal)

References

Details

(Keywords: regression, Whiteboard: [sg:critical?], fixed-in-tracemonkey [critsmash:patch])

Attachments

(1 file, 1 obsolete file)

With changes from the bug 569735 AFAICS there is a code path where a script can trigger native stack overflow. In particular, js_DeleteProperty calls js_SuppressDeletedProperty which in turn calls lookupProperty on the prototype of of the iterator object. But the latter can be a proxy function that in turn deletes a property of the object under enumeration leading to unbounded native recursion. I do not have a working example demonstrating the problem at this moment hence the question mark for the title of the bug.
I have to close this since there is another bug in js_SuppressDeletedProperty. It does not check that lookupProperty could mutate ni->props_end. If, for example, a proxy on the prototype would add and delete a property been enumerated then js_SuppressDeletedProperty would recurse and eventually the script can trigger a lot of ni->props_end-- leading to overflow etc.
Group: core-security
Is that bug in anything we've shipped? The deletion-suppression code was recently revamped completely, so I'm not sure if you're saying that the old (shipped) code is vulnerable and therefore we have to close it, or the new code is broken so we just need to block release on it. Is an overflowed (underflowed, if by --?) ni->props_end exploitable?
(In reply to comment #2) > Is that bug in anything we've shipped? The deletion-suppression code was > recently revamped completely, so I'm not sure if you're saying that the old > (shipped) code is vulnerable and therefore we have to close it, or the new code > is broken so we just need to block release on it. The bug is only in the new code and can only be exposed using recently added proxies. So we should just block the release. > > Is an overflowed (underflowed, if by --?) ni->props_end exploitable? This is hard to tell. The relevant code path is: if (idp == ni->props_cursor) { ni->props_cursor++; } else { memmove(idp, idp + 1, (ni->props_end - (idp + 1)) * sizeof(jsid)); ni->props_end--; } A script via recursion can trigger the execution of the second path a number of times and have some control over idp == ni->props_cursor checks. It would lead to something like memove(x, y, 4GB - some bytes). I do not know if that could be exploited.
Here is an example that crashes the js shell in memmove: var counter = 10; var skip = false; var p = Proxy.create({ has: function(name) { if (name == 'y' && !skip) { print(1); if (--counter == 0) return false; skip = true; obj.y = 2; skip = false; delete obj.y; } return false; }, enumerate: function() { return []; } }); var obj = { x: 0, y: 1, __proto__: p}; for (var i in obj) { delete obj.y; }
I am changing the title of the bug - there is no unbounded recursion here. Any script that lookupProxy may run will do the recursion checks on its own. So there is only memmove bug.
Summary: unbounded recursion with proxy->lookupProperty and the check for deleted properties under enumeration? → js_SuppressDeletedProperty must check for iteration mutations under lookupProperty
Attached patch patch (obsolete) — Splinter Review
Start over if a deletion occurs while handling the deletion. This is so ridiculously rare that I didn't bother trying to fix the state (walkind idp to the left should be sufficient, but its more complex).
Assignee: general → gal
Attachment #449503 - Flags: review?(igor)
Comment on attachment 449503 [details] [diff] [review] patch >+ jsid *props_end = ni->props_end; >+ for (jsid *idp = ni->props_cursor; idp < props_end; ++idp) { ... > /* >+ * If lookupProperty or getAttributes above removed a property from >+ * ni, start over. >+ */ >+ if (props_end != ni->props_end) >+ goto again; >+ >+ /* > * No property along the prototype chain steppeded in to take the > * property's place, so go ahead and delete id from the list. > * If it is the next property to be enumerated, just skip it. > */ > if (idp == ni->props_cursor) { > ni->props_cursor++; > } else { > memmove(idp, idp + 1, (ni->props_end - (idp + 1)) * sizeof(jsid)); > ni->props_end--; Suppose here the property is deleted in lookupProperty when idp == ni->props_cursor. Then the innermost recursive js_SuppressDeletedProperty can trigger ni->props_cursor++. If the previous invocation of js_SuppressDeletedProperty was for id when original(ni->props_cursor) + 1 == idp, then that would again increase ni->props_cursor. This way a script can move ni->props_cursor into ni->props_end. If the outermost js_SuppressDeletedProperty will see at this point that idp != ni->props_cursor then it will do ni->props_end-- leading to a situation when ni->props_cursor > ni->props_end. Due to the rest of code consistently using ni->props_cursor < ni->props_end and not ni->props_cursor != ni->props_end to check the end of iterations this ni->props_cursor > ni->props_end is harmles. But this rather fragile so I prefer to see more bulletproof solution. Then the check props_end != ni->props_end
Attachment #449503 - Flags: review?(igor)
Attached patch patchSplinter Review
Attachment #449503 - Attachment is obsolete: true
Attachment #449519 - Flags: review?(igor)
Whiteboard: [sg:critical?]
Not shipped, not on trunk, fix in hand. No need to hide this.
Group: core-security
Attachment #449519 - Flags: review?(igor) → review+
Whiteboard: [sg:critical?] → [sg:critical?], fixed-in-tracemonkey
Whiteboard: [sg:critical?], fixed-in-tracemonkey → [sg:critical?], fixed-in-tracemonkey [critsmash:patch]
Keywords: regression
Status: NEW → RESOLVED
Closed: 15 years ago
Resolution: --- → FIXED
blocking2.0: ? → betaN+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: