Merge lp:~mhr3/unity/fix-861144 into lp:unity

Proposed by Michal Hruby
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 1748
Proposed branch: lp:~mhr3/unity/fix-861144
Merge into: lp:unity
Diff against target: 193 lines (+78/-31)
3 files modified
services/panel-indicator-entry-accessible.c (+1/-3)
services/panel-root-accessible.c (+35/-6)
services/panel-service.c (+42/-22)
To merge this branch: bzr merge lp:~mhr3/unity/fix-861144
Reviewer Review Type Date Requested Status
Tim Penhey (community) Approve
Marco Trevisan (Treviño) Approve
Review via email: mp+81957@code.launchpad.net

Description of the change

The changes in this branch try to make the panel-service more crash-proof by disallowing casts of random pointers to IndicatorObjectEntry instances, as well as installing a weak notifier on the Indicators (in case they are unreferenced after instantiation).

Please note that this should go also into unity/4.0 branch.

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

+ {
+ /* check that there really is such IndicatorObjectEntry */
+ if (g_hash_table_lookup (priv->entry2indicator_hash, entry) != NULL)
+ {
+ return entry;
+ }

Can you add an extra check in there:

+ if (!INDICATOR_IS_OBJECT_ENTRY (entry))
+ {
+ g_critical ("Invalid IndicatorObjectEntry reference %p", entry);
+ return NULL;
+ }

Not that it makes the code much more safe, but it has a slim chance of
making debugging easier should we ever see it again

Revision history for this message
Michal Hruby (mhr3) wrote :

I'd love to add that, but IndicatorObjectEntry is just a simple struct, sorry...

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

get_entry_by_id has been previously added to be very fast and to avoid unneeded loops or checks.
If well used it shouldn't lead to crashes, was it causing some?
However the new lookup improves the safety.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Marco - at the very least it was taking a string directly from DBus
and parsing it into a memory address and accessing that address
without any sanity checks. That is *highly* unsafe and can lead to
arbitrary impossible-to-debug bugs at any later point in time. Granted
- after this patch it is still highly unsafe, if slightly less than
before :-)

For P I think we should find a way to stop passing pointers over DBus.
It is just wrong in so many ways.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

On the first version the pointer was just used as an ID that was used by an hashtable, but using this way caused some slowdown. However I know that the current implementation is potentially unsafe, but since we're mostly getting events from that entries, the risks are controlled. Also I never experienced a crash caused by that.

However I agree that this part would need a better design. I'm available for working on improving this part on all the stack (from libindicator, to unity itself).

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Ah, however to fix this crash (bug #861144) I guess that we only need (at the current state) to add a

   g_return_if_fail (object);

In the panel_service_show_entry function. I forgot to add it previously, and I'd suggest to use this check always, after that the entry_id has been parsed, leaving the old get_entry_by_id implementation.
Otherwise we'll duplicate the number of hashtable lookups.

Otherwise using something like
   void panel_service_get_indicator_entry_by_id(PanelService *self, IndicatorObject **io, IndicatorObjectEntry **entry);

To get also the IndicatorObject and to avoid further lookups.

A simple way to optimize all these operations, by the way, would be to link an entry to its own parent indicator (also if this could lead to other issues, though).

Revision history for this message
Michal Hruby (mhr3) wrote :

> Ah, however to fix this crash (bug #861144) I guess that we only need (at the
> current state) to add a
>
> g_return_if_fail (object);
>

Yes that should also suffice, but we *really* don't want unchecked pointer casting.

> In the panel_service_show_entry function. I forgot to add it previously, and
> I'd suggest to use this check always, after that the entry_id has been parsed,
> leaving the old get_entry_by_id implementation.
> Otherwise we'll duplicate the number of hashtable lookups.
>
> Otherwise using something like
> void panel_service_get_indicator_entry_by_id(PanelService *self,
> IndicatorObject **io, IndicatorObjectEntry **entry);
>
> To get also the IndicatorObject and to avoid further lookups.
>

Ok, I'll fix it to do that to save a couple of cpu cycles.

> A simple way to optimize all these operations, by the way, would be to link an
> entry to its own parent indicator (also if this could lead to other issues,
> though).

Feel free to do so, there are too many scary bits in libindicator... Although that'd be most likely an ABI break, so P stuff.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

> Feel free to do so, there are too many scary bits in libindicator... Although
> that'd be most likely an ABI break, so P stuff.

Well, I agree. But I think that would be redesigned at all with Ted.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Ah, +1 ;)

review: Approve
Revision history for this message
Tim Penhey (thumper) wrote :

Looks fine.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'services/panel-indicator-entry-accessible.c'
2--- services/panel-indicator-entry-accessible.c 2011-09-02 14:56:07 +0000
3+++ services/panel-indicator-entry-accessible.c 2011-11-17 09:51:25 +0000
4@@ -235,9 +235,7 @@
5 {
6 atk_object_set_role (accessible, ATK_ROLE_IMAGE);
7 if (piea->priv->entry->accessible_desc != NULL)
8- {
9- atk_object_set_name (accessible, atk_object_get_name (ATK_OBJECT (piea->priv->entry->image)));
10- }
11+ atk_object_set_name (accessible, piea->priv->entry->accessible_desc);
12 }
13
14 if (piea->priv->entry->accessible_desc != NULL)
15
16=== modified file 'services/panel-root-accessible.c'
17--- services/panel-root-accessible.c 2011-10-05 15:28:19 +0000
18+++ services/panel-root-accessible.c 2011-11-17 09:51:25 +0000
19@@ -88,6 +88,26 @@
20 return accessible;
21 }
22
23+static void
24+on_indicator_removed (gpointer *data, GObject *removed_indicator)
25+{
26+ PanelRootAccessible *root = data[0];
27+ AtkObject *child = data[1];
28+
29+ g_return_if_fail (PANEL_IS_ROOT_ACCESSIBLE (root));
30+ g_return_if_fail (ATK_IS_OBJECT (child));
31+
32+ gint index = g_slist_index (root->priv->a11y_children, child);
33+ if (index >= 0)
34+ {
35+ root->priv->a11y_children = g_slist_remove (root->priv->a11y_children, child);
36+ g_signal_emit_by_name (root, "children-changed::remove", index, child);
37+ g_object_unref (child);
38+ }
39+
40+ g_free (data);
41+}
42+
43 /* Implementation of AtkObject methods */
44
45 static void
46@@ -111,14 +131,23 @@
47 IndicatorObject *indicator;
48
49 indicator = panel_service_get_indicator_nth (root->priv->service, i);
50- if (indicator != NULL)
51+ if (INDICATOR_IS_OBJECT (indicator))
52 {
53- AtkObject *child;
54-
55- child = panel_indicator_accessible_new (indicator);
56+ AtkObject *child;
57+ gpointer *data;
58+
59+ child = panel_indicator_accessible_new (indicator);
60+ /* FIXME: use proper signals once we support dynamic adding/removing
61+ * of indicators */
62+ data = g_new0 (gpointer, 2);
63+ data[0] = root;
64+ data[1] = child;
65+ g_object_weak_ref (G_OBJECT (indicator),
66+ (GWeakNotify) on_indicator_removed, data);
67+
68 atk_object_set_parent (child, accessible);
69- root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child);
70- }
71+ root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child);
72+ }
73 }
74 }
75
76
77=== modified file 'services/panel-service.c'
78--- services/panel-service.c 2011-10-13 16:46:47 +0000
79+++ services/panel-service.c 2011-11-17 09:51:25 +0000
80@@ -325,15 +325,30 @@
81 return NULL;
82 }
83
84-static IndicatorObjectEntry *
85-get_entry_by_id (const gchar *entry_id)
86+static void
87+panel_service_get_indicator_entry_by_id (PanelService *self,
88+ const gchar *entry_id,
89+ IndicatorObjectEntry **entry,
90+ IndicatorObject **object)
91 {
92- IndicatorObjectEntry *entry;
93-
94- if (sscanf (entry_id, "%p", &entry) == 1)
95- return entry;
96+ IndicatorObject *indicator;
97+ IndicatorObjectEntry *probably_entry;
98+ PanelServicePrivate *priv = self->priv;
99
100- return NULL;
101+ /* FIXME: eeek, why do we even do this? */
102+ if (sscanf (entry_id, "%p", &probably_entry) == 1)
103+ {
104+ /* check that there really is such IndicatorObjectEntry */
105+ indicator = g_hash_table_lookup (priv->entry2indicator_hash,
106+ probably_entry);
107+ if (object) *object = indicator;
108+ if (entry) *entry = indicator != NULL ? probably_entry : NULL;
109+ }
110+ else
111+ {
112+ if (object) *object = NULL;
113+ if (entry) *entry = NULL;
114+ }
115 }
116
117 static gboolean
118@@ -1132,9 +1147,11 @@
119 gint width,
120 gint height)
121 {
122- PanelServicePrivate *priv = self->priv;
123- IndicatorObjectEntry *entry = get_entry_by_id (entry_id);
124- IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
125+ IndicatorObject *object;
126+ IndicatorObjectEntry *entry;
127+ PanelServicePrivate *priv = self->priv;
128+
129+ panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
130
131 if (entry)
132 {
133@@ -1184,9 +1201,9 @@
134 geo->width = width;
135 geo->height = height;
136 }
137+
138+ g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
139 }
140-
141- g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
142 }
143
144 static gboolean
145@@ -1357,10 +1374,12 @@
146 gint32 y,
147 gint32 button)
148 {
149- PanelServicePrivate *priv = self->priv;
150- IndicatorObjectEntry *entry = get_entry_by_id (entry_id);
151- IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
152+ IndicatorObject *object;
153+ IndicatorObjectEntry *entry;
154 GtkWidget *last_menu;
155+ PanelServicePrivate *priv = self->priv;
156+
157+ panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
158
159 g_return_if_fail (entry);
160
161@@ -1450,12 +1469,12 @@
162 const gchar *entry_id,
163 guint32 timestamp)
164 {
165- PanelServicePrivate *priv = self->priv;
166- IndicatorObjectEntry *entry = get_entry_by_id (entry_id);
167+ IndicatorObject *object;
168+ IndicatorObjectEntry *entry;
169+
170+ panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
171 g_return_if_fail (entry);
172
173- IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
174-
175 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry,
176 timestamp);
177 }
178@@ -1465,11 +1484,12 @@
179 const gchar *entry_id,
180 gint32 delta)
181 {
182- PanelServicePrivate *priv = self->priv;
183- IndicatorObjectEntry *entry = get_entry_by_id (entry_id);
184+ IndicatorObject *object;
185+ IndicatorObjectEntry *entry;
186+
187+ panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
188 g_return_if_fail (entry);
189
190- IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
191 GdkScrollDirection direction = delta < 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
192
193 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry,