Crash in FrameLoader::stopAllLoaders via [WebView dealloc] inside ~ObjCEventListener
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Apr 2019 19:01:38 +0000 (19:01 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Apr 2019 19:01:38 +0000 (19:01 +0000)
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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244459 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/DOM/ObjCEventListener.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitLegacy/mac/DeallocWebViewInEventListener.mm [new file with mode: 0644]

index ec5f760..5c68912 100644 (file)
@@ -1,3 +1,21 @@
+2019-04-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        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  <jer.noble@apple.com>
 
         Refactoring: Pull all fullscreen code out of Document and into its own helper class
index 8f7a467..3d403f2 100644 (file)
@@ -70,6 +70,9 @@ ObjCEventListener::ObjCEventListener(ObjCListener listener)
 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)
index 3132daa..769f7e1 100644 (file)
@@ -1,3 +1,15 @@
+2019-04-18  Ryosuke Niwa  <rniwa@webkit.org>
+
+        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  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r244434.
index 94640aa..2dd651d 100644 (file)
                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 */; };
                9B79164F1BD89D0D00D50B8F /* FirstResponderScrollingPosition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FirstResponderScrollingPosition.mm; sourceTree = "<group>"; };
                9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyURL.mm; sourceTree = "<group>"; };
                9B7D740E1F8377E60006C432 /* paste-rtfd.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "paste-rtfd.html"; sourceTree = "<group>"; };
+               9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DeallocWebViewInEventListener.mm; sourceTree = "<group>"; };
                9BCB7C2620130600003E7C0C /* PasteHTML.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteHTML.mm; sourceTree = "<group>"; };
                9BCD4119206D5ED7001D71BE /* mso-list-on-h4.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "mso-list-on-h4.html"; sourceTree = "<group>"; };
                9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AttributedSubstringForProposedRangeWithImage.mm; sourceTree = "<group>"; };
                        children = (
                                9BD5111B1FE8E11600D2B630 /* AccessingPastedImage.mm */,
                                6B306105218A372900F5A802 /* ClosingWebView.mm */,
+                               9BAD7F3D22690F1400F8DA66 /* DeallocWebViewInEventListener.mm */,
                                5CF540E82257E64B00E6BC0E /* DownloadThread.mm */,
                                5C6E27A6224EEBEA00128736 /* URLCanonicalization.mm */,
                        );
                                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 (file)
index 0000000..06e0774
--- /dev/null
@@ -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 <Carbon/Carbon.h>
+#import <WebKit/WebViewPrivate.h>
+#import <wtf/RetainPtr.h>
+
+extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+
+#if JSC_OBJC_API_ENABLED
+
+static bool didFinishLoad = false;
+static bool didClose = false;
+static RetainPtr<WebView> webView;
+
+@interface DeallocWebViewInEventListenerLoadDelegate : NSObject <WebFrameLoadDelegate>
+@end
+
+@implementation DeallocWebViewInEventListenerLoadDelegate
+- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
+{
+    didFinishLoad = true;
+}
+@end
+
+@interface DeallocWebViewInEventListener : NSObject <DOMEventListener>
+@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:@"<html><body></body></html>" 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)