Repository navigation
Description
Problem
During application exit, the FlEngine is finalized and its memory is freed. However, accessibility nodes can survive past this teardown because the external AT-SPI D-Bus accessibility bridge (libatk-bridge) maintains strong references to queried AtkObject elements. Accessibility nodes in this scenario have a dangling engine pointer.
FlAccessibleNode registers its weak-pointer notifies on itself instead of on the target object:
flutter/engine/src/flutter/shell/platform/linux/fl_accessible_node.cc
Lines 576 to 577 in 879ec7d
| g_object_add_weak_pointer(G_OBJECT(self), | |
| reinterpret_cast<gpointer*>(&(priv->parent))); |
g_object_add_weak_pointer(obj, loc) nulls *loc when obj is finalized, so registering on self provides zero protection against engine/parent destruction. priv->engine and priv->parent are therefore plain raw pointers. When the engine (or parent) is freed first — which is the normal ordering in fl_view_dispose — any subsequent AT-SPI call into the surviving node dereferences freed memory.
Suggested fix
The FlAccessibleNode should be refactored to use the thread-safe GWeakRef API for priv->engine, ensuring proper lifecycle management matching the rest of the embedder.
Potential bandaid fix...
diff --git a/engine/src/flutter/shell/platform/linux/fl_accessible_node.cc b/engine/src/flutter/shell/platform/linux/fl_accessible_node.cc
--- a/engine/src/flutter/shell/platform/linux/fl_accessible_node.cc
+++ b/engine/src/flutter/shell/platform/linux/fl_accessible_node.cc
@@ -154,7 +154,7 @@ static void fl_accessible_node_set_property(GObject* object,
case PROP_ENGINE:
g_assert(priv->engine == nullptr);
priv->engine = FL_ENGINE(g_value_get_object(value));
- g_object_add_weak_pointer(object,
+ g_object_add_weak_pointer(G_OBJECT(priv->engine),
reinterpret_cast<gpointer*>(&priv->engine));
break;
case PROP_VIEW_ID:
@@ -173,12 +173,12 @@ static void fl_accessible_node_dispose(GObject* object) {
FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(object);
if (priv->engine != nullptr) {
- g_object_remove_weak_pointer(object,
+ g_object_remove_weak_pointer(G_OBJECT(priv->engine),
reinterpret_cast<gpointer*>(&(priv->engine)));
priv->engine = nullptr;
}
if (priv->parent != nullptr) {
- g_object_remove_weak_pointer(object,
+ g_object_remove_weak_pointer(G_OBJECT(priv->parent),
reinterpret_cast<gpointer*>(&(priv->parent)));
priv->parent = nullptr;
}
@@ -571,9 +571,14 @@ void fl_accessible_node_set_parent(FlAccessibleNode* self,
gint index) {
g_return_if_fail(FL_IS_ACCESSIBLE_NODE(self));
FlAccessibleNodePrivate* priv = FL_ACCESSIBLE_NODE_GET_PRIVATE(self);
+ if (priv->parent != nullptr) {
+ g_object_remove_weak_pointer(G_OBJECT(priv->parent),
+ reinterpret_cast<gpointer*>(&(priv->parent)));
+ priv->parent = nullptr;
+ }
priv->parent = parent;
priv->index = index;
- g_object_add_weak_pointer(G_OBJECT(self),
+ g_object_add_weak_pointer(G_OBJECT(parent),
reinterpret_cast<gpointer*>(&(priv->parent)));
}
Activity
robert-ancell commentedon Jun 28, 2026
Looks like this was a case that got missed when we migrated the other code from
g_object_add_weak_pointertoGWeakRef.[linux] Use GWeakRef for engine and parent in FlAccessibleNode
[linux] Use GWeakRef for engine and parent in FlAccessibleNode (flutt…
github-actions commentedon Jul 14, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.