diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-03-14 10:06:52 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-11-24 12:42:12 -0800 |
commit | 8af346c1f66f813c3c8fe4d8b892ecfbe96eacfb (patch) | |
tree | 2a538186ff6f523aa9a756647b596aa7256b35ec /src/corelib/serialization | |
parent | 8e8815b688684f5b267db1c2d8ac99c8f7f7637a (diff) |
QCborStreamReader: add toString() and toByteArray()
They've been a long time coming. I had them in TinyCBOR before the
chunked reading; in fact, I added the chunked reading specifically for
Qt's CBOR support.
[ChangeLog][QtCore][QCborStreamReader] Added toString() and
toByteArray(), which read a full string whether it is chunked or
not. They also guard against attempting to allocate more memory than the
underlying stream could provide.
Change-Id: Icfe44ecf285a480fafe4fffd174c5815f153d5b5
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/corelib/serialization')
-rw-r--r-- | src/corelib/serialization/qcborstreamreader.cpp | 121 | ||||
-rw-r--r-- | src/corelib/serialization/qcborstreamreader.h | 18 |
2 files changed, 137 insertions, 2 deletions
diff --git a/src/corelib/serialization/qcborstreamreader.cpp b/src/corelib/serialization/qcborstreamreader.cpp index 47d1f8e014..041d2aa27e 100644 --- a/src/corelib/serialization/qcborstreamreader.cpp +++ b/src/corelib/serialization/qcborstreamreader.cpp @@ -630,6 +630,7 @@ public: }; static QCborStreamReader::StringResultCode appendStringChunk(QCborStreamReader &reader, QByteArray *data); + bool readFullString(ReadStringChunk params); QCborStreamReader::StringResult<qsizetype> readStringChunk(ReadStringChunk params); qsizetype readStringChunk_byte(ReadStringChunk params, qsizetype len); qsizetype readStringChunk_unicode(ReadStringChunk params, qsizetype utf8len); @@ -1295,11 +1296,15 @@ bool QCborStreamReader::leaveContainer() \snippet code/src_corelib_serialization_qcborstream.cpp 27 + The toString() function implements the above loop and some extra checks. + +//! [string-no-type-conversions] This function does not perform any type conversions, including from integers or from byte arrays. Therefore, it may only be called if isString() returned true; calling it in any other condition is an error. +//! [string-no-type-conversions] - \sa readByteArray(), isString(), readStringChunk() + \sa toString(), readByteArray(), isString(), readStringChunk() */ QCborStreamReader::StringResult<QString> QCborStreamReader::_readString_helper() { @@ -1327,11 +1332,15 @@ QCborStreamReader::StringResult<QString> QCborStreamReader::_readString_helper() \snippet code/src_corelib_serialization_qcborstream.cpp 28 + The toByteArray() function implements the above loop and some extra checks. + +//! [bytearray-no-type-conversions] This function does not perform any type conversions, including from integers or from strings. Therefore, it may only be called if isByteArray() is true; calling it in any other condition is an error. +//! [bytearray-no-type-conversions] - \sa readString(), isByteArray(), readStringChunk() + \sa toByteArray(), readString(), isByteArray(), readStringChunk() */ QCborStreamReader::StringResult<QByteArray> QCborStreamReader::_readByteArray_helper() { @@ -1380,6 +1389,108 @@ qsizetype QCborStreamReader::_currentStringChunkSize() const return -1; } +bool QCborStreamReaderPrivate::readFullString(ReadStringChunk params) +{ + auto r = readStringChunk(params); + while (r.status == QCborStreamReader::Ok) { + // keep appending + r = readStringChunk(params); + } + + bool ok = r.status == QCborStreamReader::EndOfString; + Q_ASSERT(ok == !lastError); + return ok; +} + +/*! + \fn QCborStreamReader::toString() + \since 6.7 + + Decodes the current text string and returns it. If the string is chunked, + this function will iterate over all chunks and concatenate them. If an + error happens, this function returns a default-constructed QString(), but + that may not be distinguishable from certain empty text strings. Instead, + check lastError() to determine if an error has happened. + + \include qcborstreamreader.cpp string-no-type-conversions + +//! [note-not-restartable] + \note This function cannot be resumed. That is, this function should not + be used in contexts where the CBOR data may still be received, for example + from a socket or pipe. It should only be used when the full data has + already been received and is available in the input QByteArray or + QIODevice. +//! [note-not-restartable] + + \sa readString(), readStringChunk(), isString(), toByteArray() + */ +/*! + \fn QCborStreamReader::toString(QString &dst) + \overload + \since 6.7 + + Decodes the current text string and appends to \a dst. If the string is + chunked, this function will iterate over all chunks and concatenate them. + If an error happens during decoding, other chunks that could be decoded + successfully may have been written to \a dst nonetheless. Returns \c true + if the decoding happened without errors, \c false otherwise. + + \include qcborstreamreader.cpp string-no-type-conversions + + \include qcborstreamreader.cpp note-not-restartable + + \sa readString(), readStringChunk(), isString(), toByteArray() + */ +bool QCborStreamReader::_toString_helper(QString &dst) +{ + bool ok = d->readFullString(&dst); + if (ok) + preparse(); + return ok; +} + +/*! + \fn QCborStreamReader::toByteArray() + \since 6.7 + + Decodes the current byte string and returns it. If the string is chunked, + this function will iterate over all chunks and concatenate them. If an + error happens, this function returns a default-constructed QByteArray(), + but that may not be distinguishable from certain empty byte strings. + Instead, check lastError() to determine if an error has happened. + + \include qcborstreamreader.cpp bytearray-no-type-conversions + + \include qcborstreamreader.cpp note-not-restartable + + \sa readByteArray(), readStringChunk(), isByteArray(), toString() + */ + +/*! + \fn QCborStreamReader::toByteArray(QByteArray &dst) + \overload + \since 6.7 + + Decodes the current byte string and appends to \a dst. If the string is + chunked, this function will iterate over all chunks and concatenate them. + If an error happens during decoding, other chunks that could be decoded + successfully may have been written to \a dst nonetheless. Returns \c true + if the decoding happened without errors, \c false otherwise. + + \include qcborstreamreader.cpp bytearray-no-type-conversions + + \include qcborstreamreader.cpp note-not-restartable + + \sa readByteArray(), readStringChunk(), isByteArray(), toString() + */ +bool QCborStreamReader::_toByteArray_helper(QByteArray &dst) +{ + bool ok = d->readFullString(&dst); + if (ok) + preparse(); + return ok; +} + /*! Reads the current string chunk into the buffer pointed to by \a ptr, whose size is \a maxlen. This function returns a \l StringResult object, with the @@ -1452,6 +1563,12 @@ QCborStreamReaderPrivate::readStringChunk(ReadStringChunk params) // qt_cbor_decoder_transfer_string() enforces that // QIODevice::bytesAvailable() be bigger than the amount we're about to // read. + // + // This is an important security gate: if the CBOR stream is corrupt or + // malicious, and has an impossibly large string size, we only go past it + // if the transfer to the destination buffer will succeed (modulo QIODevice + // I/O failures). + #if 1 // Using internal TinyCBOR API! err = _cbor_value_get_string_chunk(¤tElement, &content, &len, ¤tElement); diff --git a/src/corelib/serialization/qcborstreamreader.h b/src/corelib/serialization/qcborstreamreader.h index 5a870d17a3..5e0b293863 100644 --- a/src/corelib/serialization/qcborstreamreader.h +++ b/src/corelib/serialization/qcborstreamreader.h @@ -120,6 +120,8 @@ public: bool enterContainer() { Q_ASSERT(isContainer()); return _enterContainer_helper(); } bool leaveContainer(); + bool toString(QString &dst) { Q_ASSERT(isString()); return _toString_helper(dst); } + bool toByteArray(QByteArray &dst) { Q_ASSERT(isByteArray()); return _toByteArray_helper(dst); } StringResult<QString> readString() { Q_ASSERT(isString()); return _readString_helper(); } StringResult<QByteArray> readByteArray(){ Q_ASSERT(isByteArray()); return _readByteArray_helper(); } qsizetype currentStringChunkSize() const{ Q_ASSERT(isString() || isByteArray()); return _currentStringChunkSize(); } @@ -142,6 +144,20 @@ public: return -v - 1; return v; } + QString toString() + { + QString dst; + if (!toString(dst)) + dst.clear(); + return dst; + } + QByteArray toByteArray() + { + QByteArray dst; + if (!toByteArray(dst)) + dst.clear(); + return dst; + } private: void preparse(); @@ -149,6 +165,8 @@ private: StringResult<QString> _readString_helper(); StringResult<QByteArray> _readByteArray_helper(); qsizetype _currentStringChunkSize() const; + bool _toString_helper(QString &); + bool _toByteArray_helper(QByteArray &); template <typename FP> FP _toFloatingPoint() const noexcept { |