From b7cb953aa0b4c99543923a1f14bb2c56877441c7 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Fri, 19 Apr 2019 19:01:38 +0000 Subject: [PATCH] Crash in FrameLoader::stopAllLoaders via [WebView dealloc] inside ~ObjCEventListener https://bugs.webkit.org/show_bug.cgi?id=197079 Reviewed by Darin Adler. Source/WebKitLegacy/mac: The crash was caused by the fact deleting a node could end up deleting Objective-C event listeners some of which may be the last object holding onto WebView itself in the middle of running GC. It's not generally safe to delete Objective-C objects defiend by client applications since dealloc could execute arbitrary code, and for instance, try to execute JavaScript or load new page. Fixed the bug by delaying the dealloc'ing of Objective event listeners via autorelease pool. * DOM/ObjCEventListener.mm: (WebCore::ObjCEventListener::~ObjCEventListener): Tools: Added a regression test. It hits a slightly different backtrace but of the same class of issues. * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj: * TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm: Added. Canonical link: https://commits.webkit.org/211336@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244459 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebKitLegacy/mac/ChangeLog | 18 ++++ .../WebKitLegacy/mac/DOM/ObjCEventListener.mm | 3 + Tools/ChangeLog | 12 +++ .../TestWebKitAPI.xcodeproj/project.pbxproj | 4 + .../mac/DeallocWebViewInEventListener.mm | 93 +++++++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 Tools/TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm diff --git a/Source/WebKitLegacy/mac/ChangeLog b/Source/WebKitLegacy/mac/ChangeLog index ec5f76033ddb..5c689120772d 100644 --- a/Source/WebKitLegacy/mac/ChangeLog +++ b/Source/WebKitLegacy/mac/ChangeLog @@ -1,3 +1,21 @@ +2019-04-18 Ryosuke Niwa + + Crash in FrameLoader::stopAllLoaders via [WebView dealloc] inside ~ObjCEventListener + https://bugs.webkit.org/show_bug.cgi?id=197079 + + Reviewed by Darin Adler. + + The crash was caused by the fact deleting a node could end up deleting Objective-C event listeners + some of which may be the last object holding onto WebView itself in the middle of running GC. + + It's not generally safe to delete Objective-C objects defiend by client applications since + dealloc could execute arbitrary code, and for instance, try to execute JavaScript or load new page. + + Fixed the bug by delaying the dealloc'ing of Objective event listeners via autorelease pool. + + * DOM/ObjCEventListener.mm: + (WebCore::ObjCEventListener::~ObjCEventListener): + 2019-04-18 Jer Noble Refactoring: Pull all fullscreen code out of Document and into its own helper class diff --git a/Source/WebKitLegacy/mac/DOM/ObjCEventListener.mm b/Source/WebKitLegacy/mac/DOM/ObjCEventListener.mm index 8f7a46781ff1..3d403f2a77bd 100644 --- a/Source/WebKitLegacy/mac/DOM/ObjCEventListener.mm +++ b/Source/WebKitLegacy/mac/DOM/ObjCEventListener.mm @@ -70,6 +70,9 @@ ObjCEventListener::~ObjCEventListener() { listenerMap->remove(m_listener.get()); + // Avoid executing arbitrary code during GC; e.g. inside Node::~Node. Use CF* to be ARC safe. + CFRetain((__bridge CFTypeRef)m_listener.get()); + CFAutorelease((__bridge CFTypeRef)m_listener.get()); } void ObjCEventListener::handleEvent(ScriptExecutionContext&, Event& event) diff --git a/Tools/ChangeLog b/Tools/ChangeLog index 3132daade097..769f7e189c18 100644 --- a/Tools/ChangeLog +++ b/Tools/ChangeLog @@ -1,3 +1,15 @@ +2019-04-18 Ryosuke Niwa + + Crash in FrameLoader::stopAllLoaders via [WebView dealloc] inside ~ObjCEventListener + https://bugs.webkit.org/show_bug.cgi?id=197079 + + Reviewed by Darin Adler. + + Added a regression test. It hits a slightly different backtrace but of the same class of issues. + + * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj: + * TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm: Added. + 2019-04-18 Commit Queue Unreviewed, rolling out r244434. diff --git a/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj b/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj index 94640aa7267f..2dd651d7d24f 100644 --- a/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj +++ b/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj @@ -672,6 +672,7 @@ 9B62630C1F8C25C8007EE29B /* copy-url.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B62630B1F8C2510007EE29B /* copy-url.html */; }; 9B7A37C41F8AEBA5004AA228 /* CopyURL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */; }; 9B7D740F1F8378770006C432 /* paste-rtfd.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B7D740E1F8377E60006C432 /* paste-rtfd.html */; }; + 9BAD7F3E22690F2000F8DA66 /* DeallocWebViewInEventListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */; }; 9BCB7C2820130600003E7C0C /* PasteHTML.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BCB7C2620130600003E7C0C /* PasteHTML.mm */; }; 9BCD411A206DBCA3001D71BE /* mso-list-on-h4.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BCD4119206D5ED7001D71BE /* mso-list-on-h4.html */; }; 9BD4239A1E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */; }; @@ -1935,6 +1936,7 @@ 9B79164F1BD89D0D00D50B8F /* FirstResponderScrollingPosition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FirstResponderScrollingPosition.mm; sourceTree = ""; }; 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyURL.mm; sourceTree = ""; }; 9B7D740E1F8377E60006C432 /* paste-rtfd.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "paste-rtfd.html"; sourceTree = ""; }; + 9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DeallocWebViewInEventListener.mm; sourceTree = ""; }; 9BCB7C2620130600003E7C0C /* PasteHTML.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteHTML.mm; sourceTree = ""; }; 9BCD4119206D5ED7001D71BE /* mso-list-on-h4.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "mso-list-on-h4.html"; sourceTree = ""; }; 9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AttributedSubstringForProposedRangeWithImage.mm; sourceTree = ""; }; @@ -2869,6 +2871,7 @@ children = ( 9BD5111B1FE8E11600D2B630 /* AccessingPastedImage.mm */, 6B306105218A372900F5A802 /* ClosingWebView.mm */, + 9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */, 5CF540E82257E64B00E6BC0E /* DownloadThread.mm */, 5C6E27A6224EEBEA00128736 /* URLCanonicalization.mm */, ); @@ -4069,6 +4072,7 @@ 46A911592108E6780078D40D /* CustomUserAgent.mm in Sources */, 751B05D61F8EAC410028A09E /* DatabaseTrackerTest.mm in Sources */, 2DC4CF771D2D9DD800ECCC94 /* DataDetection.mm in Sources */, + 9BAD7F3E22690F2000F8DA66 /* DeallocWebViewInEventListener.mm in Sources */, 518EE51D20A78D3600E024F3 /* DecidePolicyForNavigationAction.mm in Sources */, 2D1646E21D1862CD00015A1A /* DeferredViewInWindowStateChange.mm in Sources */, 46918EFC2237283C00468DFE /* DeviceOrientation.mm in Sources */, diff --git a/Tools/TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm b/Tools/TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm new file mode 100644 index 000000000000..06e077486f2c --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" +#import "PlatformUtilities.h" +#import "WTFStringUtilities.h" + +#import +#import +#import + +extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef); + +#if JSC_OBJC_API_ENABLED + +static bool didFinishLoad = false; +static bool didClose = false; +static RetainPtr webView; + +@interface DeallocWebViewInEventListenerLoadDelegate : NSObject +@end + +@implementation DeallocWebViewInEventListenerLoadDelegate +- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame +{ + didFinishLoad = true; +} +@end + +@interface DeallocWebViewInEventListener : NSObject +@end + +@implementation DeallocWebViewInEventListener +- (void)handleEvent:(DOMEvent *)event +{ + +} + +- (void)dealloc +{ + webView = nullptr; + [super dealloc]; + didClose = true; +} +@end + +namespace TestWebKitAPI { + +TEST(WebKitLegacy, DeallocWebViewInEventListener) +{ + { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400) frameName:nil groupName:nil]); + auto loadDelegate = adoptNS([[DeallocWebViewInEventListenerLoadDelegate alloc] init]); + webView.get().frameLoadDelegate = loadDelegate.get(); + + [[webView mainFrame] loadHTMLString:@"" baseURL:nil]; + Util::run(&didFinishLoad); + + auto listener = adoptNS([[DeallocWebViewInEventListener alloc] init]); + [[[webView mainFrameDocument] body] addEventListener:@"keypress" listener:listener.get() useCapture:NO]; + listener = nullptr; + [webView close]; + [pool drain]; + } + Util::run(&didClose); +} + +} // namespace TestWebKitAPI + +#endif // ENABLE(JSC_OBJC_API)