QBuffer: fix writing more than two GiB of data
In Qt 6, QByteArray can hold more than two GiB of data on 64-bit platforms, so QBuffer should be able to handle writes of more than two GiB, too. But the implementation didn't check for overflow and held sizes in int variables, so it happily reported success but wrote data only mod INT_MAX. Fix by carefully avoiding overflow and using size variables of proper type. [ChangeLog][QtCore][QBuffer] Fixed silent data truncation when writing more than two GiB at once on 64-bit platforms. Pick-to: 6.3 6.2 Fixes: QTBUG-102171 Change-Id: Ib666f9f7db24495b4ed64191a48b35edc410f7e9 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
30873cc720
commit
61157c8354
@ -421,17 +421,19 @@ qint64 QBuffer::readData(char *data, qint64 len)
|
||||
qint64 QBuffer::writeData(const char *data, qint64 len)
|
||||
{
|
||||
Q_D(QBuffer);
|
||||
int extraBytes = pos() + len - d->buf->size();
|
||||
if (extraBytes > 0) { // overflow
|
||||
int newSize = d->buf->size() + extraBytes;
|
||||
d->buf->resize(newSize);
|
||||
if (d->buf->size() != newSize) { // could not resize
|
||||
const quint64 required = quint64(pos()) + quint64(len); // cannot overflow (pos() ≥ 0, len ≥ 0)
|
||||
|
||||
if (required > quint64(d->buf->size())) { // capacity exceeded
|
||||
// The following must hold, since qsizetype covers half the virtual address space:
|
||||
Q_ASSUME(required <= quint64((std::numeric_limits<qsizetype>::max)()));
|
||||
d->buf->resize(qsizetype(required));
|
||||
if (quint64(d->buf->size()) != required) { // could not resize
|
||||
qWarning("QBuffer::writeData: Memory allocation error");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(d->buf->data() + pos(), data, int(len));
|
||||
memcpy(d->buf->data() + pos(), data, size_t(len));
|
||||
|
||||
#ifndef QT_NO_QOBJECT
|
||||
d->writtenSinceLastEmit += len;
|
||||
|
@ -31,6 +31,9 @@
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <string>
|
||||
|
||||
class tst_QBuffer : public QObject
|
||||
{
|
||||
@ -58,6 +61,7 @@ private slots:
|
||||
void readLineBoundaries();
|
||||
void getAndUngetChar();
|
||||
void writeAfterQByteArrayResize();
|
||||
void writeOfMoreThan2GiB();
|
||||
void read_null();
|
||||
|
||||
protected slots:
|
||||
@ -599,6 +603,55 @@ void tst_QBuffer::writeAfterQByteArrayResize()
|
||||
QCOMPARE(buffer.buffer().size(), 1000);
|
||||
}
|
||||
|
||||
void tst_QBuffer::writeOfMoreThan2GiB()
|
||||
{
|
||||
if constexpr (sizeof(void*) == 4)
|
||||
QSKIP("This is a 64-bit-only test");
|
||||
|
||||
[[maybe_unused]] constexpr size_t GiB = 1024 * 1024 * 1024;
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
|
||||
try {
|
||||
//
|
||||
// GIVEN: an empty QBuffer open for writing
|
||||
//
|
||||
QBuffer buffer;
|
||||
QVERIFY(buffer.open(QIODevice::WriteOnly));
|
||||
|
||||
//
|
||||
// WHEN: writing more than 2GiB in a singe chunk:
|
||||
//
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
const std::string input(2 * GiB + 1, 42);
|
||||
|
||||
qDebug("created dataset in %lld ms", timer.restart());
|
||||
|
||||
const auto inputSize = qint64(input.size());
|
||||
|
||||
QCOMPARE(buffer.write(input.data(), inputSize), inputSize);
|
||||
|
||||
qDebug("performed write in %lld ms", timer.restart());
|
||||
|
||||
//
|
||||
// THEN: the buffer contains the written data
|
||||
//
|
||||
QCOMPARE(buffer.buffer().size(), inputSize);
|
||||
QVERIFY(buffer.buffer() == QByteArrayView{input});
|
||||
|
||||
qDebug("verified result in %lld ms", timer.elapsed());
|
||||
|
||||
} catch (const std::bad_alloc &) {
|
||||
QSKIP("Cannot allocate enough memory for this test");
|
||||
}
|
||||
|
||||
#else
|
||||
QSKIP("This test requires exceptions enabled.");
|
||||
#endif // QT_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
void tst_QBuffer::read_null()
|
||||
{
|
||||
QByteArray buffer;
|
||||
|
Loading…
Reference in New Issue
Block a user