From d90760f915ba7eda71a38245b1dc308c71724581 Mon Sep 17 00:00:00 2001
From: Matthias Puchner <github@mpuchner.de>
Date: Thu, 23 Dec 2021 14:38:05 +0100
Subject: [PATCH] stabilize XML parsing

---
 GUI/Model/Job/JobItem.cpp        | 12 ++++++----
 GUI/Model/Session/SessionXML.cpp | 40 +++++++++++++++++++++++++++++---
 GUI/Model/Session/SessionXML.h   |  1 +
 3 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/GUI/Model/Job/JobItem.cpp b/GUI/Model/Job/JobItem.cpp
index b57af9b6755..a86d913cd1c 100644
--- a/GUI/Model/Job/JobItem.cpp
+++ b/GUI/Model/Job/JobItem.cpp
@@ -408,13 +408,17 @@ void JobItem::readNonSessionItems(QXmlStreamReader* reader)
     while (reader->readNextStartElement()) {
         if (reader->name() == Tags::SimulationOptions) {
             m_simulationOptionsItem.readContentFrom(reader);
-            reader->skipCurrentElement();
-        } else if (reader->name() == Tags::Materials)
+            goToEndElementOfTag(reader, Tags::SimulationOptions);
+        } else if (reader->name() == Tags::Materials) {
             m_materials.readContentFrom(reader);
-        else if (reader->name() == Tags::ParameterContainer)
+            goToEndElementOfTag(reader, Tags::Materials);
+        } else if (reader->name() == Tags::ParameterContainer) {
             m_parameterContainer.readContentFrom(reader);
-        else if (reader->name() == Tags::Sample)
+            goToEndElementOfTag(reader, Tags::ParameterContainer);
+        } else if (reader->name() == Tags::Sample) {
             m_multiLayerItem.readContentFrom(reader);
+            goToEndElementOfTag(reader, Tags::Sample);
+        }
     }
 }
 
diff --git a/GUI/Model/Session/SessionXML.cpp b/GUI/Model/Session/SessionXML.cpp
index 50fdd049b14..3b19c2dc88e 100644
--- a/GUI/Model/Session/SessionXML.cpp
+++ b/GUI/Model/Session/SessionXML.cpp
@@ -26,6 +26,26 @@
 
 namespace {
 
+void assertCurrentTag(QXmlStreamReader* reader, const QString& expectedTag)
+{
+    ASSERT(reader);
+
+    if (reader->name() != expectedTag) {
+#ifdef _DEBUG
+        // to simplify debugging: what is the current tag
+        QString foundTag = reader->name().toString();
+        Q_UNUSED(foundTag);
+#endif
+        throw DeserializationException::streamError();
+    }
+}
+
+void assertCurrentToken(QXmlStreamReader* reader, QXmlStreamReader::TokenType token)
+{
+    if (reader->tokenType() != token)
+        throw DeserializationException::streamError();
+}
+
 namespace Tags {
 const QString Id("id");
 } // namespace Tags
@@ -58,6 +78,20 @@ SessionItem* createItem(SessionItem* parent, const QString& modelType, const QSt
 
 } // namespace
 
+void GUI::Session::XML::goToEndElementOfTag(QXmlStreamReader* reader, const QString& tag)
+{
+    if (reader->name() != tag) {
+        if (!reader->isEndElement())
+            reader->skipCurrentElement();
+        reader->skipCurrentElement();
+    }
+    assertCurrentTag(reader, tag);
+    if (!reader->isEndElement())
+        reader->skipCurrentElement();
+
+    assertCurrentToken(reader, QXmlStreamReader::EndElement);
+    assertCurrentTag(reader, tag);
+}
 
 void GUI::Session::XML::writeModel(QXmlStreamWriter* writer, SessionItem* modelRootItem)
 {
@@ -252,7 +286,7 @@ void GUI::Session::XML::readItems(QXmlStreamReader* reader, SessionItem* parent,
                 ASSERT(reader->name() == GUI::Session::XML::ItemTag);
             } else if (reader->name() == GUI::Session::XML::ParameterTag) {
                 GUI::Session::XML::readProperty(reader, parent, messageService);
-                ASSERT(reader->name() == GUI::Session::XML::ParameterTag);
+                goToEndElementOfTag(reader, GUI::Session::XML::ParameterTag);
             } else if (reader->name() == GUI::Session::XML::BinaryData) {
                 if (reader->attributes().value(GUI::Session::XML::Version).toInt() == 1) {
                     QString valueAsBase64 =
@@ -262,10 +296,10 @@ void GUI::Session::XML::readItems(QXmlStreamReader* reader, SessionItem* parent,
                     parent->deserializeBinaryData(data);
                 } else
                     throw DeserializationException::tooNew();
-                ASSERT(reader->name() == GUI::Session::XML::BinaryData);
+                goToEndElementOfTag(reader, GUI::Session::XML::BinaryData);
             } else if (reader->name() == GUI::Session::XML::NonSessionItemData) {
                 parent->readNonSessionItems(reader);
-                ASSERT(reader->name() == GUI::Session::XML::NonSessionItemData);
+                goToEndElementOfTag(reader, GUI::Session::XML::NonSessionItemData);
             }
         } else if (reader->isEndElement()) {
             if (reader->name() == GUI::Session::XML::ItemTag && parent)
diff --git a/GUI/Model/Session/SessionXML.h b/GUI/Model/Session/SessionXML.h
index 10e7da0a550..f7fefe608a8 100644
--- a/GUI/Model/Session/SessionXML.h
+++ b/GUI/Model/Session/SessionXML.h
@@ -76,6 +76,7 @@ void writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, doub
 void writeAttribute(QXmlStreamWriter* writer, const QString& attributeBaseName, const R3& vec);
 void writeAttribute(QXmlStreamWriter* writer, const QString& attributeBaseName, const complex_t& c);
 
+void goToEndElementOfTag(QXmlStreamReader* reader, const QString& tag);
 void readItems(QXmlStreamReader* reader, SessionItem* parent, QString topTag = "",
                MessageService* messageService = nullptr);
 QString readProperty(QXmlStreamReader* reader, SessionItem* item,
-- 
GitLab