Gui: Fix stackoverflow when loading corrupted file

If an object has a link to itself it may cause a stackoverflow
in several cases:
* If the method claimChildren3D() returns a list containing the
  object of the view provider then Document::handleChildren3D()
  will add a SoGroup to itself as a child. This will result into
  a stackoverflow as soon as an action traverses the scene.
* If the method claimChildren() returns a list containing the
  object of the view provider then DocumentItem::createNewItem()
  causes an infinite loop with DocumentItem::populateItem()

Solution:
* Inside Document::handleChildren3D() avoid to add a SoGroup to itself
* In this specific case fix ViewProviderCoordinateSystem::claimChildren()
  to avoid a cyclic dependency

Hint: Since PR 18126 FreeCAD is vulnerable for this problem.

This fixes issue 19682
This commit is contained in:
wmayer
2025-02-20 22:33:04 +01:00
committed by Ladislav Michl
parent 59717fc607
commit efe02a8675
2 changed files with 42 additions and 14 deletions

View File

@@ -2719,21 +2719,42 @@ void Document::handleChildren3D(ViewProvider* viewProvider, bool deleting)
if(!deleting) {
for (const auto & it : children) {
ViewProvider* ChildViewProvider = getViewProvider(it);
if (ChildViewProvider) {
auto itOld = oldChildren.find(static_cast<ViewProviderDocumentObject*>(ChildViewProvider));
if (auto ChildViewProvider = dynamic_cast<ViewProviderDocumentObject*>(getViewProvider(it))) {
auto itOld = oldChildren.find(ChildViewProvider);
if(itOld!=oldChildren.end()) oldChildren.erase(itOld);
SoSeparator* childRootNode = ChildViewProvider->getRoot();
childGroup->addChild(childRootNode);
if (SoSeparator* childRootNode = ChildViewProvider->getRoot()) {
if (childRootNode == childGroup) {
Base::Console().warning("Document::handleChildren3D: Do not add "
"group of '%s' to itself\n",
it->getNameInDocument());
}
else if (childGroup) {
childGroup->addChild(childRootNode);
}
}
SoSeparator* childFrontNode = ChildViewProvider->getFrontRoot();
if (frontGroup && childFrontNode)
frontGroup->addChild(childFrontNode);
if (SoSeparator* childFrontNode = ChildViewProvider->getFrontRoot()) {
if (childFrontNode == frontGroup) {
Base::Console().warning("Document::handleChildren3D: Do not add "
"foreground group of '%s' to itself\n",
it->getNameInDocument());
}
else if (frontGroup) {
frontGroup->addChild(childFrontNode);
}
}
SoSeparator* childBackNode = ChildViewProvider->getBackRoot();
if (backGroup && childBackNode)
backGroup->addChild(childBackNode);
if (SoSeparator* childBackNode = ChildViewProvider->getBackRoot()) {
if (childBackNode == backGroup) {
Base::Console().warning("Document::handleChildren3D: Do not add "
"background group of '%s' to itself\n",
it->getNameInDocument());
}
else if (backGroup) {
backGroup->addChild(childBackNode);
}
}
// cycling to all views of the document to remove the viewprovider from the viewer itself
for (Gui::BaseView* vIt : d->baseViews) {

View File

@@ -72,11 +72,18 @@ ViewProviderCoordinateSystem::~ViewProviderCoordinateSystem() {
std::vector<App::DocumentObject*> ViewProviderCoordinateSystem::claimChildren() const
{
return static_cast<App::Origin*>( getObject() )->OriginFeatures.getValues();
auto obj = getObject<App::Origin>();
std::vector<App::DocumentObject*> childs = obj->OriginFeatures.getValues();
auto it = std::find(childs.begin(), childs.end(), obj);
if (it != childs.end()) {
childs.erase(it);
}
return childs;
}
std::vector<App::DocumentObject*> ViewProviderCoordinateSystem::claimChildren3D() const {
return claimChildren ();
std::vector<App::DocumentObject*> ViewProviderCoordinateSystem::claimChildren3D() const
{
return claimChildren();
}
void ViewProviderCoordinateSystem::attach(App::DocumentObject* pcObject)