Bug 210873 - [iOS] Crash on RunningBoard process assertion invalidation
Summary: [iOS] Crash on RunningBoard process assertion invalidation
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit2 (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Chris Dumez
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2020-04-22 14:04 PDT by Chris Dumez
Modified: 2020-04-23 13:29 PDT (History)
6 users (show)

See Also:


Attachments
Patch (2.40 KB, patch)
2020-04-22 14:39 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (2.46 KB, patch)
2020-04-22 14:48 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (2.53 KB, patch)
2020-04-22 15:31 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dumez 2020-04-22 14:04:20 PDT
Crash on RunningBoard process assertion invalidation:
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
[...]
9   WebKit                        	0x000000018cff590c invocation function for block in WebKit::ProcessAssertion::ProcessAssertion(int, WTF::ASCIILiteral, WebKit::ProcessAssertionType) + 168 (/Library/Caches/com.apple.xbs/Sources/WebKit2/WebKit2-7610.1.10/UIProcess/ios/ProcessAssertionIOS.mm:375)
10  ...g_rt.asan_ios_dynamic.dylib	0x0000000104068e2c __wrap_dispatch_async_block_invoke + 188 
11  libdispatch.dylib             	0x0000000184717878 _dispatch_call_block_and_release + 24 
12  libdispatch.dylib             	0x0000000184718840 _dispatch_client_callout + 16 
13  libdispatch.dylib             	0x00000001846fade8 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 856 
14  CoreFoundation                	0x00000001849d0e48 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
15  CoreFoundation                	0x00000001849cb790 __CFRunLoopRun + 1884
16  CoreFoundation                	0x00000001849cac50 CFRunLoopRunSpecific + 520
17  XCTest                        	0x0000000106089668 -[XCTWaiter waitForExpectations:timeout:enforceOrder:] + 808
Comment 1 Chris Dumez 2020-04-22 14:04:35 PDT
<rdar://problem/62194917>
Comment 2 Chris Dumez 2020-04-22 14:39:27 PDT
Created attachment 397262 [details]
Patch
Comment 3 Chris Dumez 2020-04-22 14:48:19 PDT
Created attachment 397264 [details]
Patch
Comment 4 Darin Adler 2020-04-22 14:56:24 PDT
Comment on attachment 397264 [details]
Patch

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

> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:293
> +        if (weakSelf && weakSelf.invalidationCallback)
> +            weakSelf.invalidationCallback();

The pattern I see is that people copy such things into a strong pointer, so there’s no race with another process. I think that might be needed here.

> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:424
> +        m_delegate.get().invalidationCallback = nil;

I am mixed up a bit: What guarantees that the main queue thread didn’t already fetch the callback and then will call it some arbitrary length of time later when it was up?
Comment 5 Chris Dumez 2020-04-22 15:02:15 PDT
Comment on attachment 397264 [details]
Patch

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

>> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:293
>> +            weakSelf.invalidationCallback();
> 
> The pattern I see is that people copy such things into a strong pointer, so there’s no race with another process. I think that might be needed here.

I am not sure I understand your comment. Do you want me to use a strongSelf? If so, it would work but I don't think it is necessary to keep WKRBSAssertionDelegate alive longer than the ProcessAssertion since we're only calling the ProcessAssertion's invalidationHandler.

>> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:424
>> +        m_delegate.get().invalidationCallback = nil;
> 
> I am mixed up a bit: What guarantees that the main queue thread didn’t already fetch the callback and then will call it some arbitrary length of time later when it was up?

The ProcessAssertion destructor always runs on the main thread. Both WKRBSAssertionDelegate and ProcessAssertion only set / access invalidationCallback on the main thread. Therefore, there is no race.
Comment 6 Chris Dumez 2020-04-22 15:31:25 PDT
Created attachment 397276 [details]
Patch
Comment 7 Chris Dumez 2020-04-22 15:35:20 PDT
Comment on attachment 397276 [details]
Patch

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

> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:286
>  - (void)assertion:(RBSAssertion *)assertion didInvalidateWithError:(NSError *)error

This may get called on a background thread, thus the dispatch_async below before accessing / calling invalidationCallback. invalidationCallback gets set on the main thread by the ProcessAssertion code and the ProcessAssertion expects the invalidation handler to get called on the main thread.

> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:425
> +        m_delegate.get().invalidationCallback = nil;

ProcessAssertion is a main thread object. It always gets constructed, destroyed on the main thread. 

m_delegate.get().invalidationCallback gets initialized in the ProcessAssertion constructor on the main thread.
m_delegate.get().invalidationCallback gets null out here in the destructor on the main thread.
m_delegate.get().invalidationCallback gets accessed in WKRBSAssertionDelegate.didInvalidateWithError on the main thread (enforced by our dispatch_async)

therefore, the invalidationCallback is always used on the main thread and there should be no race.
Comment 8 Chris Dumez 2020-04-22 15:39:26 PDT
Comment on attachment 397276 [details]
Patch

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

>> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:425
>> +        m_delegate.get().invalidationCallback = nil;
> 
> ProcessAssertion is a main thread object. It always gets constructed, destroyed on the main thread. 
> 
> m_delegate.get().invalidationCallback gets initialized in the ProcessAssertion constructor on the main thread.
> m_delegate.get().invalidationCallback gets null out here in the destructor on the main thread.
> m_delegate.get().invalidationCallback gets accessed in WKRBSAssertionDelegate.didInvalidateWithError on the main thread (enforced by our dispatch_async)
> 
> therefore, the invalidationCallback is always used on the main thread and there should be no race.

Also note that ProcessAssertion retains the WKRBSAssertionDelegate via m_delegate.
Comment 9 Chris Dumez 2020-04-22 15:47:05 PDT
Comment on attachment 397276 [details]
Patch

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

> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:292
> +        WKRBSAssertionDelegate *strongSelf = weakSelf;

Hopefully this is what Darin meant by using strongSelf. Geoff recommended doing this to avoid races.
Comment 10 Darin Adler 2020-04-22 16:58:48 PDT
Comment on attachment 397276 [details]
Patch

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

>> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:292
>> +        WKRBSAssertionDelegate *strongSelf = weakSelf;
> 
> Hopefully this is what Darin meant by using strongSelf. Geoff recommended doing this to avoid races.

Yes, this is what I meant. Not sure it’s needed, but this is the pattern I have seen before.
Comment 11 EWS 2020-04-22 17:04:31 PDT
Committed r260544: <https://trac.webkit.org/changeset/260544>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 397276 [details].
Comment 12 Adam Roben (:aroben) 2020-04-22 18:05:04 PDT
(In reply to Darin Adler from comment #10)
> Comment on attachment 397276 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=397276&action=review
> 
> >> Source/WebKit/UIProcess/ios/ProcessAssertionIOS.mm:292
> >> +        WKRBSAssertionDelegate *strongSelf = weakSelf;
> > 
> > Hopefully this is what Darin meant by using strongSelf. Geoff recommended doing this to avoid races.
> 
> Yes, this is what I meant. Not sure it’s needed, but this is the pattern I
> have seen before.

It also has a minor performance benefit since each use of a weak variable involves some locking and hash table lookups in the Objective-C runtime.
Comment 13 Chris Dumez 2020-04-23 13:29:00 PDT
Committed r260595: <https://trac.webkit.org/changeset/260595>