Closed
Bug 993768
Opened 11 years ago
Closed 11 years ago
Crash [@ js::Nursery::forwardBufferPointer] or Opt-Crash on heap
Categories
(Core :: JavaScript: GC, defect)
Tracking
()
VERIFIED
FIXED
mozilla31
Tracking | Status | |
---|---|---|
firefox29 | --- | unaffected |
firefox30 | + | verified |
firefox31 | + | verified |
firefox-esr24 | --- | unaffected |
b2g-v1.2 | --- | unaffected |
b2g-v1.3 | --- | unaffected |
b2g-v1.3T | --- | unaffected |
b2g-v1.4 | --- | fixed |
b2g-v2.0 | --- | fixed |
People
(Reporter: decoder, Assigned: jonco)
References
Details
(5 keywords, Whiteboard: [jsbugmon:update,ignore])
Crash Data
Attachments
(6 files, 3 obsolete files)
(deleted),
text/plain
|
Details | |
(deleted),
patch
|
terrence
:
review+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
Waldo
:
review+
abillings
:
sec-approval+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
Waldo
:
feedback+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
jonco
:
review+
lsblakk
:
approval-mozilla-beta+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
Details | Diff | Splinter Review |
The following testcase crashes on mozilla-central revision 8883360b1edb (run with --fuzzing-safe):
var SECTION = "";
gcPreserveCode()
function test() {
var f32 = new Float32Array(10);
f32[0] = 5;
var i = 0;
do {
f32[i + 1] = f32[i] - 1;
SECTION += 1;
} while (f32[i]);
} test();
Reporter | ||
Comment 1•11 years ago
|
||
Crash traces:
Program received signal SIGSEGV, Segmentation fault.
js::Nursery::forwardBufferPointer (this=0x1831be0, pSlotsElems=0x7fffffffc3f8) at js/src/gc/Nursery.cpp:446
446 JS_ASSERT(IsWriteableAddress(*pSlotsElems));
#0 js::Nursery::forwardBufferPointer (this=0x1831be0, pSlotsElems=0x7fffffffc3f8) at js/src/gc/Nursery.cpp:446
#1 0x00000000006405f9 in UpdateIonJSFrameForMinorGC (frame=..., trc=<optimized out>) at js/src/jit/IonFrames.cpp:923
#2 js::jit::UpdateJitActivationsForMinorGC (rt=<optimized out>, trc=0x7fffffffc0c0) at js/src/jit/IonFrames.cpp:1201
#3 0x0000000000533e6b in js::Nursery::collect (this=<optimized out>, rt=0x1830e90, reason=JS::gcreason::ALLOC_TRIGGER, pretenureTypes=0x0) at js/src/gc/Nursery.cpp:777
#4 0x0000000000859774 in MinorGC (reason=JS::gcreason::ALLOC_TRIGGER, rt=0x1830e90) at js/src/jsgc.cpp:5078
#5 Collect (reason=JS::gcreason::ALLOC_TRIGGER, gckind=js::GC_NORMAL, budget=0, incremental=true, rt=0x1830e90) at js/src/jsgc.cpp:4951
#6 Collect (rt=0x1830e90, incremental=true, budget=0, gckind=js::GC_NORMAL, reason=JS::gcreason::ALLOC_TRIGGER) at js/src/jsgc.cpp:4896
#7 0x00000000007c5c48 in js::InvokeInterruptCallback (cx=0x1845a00) at js/src/jscntxt.cpp:1020
rax 0x40a00000 4647714816530579456
rdx 0x1830e90 25366160
=> 0x501368 <js::Nursery::forwardBufferPointer(js::HeapSlot**)+88>: mov (%rax),%rdx
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e4d2b7 in ?? ()
#0 0x00007ffff7e4d2b7 in ?? ()
#1 0x0000000000000000 in ?? ()
rcx 0x0 0
r10 0x40a00000 4647714816530579456
=> 0x7ffff7e4d2b7: movss (%r10,%rcx,4),%xmm0
0x7ffff7e4d2bd: ucomiss %xmm0,%xmm0
Dangerous crash with weird address, assuming sec-critical for now.
status-firefox31:
--- → affected
Component: JavaScript Engine → JavaScript: GC
Keywords: sec-critical
Whiteboard: [jsbugmon:update,bisect]
Reporter | ||
Comment 2•11 years ago
|
||
Reporter | ||
Updated•11 years ago
|
Flags: needinfo?(terrence)
Assignee | ||
Comment 3•11 years ago
|
||
Reproduced.
#0 0x00000000005e6066 in IsWriteableAddress (ptr=0x4080000040a00000)
at /home/jon/work/dev/js/src/gc/Nursery.cpp:419
#1 0x00000000005e5ff5 in js::Nursery::forwardBufferPointer (this=0x1cfc198,
pSlotsElems=0x7fffffffaf00) at /home/jon/work/dev/js/src/gc/Nursery.cpp:442
#2 0x0000000000830331 in js::jit::UpdateIonJSFrameForMinorGC (trc=0x7fffffffa9a0,
frame=...) at /home/jon/work/dev/js/src/jit/IonFrames.cpp:924
#3 0x0000000000830167 in js::jit::UpdateJitActivationsForMinorGC (rt=0x1cfb400,
trc=0x7fffffffa9a0) at /home/jon/work/dev/js/src/jit/IonFrames.cpp:1202
#4 0x00000000005e6fc8 in js::Nursery::collect (this=0x1cfc198, rt=0x1cfb400,
reason=JS::gcreason::ALLOC_TRIGGER, pretenureTypes=0x0)
at /home/jon/work/dev/js/src/gc/Nursery.cpp:775
#5 0x0000000000a5df20 in js::MinorGC (rt=0x1cfb400, reason=JS::gcreason::ALLOC_TRIGGER)
at /home/jon/work/dev/js/src/jsgc.cpp:5091
...
0x4080000040a00000 is the contents of the Float32Array. So there is a pointer to the array data on the JIT stack that needs updating, but there's no forwarding pointer set.
Assignee: nobody → jcoppeard
Assignee | ||
Comment 4•11 years ago
|
||
The JIT may keep pointers to the base of the array data on the stack, so we need to be able to forward these. The data can be inline in the fixed slots, so we need to add a forwarding pointer in that case. Also, make sure there are enough slots to fit the forwarding pointer in.
Attachment #8404048 -
Flags: review?(terrence)
Updated•11 years ago
|
status-firefox30:
--- → unaffected
status-firefox-esr24:
--- → unaffected
tracking-firefox31:
--- → ?
Keywords: regression
Comment 5•11 years ago
|
||
Comment on attachment 8404048 [details] [diff] [review]
bug993768-typedArrays
Review of attachment 8404048 [details] [diff] [review]:
-----------------------------------------------------------------
Great! r=me
Attachment #8404048 -
Flags: review?(terrence) → review+
Assignee | ||
Comment 6•11 years ago
|
||
Assignee | ||
Comment 7•11 years ago
|
||
Comment 8•11 years ago
|
||
sorry had to back this out for test failure like https://tbpl.mozilla.org/php/getParsedLog.php?id=37570917&tree=Mozilla-Inbound
Assertion failure: !isInside(src->getPrivate()), at /builds/slave/m-in-osx64-d-00000000000000000/build/js/src/gc/Nursery.cpp:591
Assignee | ||
Comment 9•11 years ago
|
||
The following assertion that I added in Nursery::forwardTypedArrayPointers() was failing:
JS_ASSERT_IF(typedArray.buffer(), !isInside(src->getPrivate()));
Which would in fact be better written as:
JS_ASSERT_IF(typedArray.buffer(), !isInside(typedArray.dataPointer()))
Do you know how this can happen as I didn't think we allocated array buffer contents in the nursery any more?
It concerns me that whatever causes this isn't exercised by any of our tests.
I'm going to do a try run with the assertion taken out, but if it is legal maybe we need to forward the buffer data too.
Assignee | ||
Comment 10•11 years ago
|
||
The problem that was causing this crash seems to be that when array buffer objects are neutered, their views are updated twice - once by neuter() and then again by changeContents(). This second update happens before the buffer's data pointer is set to the new value and results in the view's data pointers being set to a garbage value.
I don't know this code too well but it seems we don't need to update the views twice in this case. Steve, does this look right to you?
Attachment #8405442 -
Flags: review?(sphink)
Comment 12•11 years ago
|
||
Comment on attachment 8405442 [details] [diff] [review]
arraybuffer-neuter
Review of attachment 8405442 [details] [diff] [review]:
-----------------------------------------------------------------
Ouch. This looks like breakage from bug 982974. Bug 982974 wallpapered over a class of bugs by giving neutered objects a dummy chunk of data to point at. A second patch updated views' data pointer to point at the dummy data, which introduced this bug.
Can you make the cosmetic updates in my review and then request re-review from :Waldo? I really want a second set of eyes on this.
Waldo: seems like I screwed up the bug 982974 part 2 review. This is what it looks like is happening:
ABO::neuter(dummyBuffer):
foreach view
view.data = dummyBuffer
ABO::changeContents(dummyBuffer)
foreach view
view.data += dummyBuffer - buffer.data
buffer.data = dummyBuffer
So every view's data pointer gets set to dummyBuffer + dummyBuffer - the original buffer.data. Before the bug 982974 part 2 workaround, view.data would have been NULLed out and the second update would have no effect.
This mangled pointer won't be accessible during normal conditions, since the view.length is zero, but with the bug 982974 hack you'll be able to read/write through it. (And GGC will stumble across it, apparently.) This seems harder to exploit, at least.
Not relevant, but for the record, here is the mainline sequence of versions:
1. c9f1ddebc404 - original code
2. 355266055005 - Bug 982974 part 1 landed, making neutered ABOs point to dummy data
3. ce6a8fa5db7d - bhackett's change to store ABO metadata in the object instead of in an elements header
- this reshuffled all the code, but didn't really change anything relevant to this bug 993768
- in particular, the first loop over the views in the above pseudocode used to be called neuterViews()
4. e82736761361 - additional bug 982974 fix to point views at the dummy data, introducing this bug 993768
5. 1f27124a6e08+f7ff31a0bbd3 - backout+reland of the above
::: js/src/vm/ArrayBufferObject.cpp
@@ +367,5 @@
> }
> }
>
> void
> +ArrayBufferObject::changeBufferContents(JSContext *cx, void *newData)
Can you change this to take a FreeOp*? Looks too scary otherwise (like it could GC or something.)
I also think it should have a different name. "Changing buffer contents" implies more to me than just updating the data pointer. Though this function also juggles ownership. How about acceptOwnedData()? Kind of clunky. setNewOwnedData()? Either works for me.
And assert newData != old data, at least if ownsData(), since an unconditional free makes me nervous. Alternatively, since the caller already does exactly this check, you could move the check into the top of this function and early-return if they're the same.
@@ +379,5 @@
> + setDataPointer(static_cast<uint8_t *>(newData), OwnsData);
> +}
> +
> +void
> +ArrayBufferObject::changeContentsAndUpdateViews(JSContext *cx, void *newData)
I think this can then just be changeContents() or changeBufferContents().
@@ +395,5 @@
> uint8_t *viewDataPointer = view->dataPointer();
> if (viewDataPointer) {
> JS_ASSERT(newData);
> + size_t offset = viewDataPointer - oldDataPointer;
> + viewDataPointer = static_cast<uint8_t *>(newData) + offset;
I don't think I like using size_t for this, since it's a signed value being stored in an unsigned type. ptrdiff_t seems more appropriate.
Attachment #8405442 -
Flags: review?(sphink)
Assignee | ||
Comment 13•11 years ago
|
||
Attachment #8405442 -
Attachment is obsolete: true
Attachment #8406721 -
Flags: review?(jwalden+bmo)
Reporter | ||
Updated•11 years ago
|
Whiteboard: [jsbugmon:update,bisect] → [jsbugmon:update]
Reporter | ||
Comment 14•11 years ago
|
||
JSBugMon: Bisection requested, result:
=== Tinderbox Build Bisection Results by autoBisect ===
The "good" changeset has the timestamp "20140407112905" and the hash "14b5fbfa2163".
The "bad" changeset has the timestamp "20140407114801" and the hash "e35851f07b67".
Likely regression window: https://hg.mozilla.org/integration/mozilla-inbound/pushloghtml?fromchange=14b5fbfa2163&tochange=e35851f07b67
Comment 15•11 years ago
|
||
The first patch seems to fix some (pdf.js) JIT top crashes (under IonCannon), like this one:
https://crash-stats.mozilla.com/report/index/1405738f-a727-43a9-9600-352bb2140414
(Debug build crashes in forwardBufferPointer.)
Comment 16•11 years ago
|
||
Sigh, the landing of (probably) bug 945152 bitrotted this. Still going to look somewhat, and hopefully understand things.
Comment 17•11 years ago
|
||
Comment on attachment 8406721 [details] [diff] [review]
bug993768-arraybuffer-neuter v2
Review of attachment 8406721 [details] [diff] [review]:
-----------------------------------------------------------------
I grok this much better than I used to. It looks good as a spot-fix. (I think we need to return to the drawing board, once all this neutering mess is fixed security-wise, and rewrite the whole algorithm to be much much more obviously simple and correct by construction. I don't think that's something to even attempt backporting, except at absolute extremes.)
However, the bitrotting for bug 945152 realistically requires another pass here, I think. Post a patch that addresses that, and I'll compare it against whatever I come up with today, and hopefully we'll both be on the same page and can move on with this.
Also, joy of joys, you probably should start working on branch backports. Happily, bug 945152's changes here won't get in the way of those backports. (Consider this patch r+'d as concerns the state of code a week ago, for purposes of backporting. If the backports require too many extra changes, flag sfink/me as usual, of course.) Nope, only fun will be the constant stream of changes we've made to this code over the last several cycles, ESRs, b2gs. :-\
::: js/src/vm/ArrayBufferObject.cpp
@@ +397,5 @@
> uint8_t *viewDataPointer = view->dataPointer();
> if (viewDataPointer) {
> JS_ASSERT(newData);
> + ptrdiff_t offset = viewDataPointer - oldDataPointer;
> + viewDataPointer = static_cast<uint8_t *>(newData) + offset;
Hoo-rah for getting rid of += here. Lots more understandable to be re-extracting the old offset, then applying it to the new data pointer here.
Attachment #8406721 -
Flags: review?(jwalden+bmo) → feedback+
Comment 18•11 years ago
|
||
Steve, the current code in the bitrotted part looks like this:
if (buffer->isMappedArrayBuffer())
buffer->changeContents(cx, nullptr);
else if (newData != buffer->dataPointer())
buffer->changeContents(cx, newData);
Why isn't the mapped-array-buffer arm doing the same thing the other arm is doing, in terms of allocating an equal-sized backing buffer to avoid the out-of-bounds accesses that were an issue before? Because memory? If so, we should back out (yet again, sigh) this XHR fix until we have the neutering issue *actually* fixed, with no one using stale pointers or lengths to do any indexing. In theory that's relatively soon -- patches to fix all the typed array/arraybuffer methods are in bug 991981, with only JSAPI users remaining to be fixed. So it wouldn't be so bad, I think.
Flags: needinfo?(sphink)
Comment 19•11 years ago
|
||
(In reply to Jeff Walden [:Waldo] (remove +bmo to email) from comment #18)
> Steve, the current code in the bitrotted part looks like this:
>
> if (buffer->isMappedArrayBuffer())
> buffer->changeContents(cx, nullptr);
> else if (newData != buffer->dataPointer())
> buffer->changeContents(cx, newData);
>
> Why isn't the mapped-array-buffer arm doing the same thing the other arm is
> doing, in terms of allocating an equal-sized backing buffer to avoid the
> out-of-bounds accesses that were an issue before? Because memory? If so,
> we should back out (yet again, sigh) this XHR fix until we have the
> neutering issue *actually* fixed, with no one using stale pointers or
> lengths to do any indexing. In theory that's relatively soon -- patches to
> fix all the typed array/arraybuffer methods are in bug 991981, with only
> JSAPI users remaining to be fixed. So it wouldn't be so bad, I think.
You're right. Bug 999140.
Flags: needinfo?(sphink)
Assignee | ||
Comment 20•11 years ago
|
||
Updated patch.
Racing with sfink's patch in bug 999140.
Attachment #8406721 -
Attachment is obsolete: true
Attachment #8410390 -
Flags: review?(jwalden+bmo)
Comment 21•11 years ago
|
||
Comment on attachment 8410390 [details] [diff] [review]
bug993768-arraybuffer-neuter v3
Review of attachment 8410390 [details] [diff] [review]:
-----------------------------------------------------------------
Yeah, given that other patch just removes the if-mapped special case, that seems easy to rebase through.
Attachment #8410390 -
Flags: review?(jwalden+bmo) → review+
Updated•11 years ago
|
Assignee | ||
Comment 22•11 years ago
|
||
Comment 23•11 years ago
|
||
https://hg.mozilla.org/mozilla-central/rev/956c2f9e62ff
https://hg.mozilla.org/mozilla-central/rev/2fc0375a7dd2
status-b2g-v1.2:
--- → unaffected
status-b2g-v1.3:
--- → unaffected
status-b2g-v1.4:
--- → unaffected
status-b2g-v2.0:
--- → fixed
status-firefox29:
--- → unaffected
Flags: in-testsuite+
Target Milestone: --- → mozilla31
Reporter | ||
Updated•11 years ago
|
Whiteboard: [jsbugmon:update] → [jsbugmon:update,ignore]
Reporter | ||
Comment 24•11 years ago
|
||
JSBugMon: The testcase found in this bug no longer reproduces (tried revision c318868b33e9).
Updated•11 years ago
|
Group: javascript-core-security
Comment 25•11 years ago
|
||
Erm, doesn't this fix need backporting/application to other branches? Nothing about the fix as I remember it, conditions it to only trunk. (In which case, it should have gotten sec-approval before landing, too.)
Flags: needinfo?(jcoppeard)
Assignee | ||
Comment 26•11 years ago
|
||
(In reply to Jeff Walden [:Waldo] (remove +bmo to email) from comment #25)
Ah, you're right, the typed array issue was caused by bug 982974 and is also present in 30.
Flags: needinfo?(jcoppeard)
Assignee | ||
Comment 27•11 years ago
|
||
Comment on attachment 8410390 [details] [diff] [review]
bug993768-arraybuffer-neuter v3
[Security approval request comment]
Requesting approval post landing - sorry, I should have done this before landing the patch.
How easily could an exploit be constructed based on the patch?
Not trivially.
Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?
I now wish I had chosen a more subtle commit message. But either way I don't think it would be that easy to create an exploit.
Which older supported branches are affected by this flaw?
Version 30.
If not all supported branches, which bug introduced the flaw?
Bug 982974.
Do you have backports for the affected branches? If not, how different, hard to create, and risky will they be?
The same fix should apply fine.
How likely is this patch to cause regressions; how much testing does it need?
Unlikely.
Attachment #8410390 -
Flags: sec-approval?
Assignee | ||
Comment 28•11 years ago
|
||
I'm not proposing to land the GGC fix on older versions as GGC was only enabled by default in 31.
Comment 29•11 years ago
|
||
Comment on attachment 8410390 [details] [diff] [review]
bug993768-arraybuffer-neuter v3
Sec-approval+ for landing.
Attachment #8410390 -
Flags: sec-approval? → sec-approval+
Comment 30•11 years ago
|
||
(In reply to Al Billings [:abillings] from comment #29)
> Sec-approval+ for landing.
Reminder to land this patch :)
Flags: needinfo?(jcoppeard)
Assignee | ||
Comment 31•11 years ago
|
||
(In reply to Jan de Mooij [:jandem] from comment #30)
So it turns out that this doesn't apply cleanly to the 30 branch after all :( I'm working on making a patch that does at the moment.
Flags: needinfo?(jcoppeard)
Assignee | ||
Comment 32•11 years ago
|
||
Here's a patch to apply the same fix to version 30.
I'm requesting re-review because it's security related and the base code is fairly different. I had forgotten this had changed so much recently.
Attachment #8418069 -
Flags: review?(jwalden+bmo)
Comment 33•11 years ago
|
||
Let's get this into Beta and we're done.
Reporter | ||
Updated•11 years ago
|
Status: RESOLVED → VERIFIED
Reporter | ||
Comment 34•11 years ago
|
||
JSBugMon: This bug has been automatically verified fixed.
Tagging this for QA verification once it's fixed on 30.
Whiteboard: [jsbugmon:update,ignore] → [jsbugmon:update,ignore] [qa+]
Assignee | ||
Comment 37•11 years ago
|
||
Comment on attachment 8418069 [details] [diff] [review]
bug993768-arraybuffer-neuter-beta
[Approval Request Comment]
Bug caused by (feature/regressing bug #): Bug 982974
User impact if declined: Potential security issue
Testing completed (on m-c, etc.): Fix on m-c for several weeks, although the fix for beta required backporting due to substantial changes in this source file
Risk to taking this patch (and alternatives if risky): Low
String or IDL/UUID changes made by this patch: None
Attachment #8418069 -
Flags: approval-mozilla-beta?
Flags: needinfo?(jcoppeard)
Comment 38•11 years ago
|
||
Waiting for the review of the patch before uplifting it...
Updated•11 years ago
|
Group: javascript-core-security
Comment 40•11 years ago
|
||
Waldo can we get a review here so it can be uplifted if all's good?
Flags: needinfo?(jwalden+bmo)
Comment 41•11 years ago
|
||
Comment on attachment 8418069 [details] [diff] [review]
bug993768-arraybuffer-neuter-beta
Review of attachment 8418069 [details] [diff] [review]:
-----------------------------------------------------------------
I'm...not entirely sure that bug 999755 didn't interfere with this patch at the margins. I've stared at this several hours now and feel like *something* beta-specific is maybe off about it, or off in combination with current beta tip. But it's also 02:00 here, and I've been awake for 20ish hours now, so like it or not I'm not going to be able to push the rest of the way through this now, not confidently. Back in the morning for another go. Maybe by then a newer patch will be up, and/or you can make a convincing argument that this is sane in some edge case that's tickling my mind that I can't quite figure out right now?
Assignee | ||
Comment 42•11 years ago
|
||
Thanks for spending so much time looking at this.
Bug 999755 adds a way for test code to request whether to change the buffer contents or not when neutering, and adds test code that uses it. This allows the new possibility of neutering certain types of array buffer without replacing their contents.
This patch splits out the part of ArrayBufferObject::changeContents() that replaces the elements into setNewData(), leaving changeContents() to call this after updating the views. It's a straight copy so existing callers of changeContents() operate as before. setNewData() is only called in one place outside of this, which is ArrayBufferObject::neuter(), and this in turn is only called from js::NeuterArrayBuffer() and ArrayBufferObject::stealContents(), both of which previously call neuterViews() to update the views. So in these cases the views will only get updated once, which is what we want to happen.
Possible interactions would have to come from not replacing the array data in places where this previously would have happened and this somehow making a difference to what happens. ArrayBufferObject::neuter() does conditionally call setNewData() (previously changeContents()) based on whether we are changing the data, but js::NeuterArrayBuffer() always updates the views regardless of whether it's replacing the data.
So it looks ok to me, although I am by no means an expert on this code.
Did you figure out what it was that was bothering you?
Assignee | ||
Comment 43•11 years ago
|
||
Uploaded rebased patch.
Attachment #8418069 -
Attachment is obsolete: true
Attachment #8418069 -
Flags: review?(jwalden+bmo)
Attachment #8418069 -
Flags: approval-mozilla-beta?
Attachment #8430722 -
Flags: review?(jwalden+bmo)
Comment 44•11 years ago
|
||
I'll look at this in a couple hours and, if it checks out, push it to beta. (I have other beta stuff to land anyway, easy to watch.) Just to keep people apprised of status here...
Comment 45•10 years ago
|
||
Sigh, forgot about this yesterday in other-backporting rush.
I think I've figured out what looks screwball here. Consider ArrayBufferObject::stealContents. Its algorithm in beta, *right now*, before any patchwork here, is so:
1. Will the buffer's contents be stolen?
a. If so:
i. Allocate a new, zeroed header as newHeader.
ii. Let transferableHeader be the old header.
b. Else:
i. Allocate a new header, transferableHeader.
ii. Copy current data into transferableHeader.
2. Set outparams to transferableHeader info.
3. Call ArrayBufferObject::neuterViews, which will:
* throw if an asm.js buffer
* update pointers/lengths in all views of the buffer, invalidate JIT code
* remove buffer from GC lists a bit
4. If the buffer's contents will be stolen:
a. Call ArrayBufferObject::changeContents, which will
* AGAIN update pointers/lengths in all views of the buffer, invalidate JIT code
* clear this object's header's view list
* mutate this object's elements pointer
* reset the object's view list to its original value
5. Call ArrayBufferObject::neuter, which will
* Check if we're stealing stealable contents
* If so, call changeContents *again* and update pointers/lengths a *third* time
* Else, just mutate the object's elements pointer
* Zero the buffer's length
* Mark the object as a neutered buffer
That's three times we update pointers/lengths of the buffer's views, if the buffer's contents will be stolen. ("Three times! Ah ah ah ah!")
Your patch as you say doesn't change the semantics of changeContents at all. The only semantics that change are of ArrayBufferObject::neuter. So with your patch, we will update pointers/lengths/invalidate for all views in neuterViews. If the buffer's contents will be stolen, we'll update a second time for the changeContents call. And then ArrayBufferObject::neuter will *not* update. So in the non-contents-stealing case we'll have one update, good. But in the contents-stolen case we'll still have two updates.
This was a bit of a pre-existing problem, I think, that I only noticed from having read all this code way too many times across way too many branches recently. :-\ Given decreasing time and all, and your being across the pond and I think even technically on PTO right now, I'll see if I can't whip up a patch that addresses this issue as well as the one originally noted.
Flags: needinfo?(jwalden+bmo)
Comment 46•10 years ago
|
||
Or, no, not quite.
In step 5, we cannot be stealing stealable contents, because the changeContents in step 4 reverted to inline element storage. And inline element storage is not stealable. So we have *either* the update in step 4 xor the update in step 5. So max two all-views pointer/length updates. Your patch eliminates the redundant update in step 5. It does not eliminate the possible second update in step 4.
So there's still a problem, just not dire to quite the same degree as I diagnosed. But bad enough.
Updated•10 years ago
|
Comment 47•10 years ago
|
||
Up for review from the both of you in case one of you gets to it sooner, given the timing issues here.
Without this patch, a simple test like
var ab = new ArrayBuffer(256);
var ta1 = new Uint8Array(ab);
var ta2 = new Uint16Array(ab);
serialize(ab, [ab]);
will, if I look in a debugger, update view privates twice. With this patch, updating occurs only once.
This passes all jstests and jit-tests (the latter with --tbpl), *including* all the tests I've curated for bug 991981, bug 999651, bug 995679, bug 1009952, and bug 1011007. But, because neuter doesn't invoke stealContents (blargh), a separate try run is probably valuable here:
https://tbpl.mozilla.org/?tree=Try&rev=05d44e0f18d8
It'll be all sorts of orange for many things, to be sure, but hopefully it'll give good enough data to have reasonable landing confidence in it.
Attachment #8432051 -
Flags: review?(sphink)
Attachment #8432051 -
Flags: review?(jcoppeard)
Comment 48•10 years ago
|
||
And here's the full patch, if that happens to be more readable at all.
Comment 49•10 years ago
|
||
RyanVM asked about whether this change is needed anywhere else, I think somewhat as a double-check. So let's double-check, again flagging the both of you to race on this.
I see the same code pattern and issue in b2g28. But we don't need this there, because GCC isn't on in b2g28 (or even in 28 [?], for that matter). Is that right?
b2g26 has a different pattern, that doesn't do any pointer-rewriting. So it doesn't need a fix along these lines. (And from recent esr24 experience, that branch has the same different pattern, so no fix is needed there, either.)
(And, uh, I see bug 982974 landed in b2g26 and esr24, but we seem to be storing NULL as the data pointer for views when neutering. Which is not strictly a *problem* any more, now that bug 991981 is fixed [or just waiting approvals, in esr24's case]. But it does appear to me that the fake memory swapped into place in bug 982974 isn't actually *effective* for views of neutered buffers, in b2g26 or esr24. Blah. Such backporting. Very mistakes. Much mess. Ow.)
Flags: needinfo?(sphink)
Flags: needinfo?(jcoppeard)
Comment 50•10 years ago
|
||
Comment on attachment 8430722 [details] [diff] [review]
bug993768-arraybuffer-neuter-beta v2
So this is sort of okay as far as it goes, but it does need the extra hunk added to it for it to be a full fix. So, I guess f+? The moral equivalent of NS_SUCCESS_I_DID_SOMETHING here. Or something.
Attachment #8430722 -
Flags: review?(jwalden+bmo) → feedback+
Assignee | ||
Comment 51•10 years ago
|
||
Comment on attachment 8432051 [details] [diff] [review]
Delta atop posted patch, that gets rid of second round of view-neutering (and neutering to garbage pointers) in the case noted
Review of attachment 8432051 [details] [diff] [review]:
-----------------------------------------------------------------
Ah yes, I didn't spot that stealContents() was updating the views again as well. This patch takes out that update so the views should be left with the original update to newHeader by the call to neuterViews() above. So I think this is good now!
GGC is not enabled in any B2G yet so we're fine on that front. Comment 12 talks about the mangled pointer being visible via the hack in bug 982974, but if that is fixed then I think there are no more issues.
Attachment #8432051 -
Flags: review?(jcoppeard) → review+
Assignee | ||
Updated•10 years ago
|
Flags: needinfo?(jcoppeard)
Comment 52•10 years ago
|
||
Comment on attachment 8432051 [details] [diff] [review]
Delta atop posted patch, that gets rid of second round of view-neutering (and neutering to garbage pointers) in the case noted
[Approval Request Comment]
Bug caused by (feature/regressing bug #): bug 982974
User impact if declined: before bug 991981, crashes, conceivably sec-critical; after, potentially nothing *if* that patch is complete, but if it's not, it's probably still sec-critical
Testing completed (on m-c, etc.): previous patches landed on trunk/aurora, but this patch is somewhat different enough that that maybe doesn't say a huge amount; try-push with bug 991981, bug 999651, and the rest of that lot came back clean, tho -- and between JS tests and browser tests involving stealing/transfers, that should give pretty good code coverage
Risk to taking this patch (and alternatives if risky): we somehow are still screwing up transfers, with unclear consequences
String or IDL/UUID changes made by this patch: N/A
Attachment #8432051 -
Flags: review?(sphink) → approval-mozilla-beta?
Comment 53•10 years ago
|
||
Comment on attachment 8432051 [details] [diff] [review]
Delta atop posted patch, that gets rid of second round of view-neutering (and neutering to garbage pointers) in the case noted
OK we're taking this to RC landing because it's a sec-critical issue that has a testcase to verify with.
Attachment #8432051 -
Flags: approval-mozilla-beta? → approval-mozilla-beta+
Comment 54•10 years ago
|
||
Comment 55•10 years ago
|
||
Comment 56•10 years ago
|
||
Removing [qa+] flag and setting verified status due to comment 34.
Whiteboard: [jsbugmon:update,ignore] [qa+] → [jsbugmon:update,ignore]
Updated•10 years ago
|
Flags: needinfo?(sphink)
Comment 57•10 years ago
|
||
Setting status-firefox31 flag to match Status field.
Updated•9 years ago
|
Group: core-security
You need to log in
before you can comment on or make changes to this bug.
Description
•