Skip to content

Commit

Permalink
Don't read unlimited data from files
Browse files Browse the repository at this point in the history
It now guards against reading infinite files such as `/dev/zero`.
And most readings are bound with a (lax) limit.
As a side effect, more checking are done when reading a file and
overall the reading procedure is more robust.

PR #19095.
  • Loading branch information
Chocobo1 committed Jun 14, 2023
1 parent 81bc910 commit 79ca2e1
Show file tree
Hide file tree
Showing 24 changed files with 369 additions and 198 deletions.
25 changes: 15 additions & 10 deletions src/base/bittorrent/bencoderesumedatastorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QRegularExpression>
#include <QThread>

Expand Down Expand Up @@ -133,17 +134,19 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::load(cons
const Path fastresumePath = path() / Path(idString + u".fastresume");
const Path torrentFilePath = path() / Path(idString + u".torrent");

QFile resumeDataFile {fastresumePath.data()};
if (!resumeDataFile.open(QIODevice::ReadOnly))
return nonstd::make_unexpected(tr("Cannot read file %1: %2").arg(fastresumePath.toString(), resumeDataFile.errorString()));
const auto resumeDataReadResult = Utils::IO::readFile(fastresumePath, MAX_TORRENT_SIZE);
if (!resumeDataReadResult)
return nonstd::make_unexpected(resumeDataReadResult.error().message);

QFile metadataFile {torrentFilePath.data()};
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
return nonstd::make_unexpected(tr("Cannot read file %1: %2").arg(torrentFilePath.toString(), metadataFile.errorString()));

const QByteArray data = resumeDataFile.readAll();
const QByteArray metadata = (metadataFile.isOpen() ? metadataFile.readAll() : "");
const auto metadataReadResult = Utils::IO::readFile(torrentFilePath, MAX_TORRENT_SIZE);
if (!metadataReadResult)
{
if (metadataReadResult.error().status != Utils::IO::ReadError::NotExist)
return nonstd::make_unexpected(metadataReadResult.error().message);
}

const QByteArray data = resumeDataReadResult.value();
const QByteArray metadata = metadataReadResult.value_or(QByteArray());
return loadTorrentResumeData(data, metadata);
}

Expand All @@ -161,6 +164,8 @@ void BitTorrent::BencodeResumeDataStorage::doLoadAll() const

void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
{
const int lineMaxLength = 48;

QFile queueFile {queueFilename.data()};
if (!queueFile.exists())
return;
Expand All @@ -175,7 +180,7 @@ void BitTorrent::BencodeResumeDataStorage::loadQueue(const Path &queueFilename)
int start = 0;
while (true)
{
const auto line = QString::fromLatin1(queueFile.readLine().trimmed());
const auto line = QString::fromLatin1(queueFile.readLine(lineMaxLength).trimmed());
if (line.isEmpty())
break;

Expand Down
1 change: 0 additions & 1 deletion src/base/bittorrent/dbresumedatastorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@

#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QMutex>
#include <QSet>
#include <QSqlDatabase>
Expand Down
20 changes: 10 additions & 10 deletions src/base/bittorrent/sessionimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@

#include <QDebug>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QJsonArray>
#include <QJsonDocument>
Expand Down Expand Up @@ -5101,8 +5100,8 @@ void SessionImpl::loadCategories()
{
m_categories.clear();

QFile confFile {(specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME).data()};
if (!confFile.exists())
const Path path = specialFolderLocation(SpecialFolder::Config) / CATEGORIES_FILE_NAME;
if (!path.exists())
{
// TODO: Remove the following upgrade code in v4.5
// == BEGIN UPGRADE CODE ==
Expand All @@ -5113,26 +5112,27 @@ void SessionImpl::loadCategories()
// return;
}

if (!confFile.open(QFile::ReadOnly))
const int fileMaxSize = 1024 * 1024;
const auto readResult = Utils::IO::readFile(path, fileMaxSize);
if (!readResult)
{
LogMsg(tr("Failed to load Categories. File: \"%1\". Error: \"%2\"")
.arg(confFile.fileName(), confFile.errorString()), Log::CRITICAL);
LogMsg(tr("Failed to load Categories. %1").arg(readResult.error().message), Log::WARNING);
return;
}

QJsonParseError jsonError;
const QJsonDocument jsonDoc = QJsonDocument::fromJson(confFile.readAll(), &jsonError);
const QJsonDocument jsonDoc = QJsonDocument::fromJson(readResult.value(), &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
LogMsg(tr("Failed to parse Categories configuration. File: \"%1\". Error: \"%2\"")
.arg(confFile.fileName(), jsonError.errorString()), Log::WARNING);
.arg(path.toString(), jsonError.errorString()), Log::WARNING);
return;
}

if (!jsonDoc.isObject())
{
LogMsg(tr("Failed to load Categories configuration. File: \"%1\". Reason: invalid data format")
.arg(confFile.fileName()), Log::WARNING);
LogMsg(tr("Failed to load Categories configuration. File: \"%1\". Error: \"Invalid data format\"")
.arg(path.toString()), Log::WARNING);
return;
}

Expand Down
1 change: 0 additions & 1 deletion src/base/bittorrent/torrentimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@

#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QPointer>
#include <QSet>
#include <QStringList>
Expand Down
20 changes: 6 additions & 14 deletions src/base/bittorrent/torrentinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,28 +103,20 @@ nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data)

nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const Path &path) noexcept
{
QFile file {path.data()};
if (!file.open(QIODevice::ReadOnly))
return nonstd::make_unexpected(file.errorString());

if (file.size() > MAX_TORRENT_SIZE)
return nonstd::make_unexpected(tr("File size exceeds max limit %1").arg(Utils::Misc::friendlyUnit(MAX_TORRENT_SIZE)));

QByteArray data;
try
{
data = file.readAll();
const auto readResult = Utils::IO::readFile(path, MAX_TORRENT_SIZE);
if (!readResult)
return nonstd::make_unexpected(readResult.error().message);
data = readResult.value();
}
catch (const std::bad_alloc &e)
{
return nonstd::make_unexpected(tr("Torrent file read error: %1").arg(QString::fromLocal8Bit(e.what())));
return nonstd::make_unexpected(tr("Failed to allocate memory when reading file. File: \"%1\". Error: \"%2\"")
.arg(path.toString(), QString::fromLocal8Bit(e.what())));
}

if (data.size() != file.size())
return nonstd::make_unexpected(tr("Torrent file read error: size mismatch"));

file.close();

return load(data);
}

Expand Down
29 changes: 13 additions & 16 deletions src/base/rss/feed_serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

#include "feed_serializer.h"

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
Expand All @@ -46,23 +45,21 @@ const int ARTICLEDATALIST_TYPEID = qRegisterMetaType<QVector<QVariantHash>>();

void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
{
QFile file {dataFileName.data()};

if (!file.exists())
{
emit loadingFinished({});
}
else if (file.open(QFile::ReadOnly))
{
emit loadingFinished(loadArticles(file.readAll(), url));
file.close();
}
else
const int fileMaxSize = 10 * 1024 * 1024;
const auto readResult = Utils::IO::readFile(dataFileName, fileMaxSize);
if (!readResult)
{
LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
.arg(dataFileName.toString(), file.errorString())
, Log::WARNING);
if (readResult.error().status == Utils::IO::ReadError::NotExist)
{
emit loadingFinished({});
return;
}

LogMsg(tr("Failed to read RSS session data. %1").arg(readResult.error().message), Log::WARNING);
return;
}

emit loadingFinished(loadArticles(readResult.value(), url));
}

void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QVector<QVariantHash> &articlesData)
Expand Down
27 changes: 14 additions & 13 deletions src/base/rss/rss_autodownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "../logger.h"
#include "../profile.h"
#include "../utils/fs.h"
#include "../utils/io.h"
#include "rss_article.h"
#include "rss_autodownloadrule.h"
#include "rss_feed.h"
Expand Down Expand Up @@ -493,21 +494,21 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)

void AutoDownloader::load()
{
QFile rulesFile {(m_fileStorage->storageDir() / Path(RULES_FILE_NAME)).data()};

if (!rulesFile.exists())
{
loadRulesLegacy();
}
else if (rulesFile.open(QFile::ReadOnly))
{
loadRules(rulesFile.readAll());
}
else
const qint64 maxFileSize = 10 * 1024 * 1024;
const auto readResult = Utils::IO::readFile((m_fileStorage->storageDir() / Path(RULES_FILE_NAME)), maxFileSize);
if (!readResult)
{
LogMsg(tr("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
.arg(rulesFile.fileName(), rulesFile.errorString()), Log::CRITICAL);
if (readResult.error().status == Utils::IO::ReadError::NotExist)
{
loadRulesLegacy();
return;
}

LogMsg((tr("Failed to read RSS AutoDownloader rules. %1").arg(readResult.error().message)), Log::WARNING);
return;
}

loadRules(readResult.value());
}

void AutoDownloader::loadRules(const QByteArray &data)
Expand Down
31 changes: 17 additions & 14 deletions src/base/rss/rss_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "../profile.h"
#include "../settingsstorage.h"
#include "../utils/fs.h"
#include "../utils/io.h"
#include "rss_article.h"
#include "rss_feed.h"
#include "rss_folder.h"
Expand Down Expand Up @@ -261,33 +262,35 @@ Item *Session::itemByPath(const QString &path) const

void Session::load()
{
QFile itemsFile {(m_confFileStorage->storageDir() / Path(FEEDS_FILE_NAME)).data()};
if (!itemsFile.exists())
{
loadLegacy();
return;
}
const int fileMaxSize = 10 * 1024 * 1024;
const Path path = m_confFileStorage->storageDir() / Path(FEEDS_FILE_NAME);

if (!itemsFile.open(QFile::ReadOnly))
const auto readResult = Utils::IO::readFile(path, fileMaxSize);
if (!readResult)
{
LogMsg(tr("Couldn't read RSS session data. File: \"%1\". Error: \"%2\"")
.arg(itemsFile.fileName(), itemsFile.errorString()), Log::WARNING);
if (readResult.error().status == Utils::IO::ReadError::NotExist)
{
loadLegacy();
return;
}

LogMsg(tr("Failed to read RSS session data. %1").arg(readResult.error().message), Log::WARNING);
return;
}

QJsonParseError jsonError;
const QJsonDocument jsonDoc = QJsonDocument::fromJson(itemsFile.readAll(), &jsonError);
const QJsonDocument jsonDoc = QJsonDocument::fromJson(readResult.value(), &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
LogMsg(tr("Couldn't parse RSS session data. File: \"%1\". Error: \"%2\"")
.arg(itemsFile.fileName(), jsonError.errorString()), Log::WARNING);
LogMsg(tr("Failed to parse RSS session data. File: \"%1\". Error: \"%2\"")
.arg(path.toString(), jsonError.errorString()), Log::WARNING);
return;
}

if (!jsonDoc.isObject())
{
LogMsg(tr("Couldn't load RSS session data. File: \"%1\". Error: Invalid data format.")
.arg(itemsFile.fileName()), Log::WARNING);
LogMsg(tr("Failed to load RSS session data. File: \"%1\". Error: \"Invalid data format.\"")
.arg(path.toString()), Log::WARNING);
return;
}

Expand Down
7 changes: 5 additions & 2 deletions src/base/search/searchpluginmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <QDomDocument>
#include <QDomElement>
#include <QDomNode>
#include <QFile>
#include <QPointer>
#include <QProcess>
#include <QUrl>
Expand Down Expand Up @@ -517,7 +518,7 @@ void SearchPluginManager::update()
nova.start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly);
nova.waitForFinished();

const auto capabilities = QString::fromUtf8(nova.readAll());
const auto capabilities = QString::fromUtf8(nova.readAllStandardOutput());
QDomDocument xmlDoc;
if (!xmlDoc.setContent(capabilities))
{
Expand Down Expand Up @@ -629,13 +630,15 @@ Path SearchPluginManager::pluginPath(const QString &name)

PluginVersion SearchPluginManager::getPluginVersion(const Path &filePath)
{
const int lineMaxLength = 16;

QFile pluginFile {filePath.data()};
if (!pluginFile.open(QIODevice::ReadOnly | QIODevice::Text))
return {};

while (!pluginFile.atEnd())
{
const auto line = QString::fromUtf8(pluginFile.readLine()).remove(u' ');
const auto line = QString::fromUtf8(pluginFile.readLine(lineMaxLength)).remove(u' ');
if (!line.startsWith(u"#VERSION:", Qt::CaseInsensitive)) continue;

const QString versionStr = line.mid(9);
Expand Down

0 comments on commit 79ca2e1

Please sign in to comment.