Use ObjectID in ProgressDialog Window list#113642
Hidden character warning
Conversation
|
It looks like the ObjectID was preventing a heap-use-after-free. |
What. How exactly it's "used"? Does passing the pointer as an argument trigger it? How it can be ignored? |
|
It's used to detect if the pointer is valid, it's not preventing it but the |
|
Well, I know that, but pointer validity is not relevant here. |
|
Yes because the method called performs a null check which triggers use after free: godot/editor/gui/progress_dialog.cpp Lines 270 to 273 in dec5a37 |
|
This error is just stupid. Why does it care whether the pointer is freed or not? I only want to use the address, not whatever it's pointing to. How to tell the sanitizer that I know that the pointer is invalid and it doesn't matter here? I could just convert it to integer or whatever, but that probably also counts as "use". |
|
The address sanitizer checks when passing into a function apparently. There are function attributes like It should work if it is moved to predelete or enter/exit tree, right? |
|
Predelete is too late, not sure about exit tree. |
bruvzg
left a comment
There was a problem hiding this comment.
This check was not doing anything useful, host_window store pointers, and it doesn't matter if pointer is valid. And removing invalid pointers is actually necessary.
So change looks good. But since sanitizer is not happy, I would change host_window and related functions to store ObjectIDs instead of pointers. It's also safer in case some methods accessing host_window content is called after Window is freed but before pointer is removed.
diff --git a/editor/gui/progress_dialog.cpp b/editor/gui/progress_dialog.cpp
index b82b3549fc..bf3c571fe0 100644
--- a/editor/gui/progress_dialog.cpp
+++ b/editor/gui/progress_dialog.cpp
@@ -156,8 +156,11 @@ void ProgressDialog::_popup() {
// will discard every key input.
EditorNode::get_singleton()->set_process_input(true);
// Disable all other windows to prevent interaction with them.
- for (Window *w : host_windows) {
- w->set_process_mode(PROCESS_MODE_DISABLED);
+ for (ObjectID wid : host_windows) {
+ Window *w = ObjectDB::get_instance<Window>(wid);
+ if (w) {
+ w->set_process_mode(PROCESS_MODE_DISABLED);
+ }
}
Size2 ms = main->get_combined_minimum_size();
@@ -254,21 +257,22 @@ void ProgressDialog::end_task(const String &p_task) {
if (tasks.is_empty()) {
hide();
EditorNode::get_singleton()->set_process_input(false);
- for (Window *w : host_windows) {
- w->set_process_mode(PROCESS_MODE_INHERIT);
+ for (ObjectID wid : host_windows) {
+ Window *w = ObjectDB::get_instance<Window>(wid);
+ if (w) {
+ w->set_process_mode(PROCESS_MODE_INHERIT);
+ }
}
} else {
_popup();
}
}
-void ProgressDialog::add_host_window(Window *p_window) {
- ERR_FAIL_NULL(p_window);
+void ProgressDialog::add_host_window(ObjectID p_window) {
host_windows.push_back(p_window);
}
-void ProgressDialog::remove_host_window(Window *p_window) {
- ERR_FAIL_NULL(p_window);
+void ProgressDialog::remove_host_window(ObjectID p_window) {
host_windows.erase(p_window);
}
diff --git a/editor/gui/progress_dialog.h b/editor/gui/progress_dialog.h
index 17deeac79c..4e51f9e3bf 100644
--- a/editor/gui/progress_dialog.h
+++ b/editor/gui/progress_dialog.h
@@ -79,7 +79,7 @@ class ProgressDialog : public CenterContainer {
PanelContainer *center_panel = nullptr;
VBoxContainer *main = nullptr;
- LocalVector<Window *> host_windows;
+ LocalVector<ObjectID> host_windows;
Size2 main_border_size;
@@ -101,8 +101,8 @@ public:
bool task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_redraw = true);
void end_task(const String &p_task);
- void add_host_window(Window *p_window);
- void remove_host_window(Window *p_window);
+ void add_host_window(ObjectID p_window);
+ void remove_host_window(ObjectID p_window);
ProgressDialog();
};
diff --git a/editor/gui/window_wrapper.cpp b/editor/gui/window_wrapper.cpp
index 055a4361f0..e97780280d 100644
--- a/editor/gui/window_wrapper.cpp
+++ b/editor/gui/window_wrapper.cpp
@@ -383,13 +383,11 @@ WindowWrapper::WindowWrapper() {
window_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
window->add_child(window_background);
- ProgressDialog::get_singleton()->add_host_window(window);
+ ProgressDialog::get_singleton()->add_host_window(window_id);
}
WindowWrapper::~WindowWrapper() {
- if (ObjectDB::get_instance(window_id)) {
- ProgressDialog::get_singleton()->remove_host_window(window);
- }
+ ProgressDialog::get_singleton()->remove_host_window(window_id);
}
// ScreenSelect|
It is still happening, so it must be the ProgressDialog itself that is used after it is freed. |
|
The ProgressDialog singleton is never set to null, it just gets deleted. ~ProgressDialog() {
singleton = nullptr;
}Just setting it to null makes also it easier to debug, since a debugger will break when it is used without needing the sanitizer. |
Co-authored-by: bruvzg <7645683+bruvzg@users.noreply.github.com>
akien-mga
left a comment
There was a problem hiding this comment.
Seems to pass CI now!
(Android builds failed due to Maven repos being flaky, should be restarted later.)
|
Thanks! |
Use ObjectID in ProgressDialog Window list
Fixes #113276
Progress dialog has a LocalVector of pointers to Windows (nodes, not the OS lol). The
windowis a child of WindowWrapper. The condition in WindowWrapper's destructor was always false, because the Window is freed before the wrapper, and so the ObjectID was invalid. For the purpose of removing a pointer from a vector, it does not matter if it's valid or not, so that check just didn't make sense.Funny how ObjectID is normally supposed to prevent crashes 🤦♂️