Title: [208293] trunk
Revision
208293
Author
[email protected]
Date
2016-11-02 11:24:18 -0700 (Wed, 02 Nov 2016)

Log Message

NetworkSession: Network process crash when converting main resource to download
https://bugs.webkit.org/show_bug.cgi?id=164220

Patch by Carlos Garcia Campos <[email protected]> on 2016-11-02
Reviewed by Alex Christensen.

Source/WebKit2:

Right after the main resource load is converted to a download, the web process deletes the ResourceLoader which
sends the RemoveLoadIdentifier to the network process and the load is aborted. Sometimes it happens that
NetworkResourceLoader::abort() is called while the NetworkLoad is still deciding the destination of the
download. In such case, NetworkResourceLoader::didConvertToDownload() has already been called, but not
NetworkResourceLoader::didBecomeDownload(). In NetworkResourceLoader::abort() we already handle the case of
having a NetworkLoad after NetworkResourceLoader::didConvertToDownload() has been called, to avoid canceling the
load in such case, however cleanup() is always called unconditionally and the NetworkLoad is deleted before
NetworkResourceLoader::didBecomeDownload() is called. When the NetworkLoad is destroyed the NetworkDataTask
client becomes nullptr, leaving it in a state where both the client is nullptr and the download hasn't been
created yet. That's not expected to happen and when the response completion handler is called in the
NetworkDataTask it tries to use either the client or the download and it crashes.

* NetworkProcess/NetworkConnectionToWebProcess.cpp:
(WebKit::NetworkConnectionToWebProcess::removeLoadIdentifier): Update ASSERT, because abort doesn't cleanup the
resource loader in case it's becoming a download.
* NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::didBecomeDownload): Call cleanup() instead of just deleting the network load.
(WebKit::NetworkResourceLoader::isBecomingDownload): Helper method to check if the resource load was converted to a
download, but didBecomeDownload() hasn't been called yet.
(WebKit::NetworkResourceLoader::abort): If the resource load is becoming a download do not call cleanup()
because it will be called by didBecomeDownload() later.
* NetworkProcess/NetworkResourceLoader.h:

Tools:

Split /webkit2/Downloads/policy-decision-download in two, one to test the full load when main resource is
converted to a download and another one to test the cancellation as the test was doing before. When doing the
full load, delay a bit the decide destination to ensure the load is aborted before the data task has became a
download.

* TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp:
(testPolicyResponseDownload):
(testPolicyResponseDownloadCancel):
(beforeAll):

Modified Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (208292 => 208293)


--- trunk/Source/WebKit2/ChangeLog	2016-11-02 18:11:13 UTC (rev 208292)
+++ trunk/Source/WebKit2/ChangeLog	2016-11-02 18:24:18 UTC (rev 208293)
@@ -1,3 +1,33 @@
+2016-11-02  Carlos Garcia Campos  <[email protected]>
+
+        NetworkSession: Network process crash when converting main resource to download
+        https://bugs.webkit.org/show_bug.cgi?id=164220
+
+        Reviewed by Alex Christensen.
+
+        Right after the main resource load is converted to a download, the web process deletes the ResourceLoader which
+        sends the RemoveLoadIdentifier to the network process and the load is aborted. Sometimes it happens that
+        NetworkResourceLoader::abort() is called while the NetworkLoad is still deciding the destination of the
+        download. In such case, NetworkResourceLoader::didConvertToDownload() has already been called, but not
+        NetworkResourceLoader::didBecomeDownload(). In NetworkResourceLoader::abort() we already handle the case of
+        having a NetworkLoad after NetworkResourceLoader::didConvertToDownload() has been called, to avoid canceling the
+        load in such case, however cleanup() is always called unconditionally and the NetworkLoad is deleted before
+        NetworkResourceLoader::didBecomeDownload() is called. When the NetworkLoad is destroyed the NetworkDataTask
+        client becomes nullptr, leaving it in a state where both the client is nullptr and the download hasn't been
+        created yet. That's not expected to happen and when the response completion handler is called in the
+        NetworkDataTask it tries to use either the client or the download and it crashes.
+
+        * NetworkProcess/NetworkConnectionToWebProcess.cpp:
+        (WebKit::NetworkConnectionToWebProcess::removeLoadIdentifier): Update ASSERT, because abort doesn't cleanup the
+        resource loader in case it's becoming a download.
+        * NetworkProcess/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::didBecomeDownload): Call cleanup() instead of just deleting the network load.
+        (WebKit::NetworkResourceLoader::isBecomingDownload): Helper method to check if the resource load was converted to a
+        download, but didBecomeDownload() hasn't been called yet.
+        (WebKit::NetworkResourceLoader::abort): If the resource load is becoming a download do not call cleanup()
+        because it will be called by didBecomeDownload() later.
+        * NetworkProcess/NetworkResourceLoader.h:
+
 2016-11-02  David Kilzer  <[email protected]>
 
         Add logging for "WebKit encountered an internal error" messages

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp (208292 => 208293)


--- trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp	2016-11-02 18:11:13 UTC (rev 208292)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp	2016-11-02 18:24:18 UTC (rev 208293)
@@ -158,7 +158,7 @@
     // Abort the load now, as the WebProcess won't be able to respond to messages any more which might lead
     // to leaked loader resources (connections, threads, etc).
     loader->abort();
-    ASSERT(!m_networkResourceLoaders.contains(identifier));
+    ASSERT(!m_networkResourceLoaders.contains(identifier) || loader->isBecomingDownload());
 }
 
 void NetworkConnectionToWebProcess::setDefersLoading(ResourceLoadIdentifier identifier, bool defers)

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp (208292 => 208293)


--- trunk/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp	2016-11-02 18:11:13 UTC (rev 208292)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp	2016-11-02 18:24:18 UTC (rev 208293)
@@ -286,10 +286,19 @@
 {
     ASSERT(m_didConvertToDownload);
     ASSERT(m_networkLoad);
-    m_networkLoad = nullptr;
+    cleanup();
 }
 #endif
 
+bool NetworkResourceLoader::isBecomingDownload() const
+{
+#if USE(NETWORK_SESSION)
+    return m_networkLoad && m_didConvertToDownload;
+#else
+    return false;
+#endif
+}
+
 void NetworkResourceLoader::abort()
 {
     ASSERT(RunLoop::isMain());
@@ -308,6 +317,9 @@
         m_networkLoad->cancel();
     }
 
+    if (isBecomingDownload())
+        return;
+
     cleanup();
 }
 

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h (208292 => 208293)


--- trunk/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h	2016-11-02 18:11:13 UTC (rev 208292)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h	2016-11-02 18:24:18 UTC (rev 208293)
@@ -81,6 +81,7 @@
     ResourceLoadIdentifier identifier() const { return m_parameters.identifier; }
     uint64_t frameID() const { return m_parameters.webFrameID; }
     uint64_t pageID() const { return m_parameters.webPageID; }
+    bool isBecomingDownload() const;
 
     struct SynchronousLoadData;
 

Modified: trunk/Tools/ChangeLog (208292 => 208293)


--- trunk/Tools/ChangeLog	2016-11-02 18:11:13 UTC (rev 208292)
+++ trunk/Tools/ChangeLog	2016-11-02 18:24:18 UTC (rev 208293)
@@ -1,3 +1,20 @@
+2016-11-02  Carlos Garcia Campos  <[email protected]>
+
+        NetworkSession: Network process crash when converting main resource to download
+        https://bugs.webkit.org/show_bug.cgi?id=164220
+
+        Reviewed by Alex Christensen.
+
+        Split /webkit2/Downloads/policy-decision-download in two, one to test the full load when main resource is
+        converted to a download and another one to test the cancellation as the test was doing before. When doing the
+        full load, delay a bit the decide destination to ensure the load is aborted before the data task has became a
+        download.
+
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp:
+        (testPolicyResponseDownload):
+        (testPolicyResponseDownloadCancel):
+        (beforeAll):
+
 2016-11-01  Alex Christensen  <[email protected]>
 
         Remove PassRefPtr from DumpRenderTree

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp (208292 => 208293)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp	2016-11-02 18:11:13 UTC (rev 208292)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp	2016-11-02 18:24:18 UTC (rev 208293)
@@ -515,6 +515,8 @@
     {
         test->m_download = download;
         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download));
+        g_signal_connect(download, "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), test);
+        g_signal_connect(download, "finished", G_CALLBACK(downloadFinishedCallback), test);
         test->quitMainLoop();
     }
 
@@ -539,6 +541,8 @@
     {
         GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr));
         GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0));
+        if (test->m_shouldDelayDecideDestination)
+            g_usleep(0.2 * G_USEC_PER_SEC);
         webkit_download_set_destination(download, destinationURI.get());
         return TRUE;
     }
@@ -550,12 +554,11 @@
 
     void waitUntilDownloadFinished()
     {
-        g_signal_connect(m_download.get(), "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), this);
-        g_signal_connect(m_download.get(), "finished", G_CALLBACK(downloadFinishedCallback), this);
         g_main_loop_run(m_mainLoop);
     }
 
     GRefPtr<WebKitDownload> m_download;
+    bool m_shouldDelayDecideDestination { false };
 };
 
 static void testWebViewDownloadURI(WebViewDownloadTest* test, gconstpointer)
@@ -604,8 +607,10 @@
 
 static void testPolicyResponseDownload(PolicyResponseDownloadTest* test, gconstpointer)
 {
-    // Test that a download started by the the policy checker contains the web view.
     CString requestURI = kServer->getURIForPath("/test.pdf").data();
+    // Delay the DecideDestination to ensure that the load is aborted before the network task has became a download.
+    // See https://bugs.webkit.org/show_bug.cgi?id=164220.
+    test->m_shouldDelayDecideDestination = true;
     test->loadURI(requestURI.data());
     test->waitUntilDownloadStarted();
 
@@ -614,6 +619,25 @@
     ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
 
     g_assert(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
+    test->waitUntilDownloadFinished();
+
+    GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get())));
+    GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr));
+    g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0);
+    g_file_delete(downloadFile.get(), nullptr, nullptr);
+}
+
+static void testPolicyResponseDownloadCancel(PolicyResponseDownloadTest* test, gconstpointer)
+{
+    CString requestURI = kServer->getURIForPath("/test.pdf").data();
+    test->loadURI(requestURI.data());
+    test->waitUntilDownloadStarted();
+
+    WebKitURIRequest* request = webkit_download_get_request(test->m_download.get());
+    g_assert(request);
+    ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
+
+    g_assert(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
     test->cancelDownloadAndWaitUntilFinished();
 }
 
@@ -657,6 +681,7 @@
     DownloadErrorTest::add("Downloads", "remote-file-error", testDownloadRemoteFileError);
     WebViewDownloadTest::add("WebKitWebView", "download-uri", testWebViewDownloadURI);
     PolicyResponseDownloadTest::add("Downloads", "policy-decision-download", testPolicyResponseDownload);
+    PolicyResponseDownloadTest::add("Downloads", "policy-decision-download-cancel", testPolicyResponseDownloadCancel);
     DownloadTest::add("Downloads", "mime-type", testDownloadMIMEType);
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to