Bug 218494 - Release assertion failure in Optional<WebCore::SimpleRange>::operator* via CompositeEditCommand::moveParagraphs
Summary: Release assertion failure in Optional<WebCore::SimpleRange>::operator* via Co...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: HTML Editing (show other bugs)
Version: WebKit Local Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2020-11-03 02:46 PST by Ian Gilbert
Modified: 2020-11-18 00:28 PST (History)
14 users (show)

See Also:


Attachments
Crashing Input (535.22 KB, text/html)
2020-11-03 02:47 PST, Ian Gilbert
no flags Details
Reduced test case (9.03 KB, text/html)
2020-11-05 08:05 PST, Carlos Garcia Campos
no flags Details
Patch (5.00 KB, patch)
2020-11-12 04:01 PST, Carlos Garcia Campos
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Reduced test case (350 bytes, text/html)
2020-11-13 01:16 PST, Carlos Garcia Campos
no flags Details
Patch (5.36 KB, patch)
2020-11-13 06:10 PST, Carlos Garcia Campos
rniwa: review-
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (5.33 KB, patch)
2020-11-16 02:41 PST, Carlos Garcia Campos
rniwa: review+
Details | Formatted Diff | Diff
Patch for landing (5.24 KB, patch)
2020-11-17 07:10 PST, Carlos Garcia Campos
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ian Gilbert 2020-11-03 02:46:46 PST
Crash found by fuzzing. Reproduces on WebKit revision 265375.

Stack Trace
===========

0   com.apple.WebCore             	0x00000004de1ab37d WTF::Optional<WebCore::SimpleRange>::operator*() && + 45
1   com.apple.WebCore             	0x00000004e0c6d4c3 WebCore::CompositeEditCommand::moveParagraphs(WebCore::VisiblePosition const&, WebCore::VisiblePosition const&, WebCore::VisiblePosition const&, bool, bool) + 2035
2   com.apple.WebCore             	0x00000004e0d1c837 WebCore::InsertListCommand::unlistifyParagraph(WebCore::VisiblePosition const&, WebCore::HTMLElement*, WebCore::Node*) + 1927
3   com.apple.WebCore             	0x00000004e0d1bc61 WebCore::InsertListCommand::doApplyForSingleParagraph(bool, WebCore::HTMLQualifiedName const&, WebCore::SimpleRange&) + 2961
4   com.apple.WebCore             	0x00000004e0d1aa86 WebCore::InsertListCommand::doApply() + 1750
5   com.apple.WebCore             	0x00000004e0c4a6e7 WebCore::CompositeEditCommand::apply() + 535
6   com.apple.WebCore             	0x00000004e0d06949 WebCore::executeInsertOrderedList(WebCore::Frame&, WebCore::Event*, WebCore::EditorCommandSource, WTF::String const&) + 201
7   com.apple.WebCore             	0x00000004e0958a84 WebCore::Document::execCommand(WTF::String const&, bool, WTF::String const&) + 244
8   com.apple.WebCore             	0x00000004ddfe71ac WebCore::jsDocumentPrototypeFunctionExecCommandBody(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSDocument*) + 1164
9   com.apple.WebCore             	0x00000004ddf18a5c long long WebCore::IDLOperation<WebCore::JSDocument>::call<&(WebCore::jsDocumentPrototypeFunctionExecCommandBody(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSDocument*)), (WebCore::CastedThisErrorBehavior)0>(JSC::JSGlobalObject&, JSC::CallFrame&, char const*) + 252
Comment 1 Radar WebKit Bug Importer 2020-11-03 02:46:57 PST
<rdar://problem/70987390>
Comment 2 Ian Gilbert 2020-11-03 02:47:44 PST
Created attachment 413028 [details]
Crashing Input
Comment 3 Carlos Garcia Campos 2020-11-05 08:05:44 PST
Created attachment 413298 [details]
Reduced test case
Comment 4 Carlos Garcia Campos 2020-11-05 08:13:15 PST
The crash happens in CompositeEditCommand::moveParagraphs() because it's receiving a null endOfParagraphToMove. That causes that *makeSimpleRange(start, end) ends up trying to get a value that is actually nullopt. The problem comes from InsertListCommand::unlistifyParagraph(), that tries to get the start and end points of the list item children.

start = firstPositionInNode(listChildNode);
end = lastPositionInNode(listChildNode);

end is null here. There's no </li> so all nodes after the <li> are considered children of the list item:

						LI	0x7f2102132380 (renderer 0x7f2100000240)  CLASS=class1 STYLE=border-image: url(#htmlvar00007) 47% 1 -1 round; -webkit-ltr-ordering: visual; filter: brightness(1) brightness(0.361593078481); background-origin: content-box, padding-box; mso-width-alt: 1
							#text	0x7f2102138b58 "\n"
							VIDEO	0x7f2102132400 (renderer 0x7f2100000360) 
								#text	0x7f2102138bb0 "\n"
								BASE	0x7f2102132af0 (renderer (nil))  CLASS=class6
								#text	0x7f2102138c08 "\n"
								SOURCE	0x7f2102132b70 (renderer (nil))  STYLE=stroke-linecap: round; scroll-snap-destination: 44% 1px; box-flex-group: 0; -webkit-ruby-position: after; max-width: none
								#text	0x7f2102138c60 "EFgrVTq+XimoYYd+9$\n"
*							#text	0x7f2102138cb8 "\n"
							MARQUEE	0x7f2102132c50 (renderer 0x7f2102128f00) 
								#text	0x7f2102138d10 "\n"
								TIME	0x7f2102132ce0 (renderer 0x7f2100008f18) 
									#text	0x7f2102138d68 "\n"
									BODY	0x7f2100000650 (renderer 0x7f2102129000) 

The video one is the reason null is returned, but I don't know why yet. If I remove the video element then it doesn't crash, but the end point is still wrong, I assume it should be the text child with the offset = len.
Comment 5 Carlos Garcia Campos 2020-11-12 04:01:01 PST
Created attachment 413925 [details]
Patch

In the end I decided to stop the insertion when end position is null to match what firefox and chrome do.
Comment 6 Carlos Garcia Campos 2020-11-12 06:37:35 PST
It seems the test failure can just be rebaselined, for some reason there's one empty line less in the outpout now:

https://ews-build.s3-us-west-2.amazonaws.com/macOS-Mojave-Release-WK2-Tests-EWS/r413925-22814/editing/inserting/insert-list-in-iframe-in-list-pretty-diff.html
Comment 7 Ryosuke Niwa 2020-11-12 21:43:19 PST
Can we attach the reduced test case since there doesn't appear to be a security bug here?
Comment 8 Ryosuke Niwa 2020-11-12 21:43:48 PST
Comment on attachment 413925 [details]
Patch

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

> Source/WebCore/editing/InsertListCommand.cpp:301
> +    if (end.isNull())
> +        return false;

Why is end null here?
Comment 9 Carlos Garcia Campos 2020-11-12 23:49:58 PST
(In reply to Ryosuke Niwa from comment #7)
> Can we attach the reduced test case since there doesn't appear to be a
> security bug here?

I already attached the reduced test case, I'll clean it up a bit and include it as part of the patch then.
Comment 10 Carlos Garcia Campos 2020-11-12 23:53:38 PST
(In reply to Ryosuke Niwa from comment #8)
> Comment on attachment 413925 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=413925&action=review
> 
> > Source/WebCore/editing/InsertListCommand.cpp:301
> > +    if (end.isNull())
> > +        return false;
> 
> Why is end null here?

See comment #4. There's an orphan <li> without the </li> causing the elements after that one to be considered children. The next element is a video one, that causes lastPositionInNode(listChildNode) to return null.
Comment 11 Ryosuke Niwa 2020-11-13 00:35:07 PST
(In reply to Carlos Garcia Campos from comment #10)
> (In reply to Ryosuke Niwa from comment #8)
> > Comment on attachment 413925 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=413925&action=review
> > 
> > > Source/WebCore/editing/InsertListCommand.cpp:301
> > > +    if (end.isNull())
> > > +        return false;
> > 
> > Why is end null here?
> 
> See comment #4. There's an orphan <li> without the </li> causing the
> elements after that one to be considered children. The next element is a
> video one, that causes lastPositionInNode(listChildNode) to return null.

But why? We should be able to deal with the fact video is the last child of li. There is nothing wrong with that.
Comment 12 Carlos Garcia Campos 2020-11-13 01:15:37 PST
(In reply to Ryosuke Niwa from comment #11)
> (In reply to Carlos Garcia Campos from comment #10)
> > (In reply to Ryosuke Niwa from comment #8)
> > > Comment on attachment 413925 [details]
> > > Patch
> > > 
> > > View in context:
> > > https://bugs.webkit.org/attachment.cgi?id=413925&action=review
> > > 
> > > > Source/WebCore/editing/InsertListCommand.cpp:301
> > > > +    if (end.isNull())
> > > > +        return false;
> > > 
> > > Why is end null here?
> > 
> > See comment #4. There's an orphan <li> without the </li> causing the
> > elements after that one to be considered children. The next element is a
> > video one, that causes lastPositionInNode(listChildNode) to return null.
> 
> But why? We should be able to deal with the fact video is the last child of
> li. There is nothing wrong with that.

It's not the last child, there's also a marquee, leaving the video but removing the marquee doesn't crash either. Let me investigate more.
Comment 13 Carlos Garcia Campos 2020-11-13 01:16:37 PST
Created attachment 414011 [details]
Reduced test case

I've reduced more the test case and cleaned it up
Comment 14 Ryosuke Niwa 2020-11-13 01:33:53 PST
Maybe VisiblePosition is choking up? It would be good to get to the bottom of why it's getting null. That kind of bug can lead to crashes elsewhere in the editing code. We don't want to be keep chasing new crash elsewhere.
Comment 15 Carlos Garcia Campos 2020-11-13 05:49:39 PST
This is the new tree with the reduced test case:

BODY	0x7f3f118f52d0 (renderer 0x7f3f118f5460) 
	#text	0x7f3f118f6430 "\n"
	FORM	0x7f3f118f6490 (renderer 0x7f3f118f7070)  STYLE=-webkit-user-modify: read-write;
		#text	0x7f3f118f6590 "\n"
		DIV	0x7f3f118f65f0 (renderer 0x7f3f118f7180) 
		#text	0x7f3f118f6670 "\n"
		UL	0x7f3f1192c200 (renderer 0x7f3f11934200) 
			LI	0x7f3f118f66d0 (renderer 0x7f3f118f7290) 
				#text	0x7f3f118f6750 "\n"
				VIDEO	0x7f3f118f67b0 (renderer 0x7f3f118f73b0) 
*				#text	0x7f3f118f6ea0 "\n"
				MARQUEE	0x7f3f118f6f00 (renderer 0x7f3f11934300) 
					#text	0x7f3f118f6f90 "\n"
					TIME	0x7f3f118f6ff0 (renderer 0x7f3f118f7670) 
						#text	0x7f3f1193c058 "\n"
						BODY	0x7f3f118f78b0 (renderer 0x7f3f11934400) 

The last BODY, which is what the script adds, is what confuses everything. The tree is iterated looking for the end position and the last BODY returns true for isCandidate() but null is returned because !nextIsInSameEditableElement && !prevIsInSameEditableElement (the editing root for BODY is the body itself). And that seems to be the actual bug, because that body is not editable. Node::rootEditableElement() breaks ig the node is an HTMLBodyElment, but it shoudl actually check it's the document body element.
Comment 16 Carlos Garcia Campos 2020-11-13 06:10:09 PST
Created attachment 414037 [details]
Patch
Comment 17 Ryosuke Niwa 2020-11-14 14:02:12 PST
(In reply to Carlos Garcia Campos from comment #15)
>
> The last BODY, which is what the script adds, is what confuses everything.
> The tree is iterated looking for the end position and the last BODY returns
> true for isCandidate() but null is returned because
> !nextIsInSameEditableElement && !prevIsInSameEditableElement (the editing
> root for BODY is the body itself). And that seems to be the actual bug,
> because that body is not editable. Node::rootEditableElement() breaks ig the
> node is an HTMLBodyElment, but it shoudl actually check it's the document
> body element.

Ah, I see. That makes sense. Thanks for the good analysis.
Comment 18 Ryosuke Niwa 2020-11-14 14:05:05 PST
Comment on attachment 414037 [details]
Patch

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

> LayoutTests/ChangeLog:7
> +

Please say that we're adding a regression test.
Comment 19 Ryosuke Niwa 2020-11-14 14:05:50 PST
Looks like there is some assertion failure on Mac WK1:

CRASHING TEST: editing/inserting/insert-list-in-iframe-in-list.html

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.JavaScriptCore      	0x000000010d643af0 WTFCrash + 16 (Assertions.cpp:295)
1   com.apple.WebCore             	0x0000000126d4115b WTFCrashWithInfo(int, char const*, char const*, int) + 27
2   com.apple.WebCore             	0x0000000129f06f75 WebCore::CompositeEditCommand::moveParagraphs(WebCore::VisiblePosition const&, WebCore::VisiblePosition const&, WebCore::VisiblePosition const&, bool, bool) + 149
3   com.apple.WebCore             	0x0000000129f8ea56 WebCore::InsertListCommand::unlistifyParagraph(WebCore::VisiblePosition const&, WebCore::HTMLElement*, WebCore::Node*) + 2022
4   com.apple.WebCore             	0x0000000129f8e0ae WebCore::InsertListCommand::doApplyForSingleParagraph(bool, WebCore::HTMLQualifiedName const&, WebCore::SimpleRange&) + 2014
5   com.apple.WebCore             	0x0000000129f8d881 WebCore::InsertListCommand::doApply() + 2385
6   com.apple.WebCore             	0x0000000129eef665 WebCore::CompositeEditCommand::apply() + 421
7   com.apple.WebCore             	0x0000000129f7a4ad WebCore::executeInsertOrderedList(WebCore::Frame&, WebCore::Event*, WebCore::EditorCommandSource, WTF::String const&) + 157
8   com.apple.WebCore             	0x0000000129f515b7 WebCore::Editor::Command::execute(WTF::String const&, WebCore::Event*) const + 231
9   com.apple.WebCore             	0x0000000129c0fc46 WebCore::Document::execCommand(WTF::String const&, bool, WTF::String const&) + 86
10  com.apple.WebCore             	0x0000000127772e1e WebCore::jsDocumentPrototypeFunction_execCommandBody(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSDocument*) + 990
11  com.apple.WebCore             	0x00000001277729f9 long long WebCore::IDLOperation<WebCore::JSDocument>::call<&(WebCore::jsDocumentPrototypeFunction_execCommandBody(JSC::JSGlobalObject*, JSC::CallFrame*, WebCore::JSDocument*)), (WebCore::CastedThisErrorBehavior)0>(JSC::JSGlobalObject&, JSC::CallFrame&, char const*) + 793
12  com.apple.WebCore             	0x00000001276d47c4 WebCore::jsDocumentPrototypeFunction_execCommand(JSC::JSGlobalObject*, JSC::CallFrame*) + 36
13  ???                           	0x000029dbf7401178 0 + 46024722747768
Comment 20 Ryosuke Niwa 2020-11-14 14:06:24 PST
Comment on attachment 414037 [details]
Patch

r- because of the aforementioned assertion failure. Please fix that.
Comment 21 Carlos Garcia Campos 2020-11-16 02:41:38 PST
Created attachment 414211 [details]
Patch

I've fixed the assert, end can be null, but only when start is null too.
Comment 22 Ryosuke Niwa 2020-11-16 22:41:17 PST
Comment on attachment 414211 [details]
Patch

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

> Source/WebCore/editing/CompositeEditCommand.cpp:1403
> +    ASSERT(startOfParagraphToMove == endOfParagraphToMove || !endOfParagraphToMove.isNull());

This is harder to parse than (startOfParagraphToMove.isNull() && endOfParagraphToMove.isNull()) || endOfParagraphToMove.isNull()
Comment 23 Carlos Garcia Campos 2020-11-17 07:10:13 PST
Created attachment 414336 [details]
Patch for landing
Comment 24 Ryosuke Niwa 2020-11-17 14:30:08 PST
Are we waiting for something to land patch this patch??
Comment 25 EWS 2020-11-18 00:28:25 PST
Committed r269946: <https://trac.webkit.org/changeset/269946>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 414336 [details].