summaryrefslogtreecommitdiffstats
path: root/src/corelib/serialization
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2023-03-14 10:06:52 -0700
committerThiago Macieira <thiago.macieira@intel.com>2023-11-24 12:42:12 -0800
commit8af346c1f66f813c3c8fe4d8b892ecfbe96eacfb (patch)
tree2a538186ff6f523aa9a756647b596aa7256b35ec /src/corelib/serialization
parent8e8815b688684f5b267db1c2d8ac99c8f7f7637a (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.cpp121
-rw-r--r--src/corelib/serialization/qcborstreamreader.h18
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(&currentElement, &content, &len, &currentElement);
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
{