From a405b4dae05fc55d784cc485723f278b83ea64ee Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 11 Jun 2017 16:41:52 +0200 Subject: [PATCH] issue #0003070: VRML 2 crashes with corner cases --- src/Gui/ViewProvider.cpp | 62 ++++++++++ src/Gui/ViewProvider.h | 4 + src/Gui/ViewProviderVRMLObject.cpp | 189 +++++++++++++++-------------- 3 files changed, 163 insertions(+), 92 deletions(-) diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index 46006e86c3..0859caaafb 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -433,6 +433,68 @@ PyObject* ViewProvider::getPyObject() return pyViewObject; } +#include + +namespace Gui { +typedef boost::adjacency_list < + boost::vecS, // class OutEdgeListS : a Sequence or an AssociativeContainer + boost::vecS, // class VertexListS : a Sequence or a RandomAccessContainer + boost::directedS, // class DirectedS : This is a directed graph + boost::no_property, // class VertexProperty: + boost::no_property, // class EdgeProperty: + boost::no_property, // class GraphProperty: + boost::listS // class EdgeListS: +> Graph; +typedef boost::graph_traits::vertex_descriptor Vertex; +typedef boost::graph_traits::edge_descriptor Edge; + +void addNodes(Graph& graph, std::map& vertexNodeMap, SoNode* node) +{ + if (node->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) { + SoGroup* group = static_cast(node); + Vertex groupV = vertexNodeMap[group]; + + for (int i=0; igetNumChildren(); i++) { + SoNode* child = group->getChild(i); + auto it = vertexNodeMap.find(child); + + // the child node is not yet added to the map + if (it == vertexNodeMap.end()) { + Vertex childV = add_vertex(graph); + vertexNodeMap[child] = childV; + add_edge(groupV, childV, graph); + addNodes(graph, vertexNodeMap, child); + } + // the child is already there, only add the edge then + else { + add_edge(groupV, it->second, graph); + } + } + } +} +} + +bool ViewProvider::checkRecursion(SoNode* node) +{ + if (node->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) { + std::list make_order; + Graph graph; + std::map vertexNodeMap; + Vertex groupV = add_vertex(graph); + vertexNodeMap[node] = groupV; + addNodes(graph, vertexNodeMap, node); + + try { + boost::topological_sort(graph, std::front_inserter(make_order)); + } + catch (const std::exception&) { + return false; + } + } + + return true; +} + SoPickedPoint* ViewProvider::getPointOnRay(const SbVec2s& pos, const View3DInventorViewer* viewer) const { //first get the path to this node and calculate the current transformation diff --git a/src/Gui/ViewProvider.h b/src/Gui/ViewProvider.h index 7cc0f886e4..3fe344a059 100644 --- a/src/Gui/ViewProvider.h +++ b/src/Gui/ViewProvider.h @@ -333,6 +333,10 @@ public: //@} protected: + /** Helper method to check that the node is valid, i.e. it must not cause + * and infinite recursion. + */ + bool checkRecursion(SoNode*); /** Helper method to get picked entities while editing. * It's in the responsibility of the caller to delete the returned instance. */ diff --git a/src/Gui/ViewProviderVRMLObject.cpp b/src/Gui/ViewProviderVRMLObject.cpp index 9242ecadda..f54f8cc4bf 100644 --- a/src/Gui/ViewProviderVRMLObject.cpp +++ b/src/Gui/ViewProviderVRMLObject.cpp @@ -48,6 +48,7 @@ #include "SoFCSelection.h" #include #include +#include #include #include #include @@ -96,21 +97,21 @@ std::vector ViewProviderVRMLObject::getDisplayModes(void) const template void ViewProviderVRMLObject::getResourceFile(SoNode* node, std::list& resources) { - SoSearchAction sa; - sa.setType(T::getClassTypeId()); - sa.setInterest(SoSearchAction::ALL); - sa.setSearchingAll(true); - sa.apply(node); - const SoPathList & pathlist = sa.getPaths(); - for (int i = 0; i < pathlist.getLength(); i++ ) { - SoFullPath * path = static_cast(pathlist[i]); - if (path->getTail()->isOfType(T::getClassTypeId())) { - T * tex = static_cast(path->getTail()); - for (int j = 0; j < tex->url.getNum(); j++) { - this->addResource(tex->url[j], resources); - } - } - } + SoSearchAction sa; + sa.setType(T::getClassTypeId()); + sa.setInterest(SoSearchAction::ALL); + sa.setSearchingAll(true); + sa.apply(node); + const SoPathList & pathlist = sa.getPaths(); + for (int i = 0; i < pathlist.getLength(); i++ ) { + SoFullPath * path = static_cast(pathlist[i]); + if (path->getTail()->isOfType(T::getClassTypeId())) { + T * tex = static_cast(path->getTail()); + for (int j = 0; j < tex->url.getNum(); j++) { + this->addResource(tex->url[j], resources); + } + } + } } namespace Gui { @@ -118,92 +119,92 @@ namespace Gui { template<> void ViewProviderVRMLObject::getResourceFile(SoNode* node, std::list& resources) { - SoSearchAction sa; - sa.setType(SoVRMLBackground::getClassTypeId()); - sa.setInterest(SoSearchAction::ALL); - sa.setSearchingAll(true); - sa.apply(node); - const SoPathList & pathlist = sa.getPaths(); - for (int i = 0; i < pathlist.getLength(); i++ ) { - SoFullPath * path = static_cast(pathlist[i]); - if (path->getTail()->isOfType(SoVRMLBackground::getClassTypeId())) { - SoVRMLBackground * vrml = static_cast(path->getTail()); - // backUrl - for (int j = 0; j < vrml->backUrl.getNum(); j++) { - addResource(vrml->backUrl[j], resources); - } - // bottomUrl - for (int j = 0; j < vrml->bottomUrl.getNum(); j++) { - addResource(vrml->bottomUrl[j], resources); - } - // frontUrl - for (int j = 0; j < vrml->frontUrl.getNum(); j++) { - addResource(vrml->frontUrl[j], resources); - } - // leftUrl - for (int j = 0; j < vrml->leftUrl.getNum(); j++) { - addResource(vrml->leftUrl[j], resources); - } - // rightUrl - for (int j = 0; j < vrml->rightUrl.getNum(); j++) { - addResource(vrml->rightUrl[j], resources); - } - // topUrl - for (int j = 0; j < vrml->topUrl.getNum(); j++) { - addResource(vrml->topUrl[j], resources); - } - } - } + SoSearchAction sa; + sa.setType(SoVRMLBackground::getClassTypeId()); + sa.setInterest(SoSearchAction::ALL); + sa.setSearchingAll(true); + sa.apply(node); + const SoPathList & pathlist = sa.getPaths(); + for (int i = 0; i < pathlist.getLength(); i++ ) { + SoFullPath * path = static_cast(pathlist[i]); + if (path->getTail()->isOfType(SoVRMLBackground::getClassTypeId())) { + SoVRMLBackground * vrml = static_cast(path->getTail()); + // backUrl + for (int j = 0; j < vrml->backUrl.getNum(); j++) { + addResource(vrml->backUrl[j], resources); + } + // bottomUrl + for (int j = 0; j < vrml->bottomUrl.getNum(); j++) { + addResource(vrml->bottomUrl[j], resources); + } + // frontUrl + for (int j = 0; j < vrml->frontUrl.getNum(); j++) { + addResource(vrml->frontUrl[j], resources); + } + // leftUrl + for (int j = 0; j < vrml->leftUrl.getNum(); j++) { + addResource(vrml->leftUrl[j], resources); + } + // rightUrl + for (int j = 0; j < vrml->rightUrl.getNum(); j++) { + addResource(vrml->rightUrl[j], resources); + } + // topUrl + for (int j = 0; j < vrml->topUrl.getNum(); j++) { + addResource(vrml->topUrl[j], resources); + } + } + } } } void ViewProviderVRMLObject::addResource(const SbString& url, std::list& resources) { - SbString found = SoInput::searchForFile(url, SoInput::getDirectories(), SbStringList()); - Base::FileInfo fi(found.getString()); - if (fi.exists()) { - // add the resource file if not yet listed - if (std::find(resources.begin(), resources.end(), found.getString()) == resources.end()) { - resources.push_back(found.getString()); - } - } + SbString found = SoInput::searchForFile(url, SoInput::getDirectories(), SbStringList()); + Base::FileInfo fi(found.getString()); + if (fi.exists()) { + // add the resource file if not yet listed + if (std::find(resources.begin(), resources.end(), found.getString()) == resources.end()) { + resources.push_back(found.getString()); + } + } } void ViewProviderVRMLObject::getLocalResources(SoNode* node, std::list& resources) { // search for SoVRMLInline files SoSearchAction sa; - sa.setType(SoVRMLInline::getClassTypeId()); - sa.setInterest(SoSearchAction::ALL); - sa.setSearchingAll(true); - sa.apply(node); - - const SoPathList & pathlist = sa.getPaths(); - for (int i = 0; i < pathlist.getLength(); i++ ) { - SoPath * path = pathlist[i]; - SoVRMLInline * vrml = static_cast(path->getTail()); - const SbString& url = vrml->getFullURLName(); - if (url.getLength() > 0) { - // add the resource file if not yet listed - if (std::find(resources.begin(), resources.end(), url.getString()) == resources.end()) { - resources.push_back(url.getString()); - } - - // if the resource file could be loaded check if it references further resources - if (vrml->getChildData()) { - getLocalResources(vrml->getChildData(), resources); - } - } - } - - // search for SoVRMLImageTexture, ... files - getResourceFile(node, resources); - getResourceFile(node, resources); - getResourceFile(node, resources); - getResourceFile(node, resources); - getResourceFile(node, resources); - getResourceFile(node, resources); + sa.setType(SoVRMLInline::getClassTypeId()); + sa.setInterest(SoSearchAction::ALL); + sa.setSearchingAll(true); + sa.apply(node); + + const SoPathList & pathlist = sa.getPaths(); + for (int i = 0; i < pathlist.getLength(); i++ ) { + SoPath * path = pathlist[i]; + SoVRMLInline * vrml = static_cast(path->getTail()); + const SbString& url = vrml->getFullURLName(); + if (url.getLength() > 0) { + // add the resource file if not yet listed + if (std::find(resources.begin(), resources.end(), url.getString()) == resources.end()) { + resources.push_back(url.getString()); + } + + // if the resource file could be loaded check if it references further resources + if (vrml->getChildData()) { + getLocalResources(vrml->getChildData(), resources); + } + } + } + + // search for SoVRMLImageTexture, ... files + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); + getResourceFile(node, resources); } void ViewProviderVRMLObject::updateData(const App::Property* prop) @@ -231,15 +232,19 @@ void ViewProviderVRMLObject::updateData(const App::Property* prop) SoSeparator * node = SoDB::readAll(&in); if (node) { + if (!checkRecursion(node)) { + Base::Console().Error("The VRML file causes an infinite recursion!\n"); + return; + } pcVRML->addChild(node); std::list urls; getLocalResources(node, urls); - if (!urls.empty() && ivObj->Urls.getSize() == 0) { + if (!urls.empty() && ivObj->Urls.getSize() == 0) { std::vector res; res.insert(res.end(), urls.begin(), urls.end()); - ivObj->Urls.setValues(res); - } + ivObj->Urls.setValues(res); + } } SoInput::removeDirectory(filepath.constData()); SoInput::removeDirectory(subpath.constData());