diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2017-09-27 13:23:27 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-11-08 16:15:20 +0000 |
commit | 1cc2ffbaaa177340bb6f2b0b14ac267a3f24b116 (patch) | |
tree | f4c80fc018b1e2bc9eb9f2154b4c36d587495cea | |
parent | 3f941dc062674630daa39770ac027658f8fb0302 (diff) |
Prevent crash when downloading large blob data
QByteArray::{from,to}Base64 are prone to integer overflow when input data
is large, which causes segfaults. We have to set threshold on data size
to prevent this from happening.
Also, changed data URL construction code to do less intermediate memory
reservations. Though biggest offender is QUrl constructor and it has to
stay.
Original commit:
https://github.com/annulen/webkit/commit/535c062962e3e0f425848e6b32ad1c95d25bec4b
Change-Id: Ieaf4c914fdff086e15d2358bdb19d378f2a11feb
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r-- | Source/WebCore/platform/network/qt/ResourceRequestQt.cpp | 60 | ||||
-rw-r--r-- | Source/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp | 4 | ||||
-rw-r--r-- | Tools/QtTestBrowser/launcherwindow.cpp | 10 |
3 files changed, 48 insertions, 26 deletions
diff --git a/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp b/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp index 83c50062d..2719f1db6 100644 --- a/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp +++ b/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp @@ -28,10 +28,9 @@ #include "BlobStorageData.h" #endif -#include <qglobal.h> - #include <QNetworkRequest> #include <QUrl> +#include <wtf/text/Base64.h> namespace WebCore { @@ -47,11 +46,11 @@ unsigned initializeMaximumHTTPConnectionCountPerHost() } #if ENABLE(BLOB) -static void appendBlobResolved(QByteArray& data, const QUrl& url, QString* contentType = 0) +static bool appendBlobResolved(Vector<char>& out, const KURL& url, QString* contentType = 0) { RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url); if (!blobData) - return; + return false; if (contentType) *contentType = blobData->contentType(); @@ -60,35 +59,58 @@ static void appendBlobResolved(QByteArray& data, const QUrl& url, QString* conte const BlobDataItemList::const_iterator itend = blobData->items().end(); for (; it != itend; ++it) { const BlobDataItem& blobItem = *it; - if (blobItem.type == BlobDataItem::Data) - data.append(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length)); - else if (blobItem.type == BlobDataItem::Blob) - appendBlobResolved(data, blobItem.url); - else if (blobItem.type == BlobDataItem::File) { + if (blobItem.type == BlobDataItem::Data) { + if (!out.tryAppend(reinterpret_cast<const char*>(blobItem.data->data()) + blobItem.offset, blobItem.length)) + return false; + } else if (blobItem.type == BlobDataItem::File) { // File types are not allowed here, so just ignore it. } else ASSERT_NOT_REACHED(); } + return true; } -static void resolveBlobUrl(const QUrl& url, QUrl& resolvedUrl) +static QUrl resolveBlobUrl(const KURL& url) { RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url); if (!blobData) - return; + return QUrl(); - QByteArray data; + Vector<char> data; QString contentType; - appendBlobResolved(data, url, &contentType); + if (!appendBlobResolved(data, url, &contentType)) { + qWarning("Failed to convert blob data to base64: cannot allocate memory for continuous blob data"); + return QUrl(); + } + + // QByteArray::{from,to}Base64 are prone to integer overflow, this is the maximum size that can be safe + size_t maxBase64Size = std::numeric_limits<int>::max() / 3 - 1; + + Vector<char> base64; + WTF::base64Encode(data, base64, WTF::Base64URLPolicy); + if (base64.isEmpty() || base64.size() > maxBase64Size) { + qWarning("Failed to convert blob data to base64: data is too large"); + return QUrl(); + } QString dataUri(QStringLiteral("data:")); dataUri.append(contentType); dataUri.append(QStringLiteral(";base64,")); - dataUri.append(QString::fromLatin1(data.toBase64())); - resolvedUrl = QUrl(dataUri); + dataUri.reserve(dataUri.size() + base64.size()); + dataUri.append(QLatin1String(base64.data(), base64.size())); + return QUrl(dataUri); } #endif +static QUrl toQUrl(const KURL& url) +{ +#if ENABLE(BLOB) + if (url.protocolIs("blob")) + return resolveBlobUrl(url); +#endif + return url; +} + static inline QByteArray stringToByteArray(const String& string) { if (string.is8Bit()) @@ -99,13 +121,7 @@ static inline QByteArray stringToByteArray(const String& string) QNetworkRequest ResourceRequest::toNetworkRequest(NetworkingContext *context) const { QNetworkRequest request; - QUrl newurl = url(); - -#if ENABLE(BLOB) - if (newurl.scheme() == QLatin1String("blob")) - resolveBlobUrl(url(), newurl); -#endif - + QUrl newurl = toQUrl(url()); request.setUrl(newurl); request.setOriginatingObject(context ? context->originatingObject() : 0); diff --git a/Source/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp b/Source/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp index 0b811c8f2..e38c8b6b1 100644 --- a/Source/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp +++ b/Source/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp @@ -1300,7 +1300,9 @@ void FrameLoaderClientQt::startDownload(const WebCore::ResourceRequest& request, if (!m_webFrame) return; - m_webFrame->pageAdapter->emitDownloadRequested(request.toNetworkRequest(m_frame->loader()->networkingContext())); + QNetworkRequest r = request.toNetworkRequest(m_frame->loader()->networkingContext()); + if (r.url().isValid()) + m_webFrame->pageAdapter->emitDownloadRequested(r); } PassRefPtr<Frame> FrameLoaderClientQt::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) diff --git a/Tools/QtTestBrowser/launcherwindow.cpp b/Tools/QtTestBrowser/launcherwindow.cpp index 0cb0ab2ce..b40a78c8b 100644 --- a/Tools/QtTestBrowser/launcherwindow.cpp +++ b/Tools/QtTestBrowser/launcherwindow.cpp @@ -1118,9 +1118,13 @@ void LauncherWindow::downloadRequest(const QNetworkRequest &request) void LauncherWindow::fileDownloadFinished() { - QFileInfo fileInf(m_reply->request().url().toString()); - QString requestFileName = QDir::homePath() + "/" + fileInf.fileName(); - QString fileName = QFileDialog::getSaveFileName(this, "Save as...", requestFileName, "All Files (*)"); + QString suggestedFileName; + if (m_reply->request().url().scheme().toLower() != QLatin1String("data")) { + QFileInfo fileInf(m_reply->request().url().toString()); + suggestedFileName = QDir::homePath() + "/" + fileInf.fileName(); + } else + suggestedFileName = QStringLiteral("data"); + QString fileName = QFileDialog::getSaveFileName(this, "Save as...", suggestedFileName, "All Files (*)"); if (fileName.isEmpty()) return; |