summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2020-04-15 13:58:33 -0300
committerEdward Welbourne <edward.welbourne@qt.io>2020-04-30 11:00:29 +0000
commit52a2505672cbb1ca8b5b32f7bc1259485c65483b (patch)
tree9acd649a7bbdc4a76f80466c1357a3f830fef2b6 /tests
parent1eeabc6652220ff263eaa63872cd52c1693cbf69 (diff)
QCborValue: avoid signed integer oveflows when decoding time_t
QDateTime::fromSecsSinceEpoch() multiplies by 1000 but does not check for overflow. That means we must do so in QCborValue validation. We can't use mul_overflow<qint64> on 32-bit platforms, so we do a compare- and-branch there. For 64-bit platforms, we prefer to do the multiplication with checked overflow, as the common case is that it will not overflow and we'll need the multiplication anyway. Change-Id: Ibdc95e9af7bd456a94ecfffd16060cba6f1c86b8 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp109
1 files changed, 109 insertions, 0 deletions
diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp
index 64321c11fa..0b3046fbdc 100644
--- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp
+++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp
@@ -106,6 +106,8 @@ private slots:
void fromCborStreamReaderIODevice();
void validation_data();
void validation();
+ void extendedTypeValidation_data();
+ void extendedTypeValidation();
void hugeDeviceValidation_data();
void hugeDeviceValidation();
void recursionLimit_data();
@@ -118,6 +120,68 @@ private slots:
void streamVariantSerialization();
};
+namespace SimpleEncodeToCbor {
+inline size_t lengthOf(int)
+{
+ return 1; // encode as byte
+}
+
+template <typename T> inline size_t lengthOf(T)
+{
+ return sizeof(T);
+}
+
+static void encodeOneAt(char *ptr, int v)
+{
+ // encode as byte
+ *ptr = char(v);
+}
+
+template <typename T>
+static typename std::enable_if<std::is_unsigned<T>::value>::type
+encodeOneAt(char *ptr, T v)
+{
+ qToBigEndian(v, ptr);
+}
+
+template <typename T>
+static typename std::enable_if<std::is_floating_point<T>::value ||
+ std::is_same<T, qfloat16>::value>::type
+encodeOneAt(char *ptr, T v)
+{
+ typename QIntegerForSizeof<T>::Unsigned u;
+ memcpy(&u, &v, sizeof(u));
+ qToBigEndian(u, ptr);
+}
+
+static char *encodeAt(char *ptr)
+{
+ return ptr;
+}
+
+template <typename Arg0, typename... Args>
+static char *encodeAt(char *ptr, Arg0 a0, Args... a)
+{
+ encodeOneAt(ptr, a0);
+ return encodeAt(ptr + lengthOf(a0), a...);
+}
+
+} // namespace SimpleEncodetoCbor
+
+template <typename... Args>
+static QByteArray encode(Args... a)
+{
+ // this would be much easier with C++17 fold expressions...
+ using namespace SimpleEncodeToCbor;
+ using namespace std;
+ size_t lengths[] = { lengthOf(a)... };
+ size_t total = accumulate(begin(lengths), end(lengths), size_t(0), plus<size_t>{});
+ QByteArray result(QByteArray::size_type(total), Qt::Uninitialized);
+ char *ptr = result.data();
+ encodeAt(ptr, a...);
+ return result;
+}
+
// Get the validation data from TinyCBOR (see src/3rdparty/tinycbor/tests/parser/data.cpp)
#include "data.cpp"
@@ -1882,6 +1946,51 @@ void tst_QCborValue::validation()
}
}
+void tst_QCborValue::extendedTypeValidation_data()
+{
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<QCborValue>("expected");
+
+ // QDateTime currently stores time in milliseconds, so make sure
+ // we don't overflow
+ {
+ quint64 limit = std::numeric_limits<quint64>::max() / 1000;
+ QTest::newRow("UnixTime_t:integer-overflow-positive")
+ << encode(0xc1, 0x1b, limit + 1)
+ << QCborValue(QCborKnownTags::UnixTime_t, qint64(limit) + 1);
+ QTest::newRow("UnixTime_t:integer-overflow-negative")
+ << encode(0xc1, 0x3b, limit)
+ << QCborValue(QCborKnownTags::UnixTime_t, -qint64(limit) - 1);
+
+ double fplimit = std::numeric_limits<qint64>::min() / (-1000.); // 2^63 ms
+ QTest::newRow("UnixTime_t:fp-overflow-positive")
+ << encode(0xc1, 0xfb, fplimit)
+ << QCborValue(QCborKnownTags::UnixTime_t, fplimit);
+ QTest::newRow("UnixTime_t:fp-overflow-negative")
+ << encode(0xc1, 0xfb, -fplimit)
+ << QCborValue(QCborKnownTags::UnixTime_t, -fplimit);
+ }
+}
+
+void tst_QCborValue::extendedTypeValidation()
+{
+ QFETCH(QByteArray, data);
+ QFETCH(QCborValue, expected);
+
+ QCborParserError error;
+ QCborValue decoded = QCborValue::fromCbor(data, &error);
+ QVERIFY2(error.error == QCborError(), qPrintable(error.errorString()));
+ QCOMPARE(error.offset, data.size());
+ QCOMPARE(decoded, expected);
+
+ QByteArray encoded = decoded.toCbor();
+#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
+ // behavior change, see qdatetime.cpp:fromIsoTimeString
+ QEXPECT_FAIL("DateTime:Null-at-19", "QDateTime parsing fixed, but only in 6.0", Abort);
+#endif
+ QCOMPARE(encoded, data);
+}
+
void tst_QCborValue::hugeDeviceValidation_data()
{
addValidationHugeDevice(MaxByteArraySize + 1, MaxStringSize + 1);