js::ClearStructuredClone can free any address

RESOLVED FIXED in Firefox 23

Status

()

RESOLVED FIXED
5 years ago
4 years ago

People

(Reporter: jorendorff, Assigned: sfink)

Tracking

({regression, sec-critical})

unspecified
mozilla25
regression, sec-critical
Points:
---

Firefox Tracking Flags

(firefox22 unaffected, firefox23 fixed, firefox24 fixed, firefox25 fixed, firefox-esr17 unaffected, b2g18 unaffected)

Details

(Whiteboard: [adv-main23+])

Attachments

(1 attachment)

(Reporter)

Description

5 years ago
ClearStructuredClone looks like it just scans the entire buffer looking for 8-byte chunks that start with SCTAG_TRANSFER_MAP, and then reads the next 8 bytes as a pointer and passes that to js_free.

This is bad because strings are written directly to the buffer; that is, the user controls bytes in the middle of the buffer.

On little-endian platforms, something like this could trigger a bad free:

    var buf = new Int8Array(8).buffer;
    postMessage(
        {s: "\u0123\u4567\u000f\uffff\ucdef\u89ab\u4567\u0123", b: buf},
        "*",
        [buf]);

It doesn't seem to happen on my machine but probably for superficial reasons.

The shell is unfortunately immune, because it doesn't support transferables yet, and even with sfink's patch "Add an optional parameter to the shell serialize() function for specifying Transferables" in bug 861925, it never calls ClearStructuredClone, so the buffer is simply leaked and the bug can't be triggered. In a locally hacked build I added the call from Serialize() to JS_ClearStructuredClone() and ran this:

    var buf = new Int8Array(8).buffer;
    serialize(
        {s: "\u0123\u4567\u000f\uffff\ucdef\u89ab\u4567\u0123", b: buf},
        [buf]);

which caused this output:

    js(33558) malloc: *** error for object 0x123456789abcdef: pointer being freed
    was not allocated
    *** set a breakpoint in malloc_error_break to debug
(Reporter)

Comment 1

5 years ago
OK. The TRANSFER_MAP_HEADER is set to SCTAG_TM_MARKED at the very beginning of reading. That disables the broken scan.

So the broken scan doesn't happen in a success case, and if reading fails, we don't even call ClearStructuredClone; we just leak any buffers that we own.

Similarly, if something fails during writing, we don't bother to call ClearStructuredClone; we just leak any buffers that we own.

So the only way this could trigger is if we serialize some objects but then decide *not* to deserialize them. Then we could end up calling ClearStructuredClone and triggering the bad scan, and freeing an arbitrary address.

We could probably dig something up by inspecting the code, we have JSAutoStructuredCloneBuffers all over the place. Easier to just fix it, I hope. sfink, can you patch this?
Summary: js::ClearStructuredClone can free any address (?) → js::ClearStructuredClone can free any address
(Assignee)

Updated

5 years ago
Assignee: general → sphink
(Assignee)

Comment 2

5 years ago
I think I may have changed this from a leak to something more dangerous in bug 868700. When you have the main thread and a worker firing postMessages at each other and the worker can't keep up, many of these messages will end up getting dropped and therefore serialized but never deserialized. Before bug 868700, these would end up leaked. Bug 868700 converts things over to use JSAutoStructuredCloneBuffer so that they will now get cleaned up automatically, potentially triggering this bug.
status-firefox23: --- → unaffected
status-firefox24: --- → affected
status-firefox25: --- → affected
Keywords: sec-high
Keywords: sec-high → sec-critical
(Assignee)

Comment 3

5 years ago
Created attachment 777413 [details] [diff] [review]
Do not read off the end of the transfer map

Unless I'm missing something, this seems like a straightforward fix.
Attachment #777413 - Flags: review?(jorendorff)
(Assignee)

Comment 4

5 years ago
Also, note that I intend to eliminate the serialized transfer map when I rewrite my patch in bug 861925, so the code this is fixing should go away then too. But this should be safe to backport.
status-firefox23: unaffected → ---
status-firefox24: affected → ---
status-firefox25: affected → ---
(Assignee)

Updated

5 years ago
Attachment #777413 - Flags: review?(jorendorff) → review?(jwalden+bmo)
(Assignee)

Comment 5

5 years ago
and it looks like I managed to reset all of the status flags
status-firefox23: --- → unaffected
status-firefox24: --- → affected
status-firefox25: --- → affected

Comment 6

5 years ago
Comment on attachment 777413 [details] [diff] [review]
Do not read off the end of the transfer map

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

That break is analogous to the one at jsclone.cpp:1230, in JSStructuredCloneReader::readTransferMap(), right?  r=me if so, as that seems to be fairly clear about the structure of what's being parsed here.  If not, clearly (pun intended) I need to do more reading to understand this.
Attachment #777413 - Flags: review?(jwalden+bmo) → review+
(Assignee)

Comment 7

5 years ago
Comment on attachment 777413 [details] [diff] [review]
Do not read off the end of the transfer map

[Security approval request comment]
How easily could an exploit be constructed based on the patch?

In combination with bug 868700, very very easily. Without that, I'm not sure, but it would be at least moderately difficult.

Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?

The comment kind of does: "If..., we've walked off the end of the transfer map."

Which older supported branches are affected by this flaw?

It was introduced in bug 789593.

If not all supported branches, which bug introduced the flaw?

Bug 789593

Do you have backports for the affected branches? If not, how different, hard to create, and risky will they be?

They'll be the same. The patch is very simple, and works on code that hasn't changed since it landed.

How likely is this patch to cause regressions; how much testing does it need?

Very unlikely. Regressions would most probably take the form of memory leaks.

Do you have anything else to add?

This patch should be considered in combination with the patch on bug 868700, which is a straightforward memory leak fix that greatly widens the exposure from this bug. Bug 868700 has already landed and made it to Aurora. We'd like to pull it into beta too. Reading that patch doesn't really point to this flaw (or the existence of this flaw), but we really wouldn't want to ship anything with bug 868700 landed and not this bug.

What did you have for lunch?

Slightly stale muesli with almond milk, but I don't see how that's relevant to sec-approval. You're overreaching yourself here.
Attachment #777413 - Flags: sec-approval?
Comment on attachment 777413 [details] [diff] [review]
Do not read off the end of the transfer map

[Triage Comment]

sec-approval+ and I marked this approved for Aurora since it doesn't effect Beta or before. Let's get this in trunk first and if it is all green, then Aurora.
Attachment #777413 - Flags: sec-approval?
Attachment #777413 - Flags: sec-approval+
Attachment #777413 - Flags: approval-mozilla-aurora+
(Assignee)

Comment 9

5 years ago
(In reply to Al Billings [:abillings] from comment #8)
> Comment on attachment 777413 [details] [diff] [review]
> Do not read off the end of the transfer map
> 
> [Triage Comment]
> 
> sec-approval+ and I marked this approved for Aurora since it doesn't effect
> Beta or before. Let's get this in trunk first and if it is all green, then
> Aurora.

Just to be extra-paranoid -- the status-firefox23:unaffected isn't entirely accurate. Specifically, the bug is present starting in firefox23, it's just harder to trigger (as in, I don't know of any way, but there's a decent chance some way exists.) But also, the memory leak bug 868700 *does* affect firefox23, so we'd like to pull that fix in, in which case this bug becomes easily exploitable.

tl;dr: I think we want this on beta too.

So is it still ok for me to land on trunk right now?

Thanks!
status-firefox23: unaffected → affected
Flags: needinfo?(abillings)
We need to get input from Release Management about beta first. I've poked them in IRC and am going to Needinfo? them here.
Flags: needinfo?(abillings)
works for me - we can take it in beta now and it will go out in our 5th week of beta builds next week. if there's any fallout (which doesn't seem likely) we still have time to backout.
Comment on attachment 777413 [details] [diff] [review]
Do not read off the end of the transfer map

All right. Thanks. Good to go!
Attachment #777413 - Flags: approval-mozilla-beta+
https://hg.mozilla.org/mozilla-central/rev/641e812ba1ad
Status: NEW → RESOLVED
Last Resolved: 5 years ago
status-firefox25: affected → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla25
status-firefox22: --- → affected
Whiteboard: [adv-main23+]
Not sure why firefox22 was marked as affected: comment 9 seems pretty clear that it's a regression in 23 (and only got bad as a security problem in 24).

That would mean ESR17 and b2g18 are clear, too.
Blocks: 789593
status-b2g18: --- → unaffected
status-firefox22: affected → unaffected
status-firefox-esr17: --- → unaffected
Keywords: regression
Group: core-security
You need to log in before you can comment on or make changes to this bug.