Closed Bug 1092363 (CVE-2015-0826) Opened 10 years ago Closed 9 years ago

Heap-buffer-overflow in nsTransformedTextRun::SetCapitalization

Categories

(Core :: CSS Parsing and Computation, defect)

defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla38
Tracking Status
firefox33 --- unaffected
firefox34 --- unaffected
firefox35 + wontfix
firefox36 + fixed
firefox37 + fixed
firefox38 + fixed
firefox-esr31 --- unaffected
b2g-v1.4 --- unaffected
b2g-v2.0 --- unaffected
b2g-v2.0M --- unaffected
b2g-v2.1 --- unaffected
b2g-v2.2 --- fixed
b2g-master --- fixed

People

(Reporter: attekett, Assigned: heycam)

References

Details

(5 keywords, Whiteboard: [asan][adv-main36+][b2g-adv-main2.2-])

Attachments

(10 files)

Attached file repro-file.html
Tested on:

OS: Ubuntu 14.04

Firefox: ASAN build from https://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/mozilla-central-linux64-asan/1414761627/


From stack-trace I would say this is related to bug 1041512

ASAN-trace:

==21046==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000352148 at pc 0x7f5f34d4cb35 bp 0x7ffff9536760 sp 0x7ffff9536758
READ of size 8 at 0x60c000352148 thread T0
    #0 0x7f5f34d4cb34 in Length /builds/slave/m-cen-l64-asan-000000000000000/build/obj-firefox/layout/generic/../../dist/include/nsTArray.h:330:0
    #1 0x7f5f34d4cb34 in IsEmpty /builds/slave/m-cen-l64-asan-000000000000000/build/obj-firefox/layout/generic/../../dist/include/nsTArray.h:333:0
    #2 0x7f5f34d4cb34 in nsTransformedTextRun::SetCapitalization(unsigned int, unsigned int, bool*, gfxContext*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextRunTransformations.cpp:60:0
    #3 0x7f5f319942ca in nsLineBreaker::AppendText(nsIAtom*, char16_t const*, unsigned int, unsigned int, nsILineBreakSink*) /builds/slave/m-cen-l64-asan-000000000000000/build/dom/base/nsLineBreaker.cpp:291:0
    #4 0x7f5f31994cfb in nsLineBreaker::AppendText(nsIAtom*, unsigned char const*, unsigned int, unsigned int, nsILineBreakSink*) /builds/slave/m-cen-l64-asan-000000000000000/build/dom/base/nsLineBreaker.cpp:327:0
    #5 0x7f5f34d04465 in BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun*, void const*, unsigned int) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:2418:0
    #6 0x7f5f34cfa893 in BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:2323:0
    #7 0x7f5f34cf8a93 in BuildTextRunsScanner::FlushFrames(bool, bool) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:1462:0
    #8 0x7f5f34d015fd in BuildTextRunsScanner::ScanFrame(nsIFrame*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:1702:0
    #9 0x7f5f34d017dc in BuildTextRunsScanner::ScanFrame(nsIFrame*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:1712:0
    #10 0x7f5f34d06ee5 in BuildTextRuns /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:1385:0
    #11 0x7f5f34d06ee5 in nsTextFrame::EnsureTextRun(nsTextFrame::TextRunType, gfxContext*, nsIFrame*, nsLineList_iterator const*, unsigned int*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:2578:0
.
.
.
0x60c000352148 is located 12 bytes to the right of 124-byte region [0x60c0003520c0,0x60c00035213c)
allocated by thread T0 here:
    #0 0x471d71 in malloc _asan_rtl_:0
    #1 0x7f5f3139afc9 in AllocateStorageForTextRun /builds/slave/m-cen-l64-asan-000000000000000/build/gfx/thebes/gfxTextRun.cpp:108:0
    #2 0x7f5f3139afc9 in Create /builds/slave/m-cen-l64-asan-000000000000000/build/gfx/thebes/gfxTextRun.cpp:125:0
    #3 0x7f5f3139afc9 in gfxFontGroup::MakeSpaceTextRun(gfxTextRunFactory::Parameters const*, unsigned int) /builds/slave/m-cen-l64-asan-000000000000000/build/gfx/thebes/gfxTextRun.cpp:1907:0
    #4 0x7f5f34cfe45d in MakeTextRun<unsigned char> /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:566:0
    #5 0x7f5f34cfe45d in BuildTextRunsScanner::BuildTextRunForFrames(void*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:2164:0
    #6 0x7f5f34cf9120 in BuildTextRunsScanner::FlushFrames(bool, bool) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:1481:0
    #7 0x7f5f34d015fd in BuildTextRunsScanner::ScanFrame(nsIFrame*) /builds/slave/m-cen-l64-asan-000000000000000/build/layout/generic/nsTextFrame.cpp:1702:0
.
.
.
Flags: sec-bounty?
(In reply to David Baron [:dbaron] (UTC-7) (from comment #1)
> Mats, any idea why https://hg.mozilla.org/mozilla-central/rev/4e3e41e40b7e
> didn't help here?

Because DidSetStyleContext isn't called (nor is MarkIntrinsicISizesDirty)
on a bunch of text frames that have been restyled.

Here's a restyle log and the final frame tree when we start the reflow
that crashes.  Note that the text frame (7fffae0a1c30, black-on-grey)
that we reflow in this case is fine - it has a null text run. As the
stack shows, we poke around the frame tree to see if we can find an
existing text run that we can use, and bump into one of the text frames
with an invalid text run (7fffae0a2508, blue).

The interesting part is the restyle log.  It shows that we never call
RestyleContentChildren on its parent (7fffae0a2a78, lime). We do call
RestyleSelf on that chain though: magenta, cyan, lime, white, yellow,
but we only call RestyleContentChildren for the latter two.  I suspect
we should have called RestyleContentChildren also for the former.
Flags: needinfo?(mats)
Here's a patch that detects this bug in VerifyStyleTree.
I also added printing aParent in RestyleContentChildren to make it easier
to read the restyle log output.
You probably want to apply this patch temporarily as well if you're
debugging this, because that assert fails for this test.
Masayuki-san, the testcase in this bug triggers the IME assertion (in bug 1047588) quite reliably.
I'm guessing this is a regression from bug 931668 as I can reproduce it on Aurora, but not
Beta or Release branches.
Blocks: 931668
Severity: normal → critical
Flags: needinfo?(cam)
OS: Linux → All
Hardware: x86_64 → All
Whiteboard: [asan]
(In reply to Mats Palmgren (:mats) from comment #5)
> Masayuki-san, the testcase in this bug triggers the IME assertion (in bug
> 1047588) quite reliably.

Oh, that's what I'm creating a patch for. I still testing my patch, though, perhaps, it might help this.
While trying to reduce the test case a bit, I noticed that we can trigger the assertion in nsTextFrame::DidSetStyleContext with this very simple document.  It's not really a problem though since we'll have a reflow in the change list that we'll process soon after.  So maybe it is better to have this check only in VerifyStyleTree?
Flags: needinfo?(cam)
Also I don't seem to run into the VerifyStyleTree assertion from your comment 3 patch, at least here on OS X.
(In reply to Cameron McCormack (:heycam) from comment #9)
> While trying to reduce the test case a bit, I noticed that we can trigger
> the assertion in nsTextFrame::DidSetStyleContext with this very simple
> document.

The assertion in DidSetStyleContext gives a false positive when we
actually do get a MarkIntrinsicISizesDirty call that would fix it.

> So maybe it is better to have this check only in VerifyStyleTree?

It would be better (no false positives) if it weren't for the fact
that we don't always run VerifyStyleTree on the full tree, AIUI.


(In reply to Cameron McCormack (:heycam) from comment #10)
> Also I don't seem to run into the VerifyStyleTree assertion from your
> comment 3 patch, at least here on OS X.

Try NSPR_LOG_MODULES=styleverifytree:1 (or higher)
(In reply to Mats Palmgren (:mats) from comment #11)
> The assertion in DidSetStyleContext gives a false positive when we
> actually do get a MarkIntrinsicISizesDirty call that would fix it.

OK.  Seems like we'd get a lot of false positives then -- it's going to trigger any time we change the value of text-transform isn't it?

> > So maybe it is better to have this check only in VerifyStyleTree?
> 
> It would be better (no false positives) if it weren't for the fact
> that we don't always run VerifyStyleTree on the full tree, AIUI.

Fair enough.

> (In reply to Cameron McCormack (:heycam) from comment #10)
> > Also I don't seem to run into the VerifyStyleTree assertion from your
> > comment 3 patch, at least here on OS X.
> 
> Try NSPR_LOG_MODULES=styleverifytree:1 (or higher)

Thanks that works.
(In reply to Cameron McCormack (:heycam) from comment #12)
> OK.  Seems like we'd get a lot of false positives then -- it's going to
> trigger any time we change the value of text-transform isn't it?

Shouldn't that have generated lot's of assertions for existing tests then
when it landed (there was none)?  It seems unlikely we don't have
a single test changing the value of text-transform.

> (In reply to Mats Palmgren (:mats) from comment #11)
> > It would be better (no false positives) if it weren't for the fact
> > that we don't always run VerifyStyleTree on the full tree, AIUI.

OTOH, I see that we do call VerifyStyleTree unconditionally on some
frames in RestyleManager.  If that set includes the frames we call
DidSetStyleContext on, then we can replace the assertion.

For the crash testcase in this bug though, I'm pretty sure we need
to run VerifyStyleTree on the full tree to trigger that assertion.
Here's a reduced test that triggers the assertion in VerifyStyleTree.
And here's the restyle logging output.  It's surprising that when the text frames are restyled at the end that we end up returning eRestyleResult_Stop when we really should have had different style data (the text-transform change) and therefore returned eRestyleResult_Continue.
Attached file even smaller test case
Here's a smaller test case that triggers the VerifyStyleTree assertion if you change it to check for mTextTransform != NS_STYLE_TEXT_TRANSFORM_NONE (rather than looking for 'capitalize' specifically).
So here's what happening with the smaller test case.  The relevant part of the frame tree starts off as (after dropping the lines):

Block(body)(1)@11c370010 [content=1003ed380] [sc=11c370a60]<
  Inline(span)(0)@11c370830 next=11c3b3250 IBSplitSibling=11c3b3250 [content=11c3697d0] [sc=11c370788]<
    Text(0)"a"@11c3709a0 [content=11c369860] [sc=11c36fd58:-moz-non-element] [run=11a357400][0,1,T]
  >
  Block(span)(0)@11c3b3250 next=11c3b3358 IBSplitSibling=11c3b3358 IBSplitPrevSibling=11c370830
                           [content=11c3697d0] [sc=11c370f40:-moz-anonymous-block,parent=11c370788]<
    Block(div)(1)@11c370c70 [content=1003edba0] [sc=11c3708f8,parent=11c370788]<
      Text(0)"b"@11c370db0 [state=00000040b0600000] [content=11c3698f0] [sc=11c370d08:-moz-non-element]
                           [run=11c36c080][0,1,T]
    >
  >
  Inline(span)(0)@11c3b3358 IBSplitPrevSibling=11c3b3250 [content=11c3697d0] [sc=11c370788]<>
>

and the style context tree starting from the <span> looks like this:

11c370788(5) Text=11c370a10(dependent) parent=11c370a60          <span>
  11c370f40(1) Text=11c370a10(dependent) :-moz-anonymous-block   <span>'s IB split block wrapper
  11c3708f8(2) Text=11c370a10(dependent)                         <div>
    11c370d08(1) Text=11c370a10(dependent) :-moz-non-element     "b"
  11c36fd58(1) Text=11c370a10(dependent) :-moz-non-element       "a"

Then:

1. We have two restyles pending: one for the <span> and one for the <div>.
2. We start the restyle process by restyling the <span>.  It doesn't have any style changes, which means
   we end up swapping all of its structs to the new style context we just created for it.  We then
   recurse down and restyle the "a" frame, which again has no style changes (though here we return
   eRestyleResult_Stop and just leave its old style context there).
3. We get into this if statement:

     http://hg.mozilla.org/mozilla-central/file/tip/layout/base/RestyleManager.cpp#l2626

   The purpose of this ClearCachedInheritedStyleDataOnDescendants call is to ensure we don't leave
   descendants of the old style context with old cached struct pointers that were swapped into the new
   style context (which is what we did in step 2).  This ends up clearing the cached Text struct out
   from "b"'s style context.
4. When we eventually get to restyling "b", nsStyleContext::CalcDifference doesn't compare the Text structs
   because we don't have one cached on the old style context, since we cleared it in step 3.  But that
   makes us think that we never did request the Text struct and that it's OK to ignore.  Which it isn't.

So there are a few problems:

(a) I had thought ClearCachedInheritedStyleDataOnDescendants should only be run in situations where
    some other strong reference to a style context is kept, because normally oldContext (by the end of
    ElementRestyler::Restyle) should be about to go away.  This was intended to be an optimisation,
    but it looks like it could be stronger, since here we have a continuation with the same style,
    which we haven't restyled yet (the final <span> continuation in the frame tree above, the one
    after the anonymous block).

(b) ClearCachedInheritedStyleDataOnDescendants in fact isn't just an optimisation, but has an observable
    effect.  As mentioned in step 4, we interpret null cached struct pointers on a style context to
    mean that we never requested the struct and thus we don't need to compare it to the new style
    context (which would, if we forced its Text struct to be computed, have the new text-transform value).

(c) When we fill in aEqualStructs in nsStyleContext::CalcStyleDifference, we set bits indicating equality
    even for cases where the cached struct pointers were null.  This makes it easier back up in
    ElementRestyler::RestyleSelf to see if any structs were swapped.  But it also means we can wastefully
    swap null pointers between old/new style contexts in the SwapStyleData call that follows.  We use
    this same bitfield of swapped structs when calling ClearCachedInheritedStyleDataOnDescendants so
    we do a bit of wasted work in there too.

The main thing to do is fix (b) first.  Probably the easiest thing to do is not to write nulls into the cached style data blindly until we reach an owned non-null struct pointer, but to write nulls only over the struct pointers that we moved into the new style context.
(In reply to Cameron McCormack (:heycam) from comment #17)
> ElementRestyler::Restyle) should be about to go away.  This was intended
> to be an optimisation, but it looks like it could be stronger, since here we
> have a continuation with the same style, which we haven't restyled yet (the
> final <span> continuation in the frame tree above, the one after the
> anonymous block).

That's not quite right.  We *have* restyled the continuation (we've gone through the restyle-all-same-style-continuations, after all).  But we haven't restyled the IB anonymous block, whose child's style context still inherits from the <span>'s old style context.  That's where the remaining reference comes from.
(In reply to Cameron McCormack (:heycam) from comment #18)
> https://tbpl.mozilla.org/?tree=Try&rev=09a7a3e8e8ed

On top of a non-broken inbound revision this time: https://tbpl.mozilla.org/?tree=Try&rev=f08a1993bf44
Attached patch patchSplinter Review
Assignee: nobody → cam
Status: NEW → ASSIGNED
Attachment #8522689 - Flags: review?(dbaron)
Blocks: 1099110
Group: layout-core-security
Comment on attachment 8522689 [details] [diff] [review]
patch

Please call the aStyleContext parameter to
ClearCachedInheritedStyleDataOnDescendants
and DoClearCachedInheritedStyleDataOnDescendants
aNewContext instead.

>+      } else if (aStyleContext->GetCachedStyleData(i) == aStyleContext) {
(twice)

This test doesn't make sense.  You're comparing a struct to a style
context, which will never be true.

(It compiles because the style struct is a void*.)


I'm still working on understanding the main issue here, but marking
review- on the basis of that test.


sorry for the delay getting to this.
Attachment #8522689 - Flags: review?(dbaron) → review-
When I fix the cached struct comparisons, my newly added reftest fails.
What did you fix them to be?
Flags: needinfo?(cam)
      } else if (mCachedInheritedData.mStyleStructs[i] ==
                   aStyleContext->GetCachedStyleData(i)) {

and similar for the other case.
Flags: needinfo?(cam)
What's the status of this bug? will the fix be ready for v35?
Flags: needinfo?(cam)
We don't have any more betas left for Desktop 35, so depending on the risk we might not be able to take this.  Please nominate with risk/reward evaluation.
Sorry this fell off my radar.  I don't have a fix right now; the r-ed patch, after fixing the problem, didn't work, so it's going to need more investigation.  The only quick fix workaround I think of is to neuter bug 931668 by setting |RestyleResult result = eRestyleResult_Continue;| unconditionally in ElementRestyler::RestyleSelf and to skip the struct swapping.  Feels risky without beta testing though and will be a performance regression (though probably not noticed on Talos).  Doing this avoids the ASAN assertion and then reaches Mats' comment 1 assertion (and safe handling).

David/Mats what do you think?
Flags: needinfo?(mats)
Flags: needinfo?(dbaron)
Flags: needinfo?(cam)
Like this.
Attachment #8543122 - Flags: review?(dbaron)
I think we should take the wallpaper in bug 1099110 as well.  It might reveal
that we have some bug related to 'text-transform' but it seems worth that risk.
I'm hoping it would make a crash like this non-exploitable, or even not crash
at all.
Flags: needinfo?(mats)
Comment 28 makes it clear this is a miss for 35, wontfixing.
Do we still need to do this if we take bug 1099110?  (I'd certainly rather not disable it if we can avoid it.)
Flags: needinfo?(dbaron)
Comment on attachment 8543122 [details] [diff] [review]
disable bug 931668 optimisation

Though I suppose comment 17 item 4 is pretty bad, so we should probably disable for now (but figure out how to fix it!).
Attachment #8543122 - Flags: review?(dbaron) → review+
Cameron, can this land soon on trunk and then on affected branches? This will need the sec-approval? form answered.
Flags: needinfo?(cam)
Comment on attachment 8543122 [details] [diff] [review]
disable bug 931668 optimisation

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

Not easily at all.

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

No.

Which older supported branches are affected by this flaw?

All based on Firefox 35 or later.

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

bug 931668

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

The patch applies as is on beta and aurora.

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

I think not that likely; it reverts to pre-bug 931668 restyling, although it does so in a manner differently from backing out all of those patches.  So there is a small risk I think.
Flags: needinfo?(cam)
Attachment #8543122 - Flags: sec-approval?
Comment on attachment 8543122 [details] [diff] [review]
disable bug 931668 optimisation

Discussed this in IRC. Giving branch approvals and sec-approval+
Attachment #8543122 - Flags: sec-approval?
Attachment #8543122 - Flags: sec-approval+
Attachment #8543122 - Flags: approval-mozilla-beta+
Attachment #8543122 - Flags: approval-mozilla-aurora+
Do I need to do anything about b2g-v2.2 and b2g-v3.0?  I don't know what repositories they correspond to.
It looks like this caused a 5-15% regression in animation / tab-animation tests, FWIW.  (At least, I got a handful of perf-regression automated emails for having pushed a no-net-code-changes push right before this bug here's push.  Most of the regression emails had this regression range:
http://hg.mozilla.org/integration/mozilla-inbound/pushloghtml?fromchange=490ab676c5a4&tochange=ed9225015e22
...including only my no-net-code-changes push & this bug's push. (so, this bug's push is the only thing potentially responsible)

The regression emails I got (so far) were:
  Mozilla-Inbound-Non-PGO - Customization Animation Tests - WINNT 5.1 (ix) - 5.39%
  Mozilla-Inbound-Non-PGO - Tab Animation Test - WINNT 5.1 (ix) - 4.66%
  Mozilla-Inbound-Non-PGO - Customization Animation Tests - Ubuntu HW 12.04 - 15.8%
  Mozilla-Inbound-Non-PGO - Customization Animation Tests - Ubuntu HW 12.04 x64 - 14.2%
  Mozilla-Inbound-Non-PGO - Tab Animation Test - Ubuntu HW 12.04 x64 - 6.59%
  Mozilla-Inbound-Non-PGO - Customization Animation Tests - WINNT 6.1 (ix) - 12.4%
  Mozilla-Inbound-Non-PGO - Customization Animation Tests - WINNT 6.2 x64 - 21.5%

I'm not (necessarily) suggesting that any of this means we should back this out, since it's fixing a security bug, and we've got to weight that against the perf hit. Just mentioning it here, since it seems worth noting.
(In reply to Cameron McCormack (:heycam) from comment #39)
> Do I need to do anything about b2g-v2.2 and b2g-v3.0?  I don't know what
> repositories they correspond to.

No to both. 3.0 is trunk and 2.2 is b2g37 (which gets merges from Aurora during this cycle).
https://hg.mozilla.org/mozilla-central/rev/ed9225015e22

I assume that fixing the underlying bug and re-enabling the optimization is going to happen in a new bug. Feel free to reopen otherwise.
Status: ASSIGNED → RESOLVED
Closed: 9 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla38
I filed bug 1125391 on reënabling the optimization.
Flags: sec-bounty? → sec-bounty+
I am continuing the investigation into the underlying issue in bug 1127198.
Group: layout-core-security
Whiteboard: [asan] → [asan][adv-main36+]
Alias: CVE-2015-0826
Whiteboard: [asan][adv-main36+] → [asan][adv-main36+][b2g-adv-main2.2-]
Group: core-security → core-security-release
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: