aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-11-20 03:00:16 +0800
committerYuqian Yang <crupest@crupest.life>2025-11-20 03:00:16 +0800
commit478b4cdce5c584c294d38f84a0131f239d9af88f (patch)
tree74ad9ae123869195c4ce845f9ce34c4425a62b3d
parent2eb34dfc3dd3ca718da6ebf049886b4ae140b332 (diff)
downloadcru-478b4cdce5c584c294d38f84a0131f239d9af88f.tar.gz
cru-478b4cdce5c584c294d38f84a0131f239d9af88f.tar.bz2
cru-478b4cdce5c584c294d38f84a0131f239d9af88f.zip
Fix use after free in render object.
-rw-r--r--CMakeSettings.json28
-rw-r--r--include/cru/base/Guard.h1
-rw-r--r--include/cru/base/StringUtil.h4
-rw-r--r--include/cru/ui/render/LayoutRenderObject.h21
-rw-r--r--include/cru/ui/render/RenderObject.h8
-rw-r--r--src/ui/render/RenderObject.cpp2
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.