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
=== modified file 'services/panel-indicator-entry-accessible.c'
--- services/panel-indicator-entry-accessible.c 2011-09-02 14:56:07 +0000
+++ services/panel-indicator-entry-accessible.c 2011-11-17 09:51:25 +0000
@@ -235,9 +235,7 @@
235 {235 {
236 atk_object_set_role (accessible, ATK_ROLE_IMAGE);236 atk_object_set_role (accessible, ATK_ROLE_IMAGE);
237 if (piea->priv->entry->accessible_desc != NULL)237 if (piea->priv->entry->accessible_desc != NULL)
238 {238 atk_object_set_name (accessible, piea->priv->entry->accessible_desc);
239 atk_object_set_name (accessible, atk_object_get_name (ATK_OBJECT (piea->priv->entry->image)));
240 }
241 }239 }
242240
243 if (piea->priv->entry->accessible_desc != NULL)241 if (piea->priv->entry->accessible_desc != NULL)
244242
=== modified file 'services/panel-root-accessible.c'
--- services/panel-root-accessible.c 2011-10-05 15:28:19 +0000
+++ services/panel-root-accessible.c 2011-11-17 09:51:25 +0000
@@ -88,6 +88,26 @@
88 return accessible;88 return accessible;
89}89}
9090
91static void
92on_indicator_removed (gpointer *data, GObject *removed_indicator)
93{
94 PanelRootAccessible *root = data[0];
95 AtkObject *child = data[1];
96
97 g_return_if_fail (PANEL_IS_ROOT_ACCESSIBLE (root));
98 g_return_if_fail (ATK_IS_OBJECT (child));
99
100 gint index = g_slist_index (root->priv->a11y_children, child);
101 if (index >= 0)
102 {
103 root->priv->a11y_children = g_slist_remove (root->priv->a11y_children, child);
104 g_signal_emit_by_name (root, "children-changed::remove", index, child);
105 g_object_unref (child);
106 }
107
108 g_free (data);
109}
110
91/* Implementation of AtkObject methods */111/* Implementation of AtkObject methods */
92112
93static void113static void
@@ -111,14 +131,23 @@
111 IndicatorObject *indicator;131 IndicatorObject *indicator;
112132
113 indicator = panel_service_get_indicator_nth (root->priv->service, i);133 indicator = panel_service_get_indicator_nth (root->priv->service, i);
114 if (indicator != NULL)134 if (INDICATOR_IS_OBJECT (indicator))
115 {135 {
116 AtkObject *child;136 AtkObject *child;
117137 gpointer *data;
118 child = panel_indicator_accessible_new (indicator);138
139 child = panel_indicator_accessible_new (indicator);
140 /* FIXME: use proper signals once we support dynamic adding/removing
141 * of indicators */
142 data = g_new0 (gpointer, 2);
143 data[0] = root;
144 data[1] = child;
145 g_object_weak_ref (G_OBJECT (indicator),
146 (GWeakNotify) on_indicator_removed, data);
147
119 atk_object_set_parent (child, accessible);148 atk_object_set_parent (child, accessible);
120 root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child);149 root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child);
121 }150 }
122 }151 }
123}152}
124153
125154
=== modified file 'services/panel-service.c'
--- services/panel-service.c 2011-10-13 16:46:47 +0000
+++ services/panel-service.c 2011-11-17 09:51:25 +0000
@@ -325,15 +325,30 @@
325 return NULL;325 return NULL;
326}326}
327327
328static IndicatorObjectEntry *328static void
329get_entry_by_id (const gchar *entry_id)329panel_service_get_indicator_entry_by_id (PanelService *self,
330 const gchar *entry_id,
331 IndicatorObjectEntry **entry,
332 IndicatorObject **object)
330{333{
331 IndicatorObjectEntry *entry;334 IndicatorObject *indicator;
332 335 IndicatorObjectEntry *probably_entry;
333 if (sscanf (entry_id, "%p", &entry) == 1)336 PanelServicePrivate *priv = self->priv;
334 return entry;
335337
336 return NULL;338 /* FIXME: eeek, why do we even do this? */
339 if (sscanf (entry_id, "%p", &probably_entry) == 1)
340 {
341 /* check that there really is such IndicatorObjectEntry */
342 indicator = g_hash_table_lookup (priv->entry2indicator_hash,
343 probably_entry);
344 if (object) *object = indicator;
345 if (entry) *entry = indicator != NULL ? probably_entry : NULL;
346 }
347 else
348 {
349 if (object) *object = NULL;
350 if (entry) *entry = NULL;
351 }
337}352}
338353
339static gboolean354static gboolean
@@ -1132,9 +1147,11 @@
1132 gint width,1147 gint width,
1133 gint height)1148 gint height)
1134{1149{
1135 PanelServicePrivate *priv = self->priv;1150 IndicatorObject *object;
1136 IndicatorObjectEntry *entry = get_entry_by_id (entry_id);1151 IndicatorObjectEntry *entry;
1137 IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);1152 PanelServicePrivate *priv = self->priv;
1153
1154 panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
11381155
1139 if (entry)1156 if (entry)
1140 {1157 {
@@ -1184,9 +1201,9 @@
1184 geo->width = width;1201 geo->width = width;
1185 geo->height = height;1202 geo->height = height;
1186 }1203 }
1204
1205 g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
1187 }1206 }
1188
1189 g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
1190}1207}
11911208
1192static gboolean1209static gboolean
@@ -1357,10 +1374,12 @@
1357 gint32 y,1374 gint32 y,
1358 gint32 button)1375 gint32 button)
1359{1376{
1360 PanelServicePrivate *priv = self->priv;1377 IndicatorObject *object;
1361 IndicatorObjectEntry *entry = get_entry_by_id (entry_id);1378 IndicatorObjectEntry *entry;
1362 IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
1363 GtkWidget *last_menu;1379 GtkWidget *last_menu;
1380 PanelServicePrivate *priv = self->priv;
1381
1382 panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
13641383
1365 g_return_if_fail (entry);1384 g_return_if_fail (entry);
13661385
@@ -1450,12 +1469,12 @@
1450 const gchar *entry_id,1469 const gchar *entry_id,
1451 guint32 timestamp)1470 guint32 timestamp)
1452{1471{
1453 PanelServicePrivate *priv = self->priv;1472 IndicatorObject *object;
1454 IndicatorObjectEntry *entry = get_entry_by_id (entry_id);1473 IndicatorObjectEntry *entry;
1474
1475 panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
1455 g_return_if_fail (entry);1476 g_return_if_fail (entry);
14561477
1457 IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
1458
1459 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry,1478 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry,
1460 timestamp);1479 timestamp);
1461}1480}
@@ -1465,11 +1484,12 @@
1465 const gchar *entry_id,1484 const gchar *entry_id,
1466 gint32 delta)1485 gint32 delta)
1467{1486{
1468 PanelServicePrivate *priv = self->priv;1487 IndicatorObject *object;
1469 IndicatorObjectEntry *entry = get_entry_by_id (entry_id);1488 IndicatorObjectEntry *entry;
1489
1490 panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
1470 g_return_if_fail (entry);1491 g_return_if_fail (entry);
14711492
1472 IndicatorObject *object = g_hash_table_lookup (priv->entry2indicator_hash, entry);
1473 GdkScrollDirection direction = delta < 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;1493 GdkScrollDirection direction = delta < 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
14741494
1475 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry,1495 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry,