瀏覽代碼

Add deserialization issue detection

- Replace QByteArray::const_iterator by iterator with internal range check
- Implement and update tests
Alexey Edelev 5 年之前
父節點
當前提交
b3e0874d26

+ 3 - 3
src/protobuf/qprotobufobject_p.cpp

@@ -47,7 +47,7 @@ void ProtobufObjectPrivate::registerSerializers()
 
     wrapSerializer<QString>([](const QString &data, int &/*fieldIndex*/) {
         return serializeLengthDelimited(data);
-    }, [](QByteArray::const_iterator &it){
+    }, [](SelfcheckIterator &it){
         return QVariant::fromValue(QString::fromUtf8(deserializeLengthDelimited(it)));
     }, LengthDelimited);
 
@@ -103,7 +103,7 @@ QByteArray ProtobufObjectPrivate::serializeUserType(const QVariant &propertyValu
     return serializer.serializer(propertyValue, fieldIndex);
 }
 
-void ProtobufObjectPrivate::deserializeProperty(QObject *object, WireTypes wireType, const QMetaProperty &metaProperty, QByteArray::const_iterator &it)
+void ProtobufObjectPrivate::deserializeProperty(QObject *object, WireTypes wireType, const QMetaProperty &metaProperty, SelfcheckIterator &it)
 {
     qProtoDebug() << __func__ << " wireType: " << wireType << " metaProperty: " << metaProperty.typeName()
                   << "currentByte:" << QString::number((*it), 16);
@@ -135,7 +135,7 @@ void ProtobufObjectPrivate::deserializeProperty(QObject *object, WireTypes wireT
     metaProperty.write(object, newPropertyValue);
 }
 
-void ProtobufObjectPrivate::deserializeUserType(const QMetaProperty &metaType, QByteArray::const_iterator& it, QVariant &newValue)
+void ProtobufObjectPrivate::deserializeUserType(const QMetaProperty &metaType, SelfcheckIterator& it, QVariant &newValue)
 {
     qProtoDebug() << __func__ << "userType" << metaType.userType() << "typeName" << metaType.typeName()
                   << "currentByte:" << QString::number((*it), 16);

+ 39 - 37
src/protobuf/qprotobufobject_p.h

@@ -37,6 +37,7 @@
 
 #include "qtprotobuftypes.h"
 #include "qtprotobuflogging.h"
+#include "selfcheckiterator.h"
 
 #define ASSERT_FIELD_NUMBER(X) Q_ASSERT_X(X < 128 && X > 0, T::staticMetaObject.className(), "fieldIndex is out of range")
 
@@ -55,7 +56,7 @@ class ProtobufObjectPrivate
 {
 public:
     using Serializer = std::function<QByteArray(const QVariant &, int &)>;
-    using Deserializer = std::function<void(QByteArray::const_iterator &, QVariant &)>;
+    using Deserializer = std::function<void(SelfcheckIterator &, QVariant &)>;
     struct SerializationHandlers {
         Serializer serializer;
         Deserializer deserializer;
@@ -70,13 +71,13 @@ public:
 
     template <typename T,
               typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
-    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<QVariant(QByteArray::const_iterator &)> d, WireTypes type)
+    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<QVariant(SelfcheckIterator &)> d, WireTypes type)
     {
         serializers[qMetaTypeId<T>()] = {
             [s](const QVariant &value, int &fieldIndex) {
                 return s(value.value<T>(), fieldIndex);
             },
-            [d](QByteArray::const_iterator &it, QVariant & value){
+            [d](SelfcheckIterator &it, QVariant & value){
                 value = d(it);
             },
             type
@@ -85,7 +86,7 @@ public:
 
     template <typename T,
               typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
-    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<void(QByteArray::const_iterator &it, QVariant & value)> d, WireTypes type)
+    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<void(SelfcheckIterator &it, QVariant & value)> d, WireTypes type)
     {
         serializers[qMetaTypeId<T>()] = {
             [s](const QVariant &value, int &fieldIndex) {
@@ -98,13 +99,13 @@ public:
 
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<QVariant(QByteArray::const_iterator &)> d, WireTypes type)
+    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<QVariant(SelfcheckIterator &)> d, WireTypes type)
     {
         serializers[qMetaTypeId<T*>()] = {
             [s](const QVariant &value, int &fieldIndex) {
                 return s(*(value.value<T*>()), fieldIndex);
             },
-            [d](QByteArray::const_iterator &it, QVariant &value){
+            [d](SelfcheckIterator &it, QVariant &value){
                 value = d(it);
             },
             type
@@ -113,7 +114,7 @@ public:
 
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<void(QByteArray::const_iterator &it, QVariant &value)> d, WireTypes type)
+    static void wrapSerializer(std::function<QByteArray(const T &, int &)> s, std::function<void(SelfcheckIterator &it, QVariant &value)> d, WireTypes type)
     {
         serializers[qMetaTypeId<T*>()] = {
             [s](const QVariant &value, int &fieldIndex) {
@@ -130,8 +131,8 @@ public:
     static QByteArray serializeValue(const QVariant &propertyValue, int fieldIndex, const QMetaProperty &metaProperty);
     static QByteArray serializeUserType(const QVariant &propertyValue, int &fieldIndex, WireTypes &type);
 
-    static void deserializeProperty(QObject *object, WireTypes wireType, const QMetaProperty &metaProperty, QByteArray::const_iterator &it);
-    static void deserializeUserType(const QMetaProperty &metaType, QByteArray::const_iterator &it, QVariant &newValue);
+    static void deserializeProperty(QObject *object, WireTypes wireType, const QMetaProperty &metaProperty, SelfcheckIterator &it);
+    static void deserializeUserType(const QMetaProperty &metaType, SelfcheckIterator &it, QVariant &newValue);
 
     //###########################################################################
     //                           Serialization helpers
@@ -373,7 +374,7 @@ public:
                                         || std::is_same<V, sfixed64>::value
                                         || std::is_same<V, fixed32>::value
                                         || std::is_same<V, fixed64>::value, int> = 0>
-    static uint32 getRepeatedFieldCount(QByteArray::const_iterator &it) {
+    static uint32 getRepeatedFieldCount(SelfcheckIterator &it) {
         return deserializeBasic<uint32>(it).value<uint32>() / sizeof(V);
     }
 
@@ -381,7 +382,7 @@ public:
               typename std::enable_if_t<std::is_integral<V>::value
                                         || std::is_same<V, sint32>::value
                                         || std::is_same<V, sint64>::value, int> = 0>
-    static uint32 getRepeatedFieldCount(QByteArray::const_iterator &it) {
+    static uint32 getRepeatedFieldCount(SelfcheckIterator &it) {
         return deserializeBasic<uint32>(it).value<uint32>();
     }
 
@@ -392,10 +393,10 @@ public:
                                         || std::is_same<V, fixed64>::value
                                         || std::is_same<V, sfixed32>::value
                                         || std::is_same<V, sfixed64>::value, int> = 0>
-    static QVariant deserializeBasic(QByteArray::const_iterator &it) {
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
-        QVariant newPropertyValue(QVariant::fromValue(*(V*)it));
+        QVariant newPropertyValue(QVariant::fromValue(*(V*)((QByteArray::const_iterator&)it)));
         it += sizeof(V);
         return newPropertyValue;
     }
@@ -403,7 +404,7 @@ public:
     template <typename V,
               typename std::enable_if_t<std::is_integral<V>::value
                                         && std::is_unsigned<V>::value, int> = 0>
-    static QVariant deserializeBasic(QByteArray::const_iterator &it) {
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
         return QVariant::fromValue(deserializeVarintCommon<V>(it));
@@ -412,7 +413,7 @@ public:
     template <typename V,
               typename std::enable_if_t<std::is_integral<V>::value
                                         && std::is_signed<V>::value,int> = 0>
-    static QVariant deserializeBasic(QByteArray::const_iterator &it) {
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
         using  UV = typename qtprotobuf::make_unsigned<V>::type;
         UV unsignedValue = deserializeVarintCommon<UV>(it);
@@ -423,7 +424,7 @@ public:
     template <typename V,
               typename std::enable_if_t<std::is_same<int32, V>::value
                                         || std::is_same<int64, V>::value, int> = 0>
-    static QVariant deserializeBasic(QByteArray::const_iterator &it) {
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
         using  UV = typename qtprotobuf::make_unsigned<V>::type;
         UV unsignedValue = deserializeVarintCommon<UV>(it);
@@ -434,7 +435,7 @@ public:
     template <typename V,
               typename std::enable_if_t<std::is_integral<V>::value
                                         && std::is_unsigned<V>::value, int> = 0>
-    static V deserializeVarintCommon(QByteArray::const_iterator &it) {
+    static V deserializeVarintCommon(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
         V value = 0;
@@ -453,11 +454,11 @@ public:
     }
 
     //---------------Deserialize length delimited bytes and strings--------------
-    static QByteArray deserializeLengthDelimited(QByteArray::const_iterator &it) {
+    static QByteArray deserializeLengthDelimited(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
         unsigned int length = deserializeBasic<uint32>(it).toUInt();
-        QByteArray result(it, length);
+        QByteArray result((QByteArray::const_iterator&)it, length);
         it += length;
         return result;
     }
@@ -466,7 +467,7 @@ public:
     template <typename V,
               typename std::enable_if_t<std::is_same<V, QString>::value
                                         || std::is_same<V, QByteArray>::value, int> = 0>
-    static QVariant deserializeList(QByteArray::const_iterator &it) {
+    static QVariant deserializeList(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
         return QVariant::fromValue(deserializeLengthDelimited(it));
@@ -474,7 +475,7 @@ public:
 
     template <typename V,
               typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static QVariant deserializeList(QByteArray::const_iterator &it) {
+    static QVariant deserializeList(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
         QVariant newValue;
         serializers.at(qMetaTypeId<V*>()).deserializer(it, newValue);//Throws exception if not found
@@ -485,12 +486,12 @@ public:
               typename std::enable_if_t<!(std::is_same<V, QString>::value
                                         || std::is_same<V, QByteArray>::value
                                         || std::is_base_of<QObject, V>::value), int> = 0>
-    static QVariant deserializeList(QByteArray::const_iterator &it) {
+    static QVariant deserializeList(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
         QList<V> out;
         unsigned int count = deserializeBasic<uint32>(it).toUInt();
-        QByteArray::const_iterator lastVarint = it + count;
+        SelfcheckIterator lastVarint = it + count;
         while (it != lastVarint) {
             QVariant variant = deserializeBasic<V>(it);
             out.append(variant.value<V>());
@@ -501,7 +502,7 @@ public:
     //-----------------------Deserialize maps of any type------------------------
     template <typename K, typename V,
               typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
-    static void deserializeMap(QByteArray::const_iterator &it, QVariant &previous) {
+    static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
         QMap<K,V> out = previous.value<QMap<K,V>>();
 
@@ -513,7 +514,7 @@ public:
 
         unsigned int count = deserializeBasic<uint32>(it).toUInt();
         qProtoDebug() << __func__ << "count:" << count;
-        QByteArray::const_iterator last = it + count;
+        SelfcheckIterator last = it + count;
         while (it != last) {
             decodeHeaderByte(*it, mapIndex, type);
             ++it;
@@ -530,7 +531,7 @@ public:
 
     template <typename K, typename V,
               typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static void deserializeMap(QByteArray::const_iterator &it, QVariant &previous) {
+    static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
         auto out = previous.value<QMap<K, QSharedPointer<V>>>();
 
@@ -542,7 +543,7 @@ public:
 
         unsigned int count = deserializeBasic<uint32>(it).toUInt();
         qProtoDebug() << __func__ << "count:" << count;
-        QByteArray::const_iterator last = it + count;
+        SelfcheckIterator last = it + count;
         while (it != last) {
             decodeHeaderByte(*it, mapIndex, type);
             ++it;
@@ -559,7 +560,7 @@ public:
 
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static T *deserializeMapHelper(QByteArray::const_iterator &it) {
+    static T *deserializeMapHelper(SelfcheckIterator &it) {
         auto serializer = serializers.at(qMetaTypeId<T *>());//Throws exception if not found
         QVariant value;
         serializer.deserializer(it, value);
@@ -568,7 +569,7 @@ public:
 
     template <typename T,
               typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
-    static T deserializeMapHelper(QByteArray::const_iterator &it) {
+    static T deserializeMapHelper(SelfcheckIterator &it) {
         auto serializer = serializers.at(qMetaTypeId<T>());//Throws exception if not found
         QVariant value;
         serializer.deserializer(it, value);
@@ -591,7 +592,7 @@ public:
 
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static QVariant deserializeComplexType(QByteArray::const_iterator &it) {
+    static QVariant deserializeComplexType(SelfcheckIterator &it) {
         T *value = new T;
         value->deserialize(ProtobufObjectPrivate::deserializeLengthDelimited(it));
         return QVariant::fromValue<T*>(value);
@@ -606,7 +607,7 @@ public:
 
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static void deserializeComplexListType(QByteArray::const_iterator &it, QVariant &previous) {
+    static void deserializeComplexListType(SelfcheckIterator &it, QVariant &previous) {
         QList<QSharedPointer<T>> previousList = previous.value<QList<QSharedPointer<T>>>();
         QVariant newMember = ProtobufObjectPrivate::deserializeList<T>(it);
         previousList.append(QSharedPointer<T>(newMember.value<T*>()));
@@ -635,23 +636,24 @@ public:
     static void deserialize(QObject* object, const QByteArray &array) {
         qProtoDebug() << T::staticMetaObject.className() << "deserialize";
 
-        for (QByteArray::const_iterator it = array.begin(); it != array.end();) {
+        for (SelfcheckIterator it(array); it != array.end();) {
             //Each iteration we expect iterator is setup to beginning of next chunk
             int fieldNumber = NotUsedFieldIndex;
             WireTypes wireType = UnknownWireType;
             if (!ProtobufObjectPrivate::decodeHeaderByte(*it, fieldNumber, wireType)) {
                 qProtoCritical() << "Message received doesn't contains valid header byte. "
-                               "Trying next, but seems stream is broken" << QString::number((*it), 16);
-                ++it;
-                continue;
+                                    "Trying next, but seems stream is broken" << QString::number((*it), 16);
+                throw std::invalid_argument("Message received doesn't contains valid header byte. "
+                                      "Seems stream is broken");
             }
 
             auto propertyNumberIt = T::propertyOrdering.find(fieldNumber);
             if (propertyNumberIt == std::end(T::propertyOrdering)) {
-                ++it;
                 qProtoCritical() << "Message received contains invalid field number. "
                                "Trying next, but seems stream is broken";
-                continue;
+                throw std::invalid_argument("Message received contains invalid field number. "
+                                            "Seems stream is broken.\n"
+                                            "QtProtobuf doesn't support extra data for optional fields.");
             }
 
             int propertyIndex = propertyNumberIt->second;

+ 124 - 0
src/protobuf/selfcheckiterator.h

@@ -0,0 +1,124 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <QByteArray>
+#include <stdexcept>
+
+#pragma once
+
+namespace qtprotobuf {
+
+class SelfcheckIterator {
+public:
+    SelfcheckIterator(const QByteArray& container) : m_sizeLeft(container.size())
+      , m_containerSize(container.size())
+      , m_it(container.begin()){}
+
+    SelfcheckIterator(const SelfcheckIterator& other) : m_sizeLeft(other.m_sizeLeft)
+      ,m_containerSize(other.m_containerSize)
+      , m_it(other.m_it){}
+
+    explicit operator QByteArray::const_iterator&() { return m_it; }
+    explicit operator QByteArray::const_iterator() const { return m_it; }
+
+    char operator *() { return *m_it; }
+
+    SelfcheckIterator& operator ++() {
+        --m_sizeLeft;
+        if (m_sizeLeft < 0) {
+            throw std::out_of_range("Container is less than required fields number. Deserialization failed");
+        }
+        ++m_it;
+        return *this;
+    }
+
+    SelfcheckIterator& operator --() {
+        ++m_sizeLeft;
+        if (m_sizeLeft >= m_containerSize) {
+            throw std::out_of_range("Container is less than required fields number. Deserialization failed");
+        }
+        --m_it;
+        return *this;
+    }
+
+    SelfcheckIterator& operator +=(int count) {
+        m_sizeLeft -= count;
+        if (m_sizeLeft < 0) {
+            throw std::out_of_range("Container is less than required fields number. Deserialization failed");
+        }
+        m_it += count;
+        return *this;
+    }
+
+    SelfcheckIterator& operator -=(int count) {
+        m_sizeLeft += count;
+        if (m_sizeLeft >= m_containerSize) {
+            throw std::out_of_range("Container is less than required fields number. Deserialization failed");
+        }
+        m_it -= count;
+        return *this;
+    }
+
+    SelfcheckIterator& operator =(SelfcheckIterator& other) {
+        if (this == &other) {
+            return *this;
+        }
+
+        m_containerSize = other.m_containerSize;
+        m_sizeLeft = other.m_sizeLeft;
+        if (m_sizeLeft >= m_containerSize) {
+            throw std::out_of_range("Container is less than required fields number. Deserialization failed");
+        }
+        m_it = other.m_it;
+        return *this;
+    }
+
+    bool operator ==(const SelfcheckIterator& other) const {
+        return other.m_it == m_it;
+    }
+
+    bool operator !=(const SelfcheckIterator& other) const {
+        return other.m_it != m_it;
+    }
+
+    bool operator ==(const QByteArray::const_iterator& other) const {
+        return other == m_it;
+    }
+
+    bool operator !=(const QByteArray::const_iterator& other) const {
+        return other != m_it;
+    }
+private:
+    int m_sizeLeft;
+    int m_containerSize;
+    QByteArray::const_iterator m_it;
+};
+
+inline SelfcheckIterator operator +(const SelfcheckIterator& it, int lenght) {
+    SelfcheckIterator itNew = it;
+    return itNew += lenght;
+}
+
+}

+ 34 - 0
tests/test_protobuf/deserializationtest.cpp

@@ -833,3 +833,37 @@ TEST_F(DeserializationTest, SimpleStringComplexMapDeserializeTest)
     ASSERT_TRUE(*test.mapField()["where is my car dude?"] == ComplexMessage({10 , {"fourty two ten sixteen"}}));
     ASSERT_TRUE(*test.mapField()["WUT??"] == ComplexMessage({10 , {"?WUT?"}}));
 }
+
+TEST_F(DeserializationTest, SimpleUInt64ComplexMapInvalidLengthDeserializeTest)
+{
+    SimpleUInt64ComplexMessageMapMessage test;
+    EXPECT_THROW(test.deserialize(QByteArray::fromHex("3214080a1210120c320a74656e20656c6576656e080b3220082a121c12183216666f757274792074776f2074656e207369787465656e080a321008938004120a120")),
+                 std::invalid_argument);
+    ASSERT_TRUE(test.mapField().isEmpty());
+}
+
+TEST_F(DeserializationTest, SimpleStringComplexMapInvalidLengthDeserializeTest)
+{
+    SimpleStringComplexMessageMapMessage test;
+    EXPECT_THROW(test.deserialize(QByteArray::fromHex("6a140a055755543f3f120b120732053f5755543f080a6a170a0362656e1210120c320a74656e20656c6576656e080b6a350a157768657265206973206d792063617220647564653f121c12183216666f757274792074776f2074656e20736978746565")),
+                 std::out_of_range);
+    ASSERT_TRUE(*test.mapField()["ben"] == ComplexMessage({11 , {"ten eleven"}}));
+    ASSERT_TRUE(*test.mapField()["WUT??"] == ComplexMessage({10 , {"?WUT?"}}));
+    ASSERT_FALSE(test.mapField().contains("where is my car dude?"));
+}
+
+TEST_F(DeserializationTest, SimpleUInt64ComplexMapCorruptedDeserializeTest)
+{
+    SimpleUInt64ComplexMessageMapMessage test;
+    EXPECT_THROW(test.deserialize(QByteArray::fromHex("3214080a1210120c320a74656e20656c6576656e080b3221233522345b2183216666f757274792074776f2074656e207369787465656e080a321008938004120a120632045755543f080a")),
+                 std::invalid_argument);
+    ASSERT_TRUE(test.mapField().isEmpty());
+}
+
+TEST_F(DeserializationTest, SimpleStringComplexMapInvalidFieldDeserializeTest)
+{
+    SimpleStringComplexMessageMapMessage test;
+    EXPECT_THROW(test.deserialize(QByteArray::fromHex("6a460a055755543f3f120b120732053f5755543f080a6a170a0362656e1210120c320a74656e20656c6576656e080b6a350a157768657265206973206d792063617220647564653f121c12183216666f757274792074776f2074656e207369787465656e080a")),
+                 std::invalid_argument);
+    ASSERT_TRUE(test.mapField().isEmpty());
+}