diff options
| author | Yuqian Yang <crupest@crupest.life> | 2025-11-20 03:00:16 +0800 |
|---|---|---|
| committer | Yuqian Yang <crupest@crupest.life> | 2025-11-20 03:00:16 +0800 |
| commit | 478b4cdce5c584c294d38f84a0131f239d9af88f (patch) | |
| tree | 74ad9ae123869195c4ce845f9ce34c4425a62b3d | |
| parent | 2eb34dfc3dd3ca718da6ebf049886b4ae140b332 (diff) | |
| download | cru-478b4cdce5c584c294d38f84a0131f239d9af88f.tar.gz cru-478b4cdce5c584c294d38f84a0131f239d9af88f.tar.bz2 cru-478b4cdce5c584c294d38f84a0131f239d9af88f.zip | |
Fix use after free in render object.
| -rw-r--r-- | CMakeSettings.json | 28 | ||||
| -rw-r--r-- | include/cru/base/Guard.h | 1 | ||||
| -rw-r--r-- | include/cru/base/StringUtil.h | 4 | ||||
| -rw-r--r-- | include/cru/ui/render/LayoutRenderObject.h | 21 | ||||
| -rw-r--r-- | include/cru/ui/render/RenderObject.h | 8 | ||||
| -rw-r--r-- | src/ui/render/RenderObject.cpp | 2 |
6 files changed, 54 insertions, 10 deletions
diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 00000000..f7e15ea2 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,28 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "x64-Debug-Asan", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x64_x64" ], + "addressSanitizerEnabled": true, + "variables": [] + } + ] +} diff --git a/include/cru/base/Guard.h b/include/cru/base/Guard.h index 77e48d79..a23e2574 100644 --- a/include/cru/base/Guard.h +++ b/include/cru/base/Guard.h @@ -1,7 +1,6 @@ #pragma once #include "Base.h" -#include "cru/base/Base.h" #include <cstdlib> #include <functional> diff --git a/include/cru/base/StringUtil.h b/include/cru/base/StringUtil.h index 7748f0d3..dbc748d7 100644 --- a/include/cru/base/StringUtil.h +++ b/include/cru/base/StringUtil.h @@ -240,7 +240,7 @@ CodePoint CRU_BASE_API Utf8NextCodePoint(const Utf8CodeUnit* ptr, Index size, inline CodePoint Utf8NextCodePoint(Utf8StringView str, Index current, Index* next_position) { - return Utf8NextCodePoint(str.data(), str.size(), next_position); + return Utf8NextCodePoint(str.data(), str.size(), current, next_position); } CodePoint CRU_BASE_API Utf8PreviousCodePoint(const Utf8CodeUnit* ptr, @@ -249,7 +249,7 @@ CodePoint CRU_BASE_API Utf8PreviousCodePoint(const Utf8CodeUnit* ptr, inline CodePoint Utf8PreviousCodePoint(Utf8StringView str, Index current, Index* next_position) { - return Utf8PreviousCodePoint(str.data(), str.size(), next_position); + return Utf8PreviousCodePoint(str.data(), str.size(), current, next_position); } namespace details { diff --git a/include/cru/ui/render/LayoutRenderObject.h b/include/cru/ui/render/LayoutRenderObject.h index 11df0449..c0e4218d 100644 --- a/include/cru/ui/render/LayoutRenderObject.h +++ b/include/cru/ui/render/LayoutRenderObject.h @@ -13,6 +13,8 @@ class LayoutRenderObject : public RenderObject { struct ChildData { RenderObject* render_object; ChildLayoutData layout_data; + bool render_object_destroyed; + EventHandlerRevokerListGuard event_guard; }; protected: @@ -33,21 +35,34 @@ class LayoutRenderObject : public RenderObject { if (position < 0) position = 0; if (position > GetChildCount()) position = GetChildCount(); children_.insert(children_.begin() + position, - ChildData{render_object, ChildLayoutData()}); + ChildData{render_object, ChildLayoutData(), false}); render_object->SetParent(this); + render_object->DestroyEvent()->AddSpyOnlyHandler([this, render_object] { + auto iter = std::ranges::find_if( + children_, [render_object](const ChildData& data) { + return data.render_object == render_object; + }); + if (iter != children_.cend()) { + iter->render_object_destroyed = true; + } + }); InvalidateLayout(); } void RemoveChild(Index position) { Expects(position >= 0 && position < GetChildCount()); - children_[position].render_object->SetParent(nullptr); + if (!children_[position].render_object_destroyed) { + children_[position].render_object->SetParent(nullptr); + } children_.erase(children_.begin() + position); InvalidateLayout(); } void ClearChildren() { for (auto child : children_) { - child.render_object->SetParent(nullptr); + if (!child.render_object_destroyed) { + child.render_object->SetParent(nullptr); + } } children_.clear(); InvalidateLayout(); diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h index 4c19ad3e..c7e534a8 100644 --- a/include/cru/ui/render/RenderObject.h +++ b/include/cru/ui/render/RenderObject.h @@ -3,6 +3,7 @@ #include "MeasureRequirement.h" +#include <cru/base/Event.h> #include <cru/platform/graphics/Painter.h> namespace cru::ui::render { @@ -62,11 +63,8 @@ struct BoxConstraint { class CRU_UI_API RenderObject : public Object { CRU_DEFINE_CLASS_LOG_TAG("RenderObject") - protected: - RenderObject() = default; - public: - ~RenderObject() override = default; + ~RenderObject() override; controls::Control* GetAttachedControl() const { return control_; } void SetAttachedControl(controls::Control* new_control); @@ -145,6 +143,8 @@ class CRU_UI_API RenderObject : public Object { virtual std::string GetName() const; std::string GetDebugPathInTree() const; + CRU_DEFINE_EVENT(Destroy, RenderObject*) + protected: // Size measure including margin and padding. Please reduce margin and padding // or other custom things and pass the result content measure requirement and diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index fbb7c292..d999be7b 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -9,6 +9,8 @@ namespace cru::ui::render { const BoxConstraint BoxConstraint::kNotLimit{Size::kMax, Size::kZero}; +RenderObject::~RenderObject() { DestroyEvent_.Raise(this); } + void RenderObject::SetParent(RenderObject* new_parent) { #ifdef CRU_DEBUG // In case there is a cycle. |
