diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp index a7da9ae600..e031e1c77b 100644 --- a/src/corelib/io/qbuffer.cpp +++ b/src/corelib/io/qbuffer.cpp @@ -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::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; diff --git a/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp b/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp index 7a68da8192..fb756d9e63 100644 --- a/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp +++ b/tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp @@ -31,6 +31,9 @@ #include #include +#include + +#include 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;