Bug 206099

Summary: Null deref crash in DOMWindow::scrollBy after evoking updateLayoutIgnorePendingStylesheets()
Product: WebKit Reporter: Jack <shihchieh_lee>
Component: DOMAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: cdumez, commit-queue, ggaren, pgyanchandani, rniwa, simon.fraser, zalan
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Test case
none
Patch
simon.fraser: review-
Patch
none
Patch
none
Patch
none
Patch
none
Patch none

Description Jack 2020-01-10 14:49:19 PST
<radr://problem/57213791>

Similar to https://webkit.org/b/200409, but the frame is removed by reentrancy after calling updateLayoutIgnorePendingStylesheets.
Comment 1 Jack 2020-01-10 14:52:47 PST
<rdar://problem/57213791>
Comment 2 Jack 2020-01-10 14:55:06 PST
Created attachment 387377 [details]
Test case

The test case becomes flaky if we further reduce the html file.
Comment 3 Jack 2020-01-10 16:40:26 PST
Created attachment 387396 [details]
Patch
Comment 4 Geoffrey Garen 2020-01-10 16:49:21 PST
Comment on attachment 387396 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=387396&action=review

> Source/WebCore/page/DOMWindow.cpp:1609
> +    auto frame = makeRefPtr(this->frame());
> +    if (!frame)
> +        return;
>  
> -    FrameView* view = frame()->view();
> +    auto view = makeRefPtr(frame->view());
>      if (!view)
>          return;

In this case, do we even need "frame" and "view" before layout, or should we just delete these accesses, and only use the "afterLayout" variants? (Is there a specific need to perform these null checks? If so, maybe we should just perform them as expressions without saving a local variable at all.)

> Source/WebCore/page/DOMWindow.cpp:1655
> +    // Layout may have affected the current frame:

Can you specify why? For example, is there an event that fires?
Comment 5 Chris Dumez 2020-01-10 16:53:40 PST
Comment on attachment 387396 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=387396&action=review

> Source/WebCore/page/DOMWindow.cpp:1604
> +    if (!frame)

This is a bogus check. if (!isCurrentlyDisplayedInFrame()) would have returned early above if the frame was null.
Comment 6 Simon Fraser (smfr) 2020-01-10 17:41:40 PST
Comment on attachment 387396 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=387396&action=review

> Source/WebCore/page/DOMWindow.cpp:1644
> +    auto frame = makeRefPtr(this->frame());
> +    if (!frame)
>          return;
>  
> +    auto view = makeRefPtr(frame->view());
> +    if (!view)
> +        return;

We repeat this pattern in various accessors in this file. It's so common I think we should share code for it.

I also think we should limit 'view' and 'frame' to a scope that ends before the updateLayoutIgnorePendingStylesheets call. I see no reason to keep a ref to these across the updateLayoutIgnorePendingStylesheets call.
Comment 7 Jack 2020-01-13 08:40:58 PST
We had the same discussion. In this test case, frame is null after calling
"updateLayoutIgnorePendingStylesheets". Some have concern about using freed frame pointer, so we use the same approach as in other functions.

(In reply to Geoffrey Garen from comment #4)
> Comment on attachment 387396 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=387396&action=review
> 
> > Source/WebCore/page/DOMWindow.cpp:1609
> > +    auto frame = makeRefPtr(this->frame());
> > +    if (!frame)
> > +        return;
> >  
> > -    FrameView* view = frame()->view();
> > +    auto view = makeRefPtr(frame->view());
> >      if (!view)
> >          return;
> 
> In this case, do we even need "frame" and "view" before layout, or should we
> just delete these accesses, and only use the "afterLayout" variants? (Is
> there a specific need to perform these null checks? If so, maybe we should
> just perform them as expressions without saving a local variable at all.)
> 
> > Source/WebCore/page/DOMWindow.cpp:1655
> > +    // Layout may have affected the current frame:
> 
> Can you specify why? For example, is there an event that fires?
Comment 8 Jack 2020-01-13 08:45:04 PST
Thanks! Will either keep this line or add reference count to prevent frame being deleted.

(In reply to Chris Dumez from comment #5)
> Comment on attachment 387396 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=387396&action=review
> 
> > Source/WebCore/page/DOMWindow.cpp:1604
> > +    if (!frame)
> 
> This is a bogus check. if (!isCurrentlyDisplayedInFrame()) would have
> returned early above if the frame was null.
Comment 9 Chris Dumez 2020-01-13 08:53:03 PST
(In reply to Jack from comment #8)
> Thanks! Will either keep this line or add reference count to prevent frame
> being deleted.
> 
> (In reply to Chris Dumez from comment #5)
> > Comment on attachment 387396 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=387396&action=review
> > 
> > > Source/WebCore/page/DOMWindow.cpp:1604
> > > +    if (!frame)
> > 
> > This is a bogus check. if (!isCurrentlyDisplayedInFrame()) would have
> > returned early above if the frame was null.

I think Simon already mentioned this but I don't think we want to keep the frame alive past the updateLayoutIgnorePendingStylesheets() call. Instead, you should get the frame *after* the updateLayoutIgnorePendingStylesheets() call and null check it then.
Comment 10 Jack 2020-01-13 09:19:59 PST
Thanks! Yes, there is a pattern in this file that can be abstracted out.

(In reply to Simon Fraser (smfr) from comment #6)
> Comment on attachment 387396 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=387396&action=review
> 
> > Source/WebCore/page/DOMWindow.cpp:1644
> > +    auto frame = makeRefPtr(this->frame());
> > +    if (!frame)
> >          return;
> >  
> > +    auto view = makeRefPtr(frame->view());
> > +    if (!view)
> > +        return;
> 
> We repeat this pattern in various accessors in this file. It's so common I
> think we should share code for it.
> 
> I also think we should limit 'view' and 'frame' to a scope that ends before
> the updateLayoutIgnorePendingStylesheets call. I see no reason to keep a ref
> to these across the updateLayoutIgnorePendingStylesheets call.
Comment 11 Jack 2020-01-13 09:33:35 PST
Geoffrey commented about the lifespan of frame pointer. There was concern about using freed frame pointer so we follow the pattern of other functions.

Will discuss further with Geoffrey and others to decide if we should protect old frame pointer.

(In reply to Chris Dumez from comment #9)
> (In reply to Jack from comment #8)
> > Thanks! Will either keep this line or add reference count to prevent frame
> > being deleted.
> > 
> > (In reply to Chris Dumez from comment #5)
> > > Comment on attachment 387396 [details]
> > > Patch
> > > 
> > > View in context:
> > > https://bugs.webkit.org/attachment.cgi?id=387396&action=review
> > > 
> > > > Source/WebCore/page/DOMWindow.cpp:1604
> > > > +    if (!frame)
> > > 
> > > This is a bogus check. if (!isCurrentlyDisplayedInFrame()) would have
> > > returned early above if the frame was null.
> 
> I think Simon already mentioned this but I don't think we want to keep the
> frame alive past the updateLayoutIgnorePendingStylesheets() call. Instead,
> you should get the frame *after* the updateLayoutIgnorePendingStylesheets()
> call and null check it then.
Comment 12 Pinki Gyanchandani 2020-01-27 21:30:12 PST
Created attachment 388966 [details]
Patch
Comment 13 Ryosuke Niwa 2020-01-27 21:51:56 PST
Comment on attachment 388966 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=388966&action=review

r- due to the following issues.

> Source/WebCore/ChangeLog:3
> +        Deploy Ref and RefPtr in DOMWindow::scroll* functions

The title of this bug doesn’t match your patch at all. Please fix it.

> Source/WebCore/ChangeLog:8
> +        Added Null pointer check for frame in scrollBy function before usage.

Nit: null pointer check, not Null pointer check.

> Source/WebCore/page/DOMWindow.cpp:1690
> +    FrameView* view = frame->view();

We should store FrameView in RefPtr. Use auto & makeRefPtr.

> LayoutTests/platform/mac/fast/dom/Window/window-scroll-ignore-null-frame-expected.txt:1
> +layer at (0,0) size 800x600

Please call dumpAsText in the test and add a platform agnostic result.
Comment 14 Ryosuke Niwa 2020-01-27 21:52:48 PST
Wait a minute, maybe you uploaded your patch to a wrong bug?
Comment 15 Pinki Gyanchandani 2020-01-28 14:22:06 PST
Created attachment 389062 [details]
Patch
Comment 16 Pinki Gyanchandani 2020-01-28 14:25:45 PST
Created attachment 389063 [details]
Patch
Comment 17 Ryosuke Niwa 2020-01-28 14:29:05 PST
Comment on attachment 389063 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=389063&action=review

> Source/WebCore/ChangeLog:3
> +        Deploy Ref and RefPtr in DOMWindow::scroll* functions

This line should be updated to reflect the new bug title.

> Source/WebCore/ChangeLog:9
> +        Added null pointer check for frame in scrollBy function before usage.
> +        Test: fast/dom/Window/window-scroll-ignore-null-frame.html

Nit: There should be a blank line between the description & Test: ~ line.

> LayoutTests/ChangeLog:3
> +        Deploy Ref and RefPtr in DOMWindow::scroll* functions

Ditto about updating this line.

> LayoutTests/ChangeLog:8
> +        Addedd null pointer check for frame in scrollBy, before using.

This description should be describing what we're doing in LayoutTests directory.
In this case, you're only adding a test case so it's sufficient to say: Added a regression test.
Comment 18 Pinki Gyanchandani 2020-01-28 14:37:42 PST
Created attachment 389066 [details]
Patch
Comment 19 Ryosuke Niwa 2020-01-28 14:42:50 PST
Comment on attachment 389066 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=389066&action=review

> LayoutTests/ChangeLog:8
> +        Added null pointer check for frame in scrollBy function before usage.

Again, this line should describe what change we're making in this directory. e.g. "Added a regression test"

> LayoutTests/ChangeLog:10
> +        * fast/dom/Window/window-scroll-ignore-null-frame.html: Added.

This is missing a line for -expected.txt file.
Comment 20 Pinki Gyanchandani 2020-01-28 14:53:45 PST
Created attachment 389069 [details]
Patch
Comment 21 Ryosuke Niwa 2020-01-28 15:16:44 PST
Comment on attachment 389069 [details]
Patch

Cool. r=me.
Comment 22 Ryosuke Niwa 2020-01-28 15:25:32 PST
Let's wait for EWS first. Please also set cq? flag in your patch to ask for your patch to be landed by the commit queue in the future.
Comment 23 Ryosuke Niwa 2020-01-28 18:19:22 PST
Comment on attachment 389069 [details]
Patch

Looks like iOS EWS is hosed :(
Comment 24 WebKit Commit Bot 2020-01-28 19:02:04 PST
Comment on attachment 389069 [details]
Patch

Clearing flags on attachment: 389069

Committed r255334: <https://trac.webkit.org/changeset/255334>
Comment 25 WebKit Commit Bot 2020-01-28 19:02:06 PST
All reviewed patches have been landed.  Closing bug.