1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-24 02:10:39 +00:00

Merge branch 'limit_layout_depth' into 'master'

Limit maximum Lua UI layout depth to prevent stack overflow

Closes #6733

See merge request OpenMW/openmw!1841
This commit is contained in:
Petr Mikheev 2022-05-19 16:56:59 +00:00
commit b832ba5c83

View file

@ -7,6 +7,8 @@
#include "widget.hpp" #include "widget.hpp"
namespace LuaUi namespace LuaUi
{
namespace
{ {
namespace LayoutKeys namespace LayoutKeys
{ {
@ -22,6 +24,8 @@ namespace LuaUi
const std::string defaultWidgetType = "LuaWidget"; const std::string defaultWidgetType = "LuaWidget";
constexpr uint64_t maxDepth = 250;
std::string widgetType(const sol::table& layout) std::string widgetType(const sol::table& layout)
{ {
sol::object typeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::type); sol::object typeField = LuaUtil::getFieldOrNil(layout, LayoutKeys::type);
@ -44,12 +48,13 @@ namespace LuaUi
MyGUI::Gui::getInstancePtr()->destroyWidget(ext->widget()); MyGUI::Gui::getInstancePtr()->destroyWidget(ext->widget());
} }
WidgetExtension* createWidget(const sol::table& layout); WidgetExtension* createWidget(const sol::table& layout, uint64_t depth);
void updateWidget(WidgetExtension* ext, const sol::table& layout); void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth);
std::vector<WidgetExtension*> updateContent( std::vector<WidgetExtension*> updateContent(
const std::vector<WidgetExtension*>& children, const sol::object& contentObj) const std::vector<WidgetExtension*>& children, const sol::object& contentObj, uint64_t depth)
{ {
++depth;
std::vector<WidgetExtension*> result; std::vector<WidgetExtension*> result;
if (contentObj == sol::nil) if (contentObj == sol::nil)
{ {
@ -68,28 +73,29 @@ namespace LuaUi
sol::table newLayout = content.at(i); sol::table newLayout = content.at(i);
if (ext->widget()->getTypeName() == widgetType(newLayout)) if (ext->widget()->getTypeName() == widgetType(newLayout))
{ {
updateWidget(ext, newLayout); updateWidget(ext, newLayout, depth);
} }
else else
{ {
destroyWidget(ext); destroyWidget(ext);
ext = createWidget(newLayout); ext = createWidget(newLayout, depth);
} }
result[i] = ext; result[i] = ext;
} }
for (size_t i = minSize; i < children.size(); i++) for (size_t i = minSize; i < children.size(); i++)
destroyWidget(children[i]); destroyWidget(children[i]);
for (size_t i = minSize; i < content.size(); i++) for (size_t i = minSize; i < content.size(); i++)
result[i] = createWidget(content.at(i)); result[i] = createWidget(content.at(i), depth);
return result; return result;
} }
void setTemplate(WidgetExtension* ext, const sol::object& templateLayout) void setTemplate(WidgetExtension* ext, const sol::object& templateLayout, uint64_t depth)
{ {
++depth;
sol::object props = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::props); sol::object props = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::props);
ext->setTemplateProperties(props); ext->setTemplateProperties(props);
sol::object content = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::content); sol::object content = LuaUtil::getFieldOrNil(templateLayout, LayoutKeys::content);
ext->setTemplateChildren(updateContent(ext->templateChildren(), content)); ext->setTemplateChildren(updateContent(ext->templateChildren(), content, depth));
} }
void setEventCallbacks(LuaUi::WidgetExtension* ext, const sol::object& eventsObj) void setEventCallbacks(LuaUi::WidgetExtension* ext, const sol::object& eventsObj)
@ -112,7 +118,7 @@ namespace LuaUi
}); });
} }
WidgetExtension* createWidget(const sol::table& layout) WidgetExtension* createWidget(const sol::table& layout, uint64_t depth)
{ {
static auto widgetTypeMap = widgetTypeToName(); static auto widgetTypeMap = widgetTypeToName();
std::string type = widgetType(layout); std::string type = widgetType(layout);
@ -130,19 +136,21 @@ namespace LuaUi
throw std::runtime_error("Invalid widget!"); throw std::runtime_error("Invalid widget!");
ext->initialize(layout.lua_state(), widget); ext->initialize(layout.lua_state(), widget);
updateWidget(ext, layout); updateWidget(ext, layout, depth);
return ext; return ext;
} }
void updateWidget(WidgetExtension* ext, const sol::table& layout) void updateWidget(WidgetExtension* ext, const sol::table& layout, uint64_t depth)
{ {
if (depth >= maxDepth)
throw std::runtime_error("Maximum layout depth exceeded, probably caused by a circular reference");
ext->reset(); ext->reset();
ext->setLayout(layout); ext->setLayout(layout);
ext->setExternal(layout.get<sol::object>(LayoutKeys::external)); ext->setExternal(layout.get<sol::object>(LayoutKeys::external));
setTemplate(ext, layout.get<sol::object>(LayoutKeys::templateLayout)); setTemplate(ext, layout.get<sol::object>(LayoutKeys::templateLayout), depth);
ext->setProperties(layout.get<sol::object>(LayoutKeys::props)); ext->setProperties(layout.get<sol::object>(LayoutKeys::props));
setEventCallbacks(ext, layout.get<sol::object>(LayoutKeys::events)); setEventCallbacks(ext, layout.get<sol::object>(LayoutKeys::events));
ext->setChildren(updateContent(ext->children(), layout.get<sol::object>(LayoutKeys::content))); ext->setChildren(updateContent(ext->children(), layout.get<sol::object>(LayoutKeys::content), depth));
ext->updateCoord(); ext->updateCoord();
} }
@ -159,6 +167,7 @@ namespace LuaUi
} }
return newLayer; return newLayer;
} }
}
std::map<Element*, std::shared_ptr<Element>> Element::sAllElements; std::map<Element*, std::shared_ptr<Element>> Element::sAllElements;
@ -184,7 +193,7 @@ namespace LuaUi
assert(!mRoot); assert(!mRoot);
if (!mRoot) if (!mRoot)
{ {
mRoot = createWidget(mLayout); mRoot = createWidget(mLayout, 0);
mLayer = setLayer(mRoot, mLayout); mLayer = setLayer(mRoot, mLayout);
updateAttachment(); updateAttachment();
} }
@ -197,11 +206,11 @@ namespace LuaUi
if (mRoot->widget()->getTypeName() != widgetType(mLayout)) if (mRoot->widget()->getTypeName() != widgetType(mLayout))
{ {
destroyWidget(mRoot); destroyWidget(mRoot);
mRoot = createWidget(mLayout); mRoot = createWidget(mLayout, 0);
} }
else else
{ {
updateWidget(mRoot, mLayout); updateWidget(mRoot, mLayout, 0);
} }
mLayer = setLayer(mRoot, mLayout); mLayer = setLayer(mRoot, mLayout);
updateAttachment(); updateAttachment();