Merge lp:~compiz-team/compiz/compiz.fix_1018602 into lp:compiz/0.9.8

Proposed by Sam Spilsbury
Status: Merged
Merged at revision: 3267
Proposed branch: lp:~compiz-team/compiz/compiz.fix_1018602
Merge into: lp:compiz/0.9.8
Diff against target: 497 lines (+120/-96)
1 file modified
compizconfig/gsettings/src/gsettings.c (+120/-96)
To merge this branch: bzr merge lp:~compiz-team/compiz/compiz.fix_1018602
Reviewer Review Type Date Requested Status
Daniel van Vugt Approve
Review via email: mp+112718@code.launchpad.net

This proposal supersedes a proposal from 2012-06-29.

Description of the change

Fixes LP (#1018602) : An invalid read when using g_variant_iter_loop.

No tests yet, tests will be added to gsettings in another commit when the lcc testing work lands.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Oops. There is a problem under case TypeMatch:
array is no longer initialized with zeros because you changed calloc to malloc. So the free() loop after it could potentially free an invalid pointer if g_variant_iter_loop doesn't find exactly nItems.

Also, strdup(value) is not necessary if you use:
     while (g_variant_iter_next (iter, "s", &value))
  *arrayCounter++ = value;
and then change the free() calls below it to g_free().

On that note, the variable variantType should be removed from readListValue. Just use "s", "i", "b" and "d" instead. It will be smaller, faster and more readable.

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Coolio, fixed the calloc issue, plugged a few other memleaks and fixed some gvariant errors I noticed.

I abstracted out the variantType stuff into a function - it should probably be transparent to the user as to the implementation detail of having to pass an arbitrary string to g_variant_get

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Thanks for the tip on using g_variant_iter_next ... that's now fixed too.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

The addition of compizconfigTypeToVariantType makes no sense.

We already have a tight coupling between:
    TYPE x;
and:
    g_variant_iter_loop (&iter, TYPESTRING, &x)

Using:
    g_variant_iter_loop (&iter, someVariable, &x)
only makes the code harder to read, with looser coupling and weaker cohesion.

Please remove compizconfigTypeToVariantType and just use:
    g_variant_iter_loop (&iter, "X", &x)

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

It's the same as:
    printf(secretFormatString, x, y, z);
It's too hard to read so adds maintenance risk.

Everyone would much prefer to read:
    printf("The results are: %d %s %f\n", x, y, z);

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

I think the printf analogy in this case doesn't apply here, the type string is merely an implementation detail - we know from the code what we want to extract already, and the type string is there to operate the valist API.

In any case, I'll change it.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Maybe. But back to the actual case of g_variant_iter_loop(I, F, V)... F and V are tightly coupled and need to match up so you should see the explicit value of F and type of V within the same block of code...

int v;
g_variant_iter_loop(&x, "i", &v)

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

I had another look at the code and variantType is more or less used as a placeholder so that copypasting was easier and so that we could return if there was no variant type for that setting. The fact that we are copypasting is a problem, but I modified the function so that it returns a boolean for whether or not we can read that type of setting.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

It works OK, but I can see some potentially serious mistakes:

203: "f" should be "d"
(http://developer.gnome.org/glib/2.30/glib-GVariant.html#GVariantClass)

235: Missing g_free() loop:
    for (i = 0; i < nItems; i++)
        if (array[i])
            g_free(array[i]);

239: g_free() on calloc'd block (214). Either change g_free() to free(), or calloc() to g_malloc0().

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

> It works OK, but I can see some potentially serious mistakes:
>
> 203: "f" should be "d"
> (http://developer.gnome.org/glib/2.30/glib-GVariant.html#GVariantClass)

Thanks, fixed.

>
> 235: Missing g_free() loop:
> for (i = 0; i < nItems; i++)
> if (array[i])
> g_free(array[i]);

My understanding is that g_variant_iter_next returns a shallow copy of the array, so free () is sufficient in this case (unlike the previous code where we copied the values out of the array. Valgrind reports there are no leaks here.

>
> 239: g_free() on calloc'd block (214). Either change g_free() to free(), or
> calloc() to g_malloc0().

Fixed, although I think g_malloc is a wrapper around malloc itself (eg, doesn't use g_slice_alloc)

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

No, you still need the g_free loop to free each array element after g_variant_iter_next:
  http://developer.gnome.org/glib/2.32/glib-GVariant.html#g-variant-iter-next
It's possible valgrind doesn't see the leak because the memory is managed by glib and freed at exit. But it's still a leak even still.

Also, I was referring to g_malloc0() to replace calloc. Not g_malloc(). :)

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

You're right, I was confusing the array with the values themselves. Fixing it now.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Still needs fixing. Please don't click resubmit till after you've pushed the changes.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Sorry, my fault. I commented on an old version of the proposal. But still needs fixing.

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Fixed that, plugged other memleaks.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Thanks for the extra leak fixes but they're not part of fixing the crash.

I give up. It looks OK now but I don't want to manually test and valgrind it again. We'll be valgrinding a lot more in the near future.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'compizconfig/gsettings/src/gsettings.c'
2--- compizconfig/gsettings/src/gsettings.c 2012-06-28 09:03:09 +0000
3+++ compizconfig/gsettings/src/gsettings.c 2012-06-29 07:54:24 +0000
4@@ -33,6 +33,31 @@
5
6 #include "gsettings.h"
7
8+static gboolean
9+compizconfigTypeHasVariantType (CCSSettingType type)
10+{
11+ gint i = 0;
12+
13+ static const unsigned int nVariantTypes = 6;
14+ static const CCSSettingType variantTypes[] =
15+ {
16+ TypeString,
17+ TypeMatch,
18+ TypeColor,
19+ TypeBool,
20+ TypeInt,
21+ TypeFloat
22+ };
23+
24+ for (; i < nVariantTypes; i++)
25+ {
26+ if (variantTypes[i] == type)
27+ return TRUE;
28+ }
29+
30+ return FALSE;
31+}
32+
33 static void
34 valueChanged (GSettings *settings,
35 gchar *keyname,
36@@ -121,10 +146,11 @@
37 GList *l = settingsList;
38 gchar *schemaName = getSchemaNameForPlugin (plugin);
39 GVariant *writtenPlugins;
40+ gsize writtenPluginsLen;
41+ gsize newWrittenPluginsSize;
42+ gchar **newWrittenPlugins;
43 char *plug;
44- GVariant *newWrittenPlugins;
45- GVariantBuilder *newWrittenPluginsBuilder;
46- GVariantIter *iter;
47+ GVariantIter iter;
48 gboolean found = FALSE;
49
50 while (l)
51@@ -143,6 +169,8 @@
52 return settingsObj;
53 }
54
55+ g_free (name);
56+
57 l = g_list_next (l);
58 }
59
60@@ -162,26 +190,31 @@
61
62 writtenPlugins = g_settings_get_value (currentProfileSettings, "plugins-with-set-keys");
63
64- newWrittenPluginsBuilder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
65+ g_variant_iter_init (&iter, writtenPlugins);
66+ newWrittenPluginsSize = g_variant_iter_n_children (&iter);
67
68- iter = g_variant_iter_new (writtenPlugins);
69- while (g_variant_iter_loop (iter, "s", &plug))
70+ while (g_variant_iter_loop (&iter, "s", &plug))
71 {
72- g_variant_builder_add (newWrittenPluginsBuilder, "s", plug);
73-
74 if (!found)
75 found = (g_strcmp0 (plug, plugin) == 0);
76 }
77
78 if (!found)
79- g_variant_builder_add (newWrittenPluginsBuilder, "s", plugin);
80-
81- newWrittenPlugins = g_variant_new ("as", newWrittenPluginsBuilder);
82- g_settings_set_value (currentProfileSettings, "plugins-with-set-keys", newWrittenPlugins);
83-
84- g_variant_iter_free (iter);
85- g_variant_unref (newWrittenPlugins);
86- g_variant_builder_unref (newWrittenPluginsBuilder);
87+ newWrittenPluginsSize++;
88+
89+ newWrittenPlugins = g_variant_dup_strv (writtenPlugins, &writtenPluginsLen);
90+
91+ if (writtenPluginsLen > newWrittenPluginsSize)
92+ {
93+ newWrittenPlugins = g_realloc (newWrittenPlugins, (newWrittenPluginsSize + 1) * sizeof (gchar *));
94+ newWrittenPlugins[writtenPluginsLen + 1] = g_strdup (plugin);
95+ newWrittenPlugins[newWrittenPluginsSize] = NULL;
96+ }
97+
98+ g_settings_set_strv (currentProfileSettings, "plugins-with-set-keys", (const gchar * const *)newWrittenPlugins);
99+
100+ g_free (schemaName);
101+ g_strfreev (newWrittenPlugins);
102
103 return settingsObj;
104 }
105@@ -213,36 +246,52 @@
106 {
107 CCSContext *context = (CCSContext *)user_data;
108 char *uncleanKeyName;
109- char *path;
110+ char *path, *pathOrig;
111 char *token;
112 int index;
113 unsigned int screenNum;
114 CCSPlugin *plugin;
115 CCSSetting *setting;
116
117- g_object_get (G_OBJECT (settings), "path", &path, NULL);
118+ g_object_get (G_OBJECT (settings), "path", &pathOrig, NULL);
119
120+ path = pathOrig;
121 path += strlen (COMPIZ) + 1;
122
123 token = strsep (&path, "/"); /* Profile name */
124 if (!token)
125+ {
126+ g_free (pathOrig);
127 return;
128+ }
129
130 token = strsep (&path, "/"); /* plugins */
131 if (!token)
132+ {
133+ g_free (pathOrig);
134 return;
135+ }
136
137 token = strsep (&path, "/"); /* plugin */
138 if (!token)
139+ {
140+ g_free (pathOrig);
141 return;
142+ }
143
144 plugin = ccsFindPlugin (context, token);
145 if (!plugin)
146+ {
147+ g_free (pathOrig);
148 return;
149+ }
150
151 token = strsep (&path, "/"); /* screen%i */
152 if (!token)
153+ {
154+ g_free (pathOrig);
155 return;
156+ }
157
158 sscanf (token, "screen%d", &screenNum);
159
160@@ -253,6 +302,7 @@
161 {
162 printf ("GSettings Backend: unable to find setting %s, for path %s\n", uncleanKeyName, path);
163 free (uncleanKeyName);
164+ g_free (pathOrig);
165 return;
166 }
167
168@@ -270,42 +320,24 @@
169 }
170
171 free (uncleanKeyName);
172+ g_free (pathOrig);
173 }
174
175 static Bool
176 readListValue (CCSSetting *setting)
177 {
178 GSettings *settings = getSettingsObjectForCCSSetting (setting);
179- gchar *variantType;
180+ gboolean hasVariantType;
181 unsigned int nItems, i = 0;
182 CCSSettingValueList list = NULL;
183 GVariant *value;
184- GVariantIter *iter;
185+ GVariantIter iter;
186
187 char *cleanSettingName = translateKeyForGSettings (setting->name);
188
189- switch (setting->info.forList.listType)
190- {
191- case TypeString:
192- case TypeMatch:
193- case TypeColor:
194- variantType = g_strdup ("s");
195- break;
196- case TypeBool:
197- variantType = g_strdup ("b");
198- break;
199- case TypeInt:
200- variantType = g_strdup ("i");
201- break;
202- case TypeFloat:
203- variantType = g_strdup ("d");
204- break;
205- default:
206- variantType = NULL;
207- break;
208- }
209+ hasVariantType = compizconfigTypeHasVariantType (setting->info.forList.listType);
210
211- if (!variantType)
212+ if (!hasVariantType)
213 return FALSE;
214
215 value = g_settings_get_value (settings, cleanSettingName);
216@@ -315,8 +347,8 @@
217 return TRUE;
218 }
219
220- iter = g_variant_iter_new (value);
221- nItems = g_variant_iter_n_children (iter);
222+ g_variant_iter_init (&iter, value);
223+ nItems = g_variant_iter_n_children (&iter);
224
225 switch (setting->info.forList.listType)
226 {
227@@ -324,14 +356,14 @@
228 {
229 Bool *array = malloc (nItems * sizeof (Bool));
230 Bool *arrayCounter = array;
231+ gboolean value;
232
233 if (!array)
234 break;
235-
236- /* Reads each item from the variant into the position pointed
237- * at by arrayCounter */
238- while (g_variant_iter_loop (iter, variantType, arrayCounter))
239- arrayCounter++;
240+
241+ /* Reads each item from the variant into arrayCounter */
242+ while (g_variant_iter_loop (&iter, "b", &value))
243+ *arrayCounter++ = value;
244
245 list = ccsGetValueListFromBoolArray (array, nItems, setting);
246 free (array);
247@@ -341,14 +373,14 @@
248 {
249 int *array = malloc (nItems * sizeof (int));
250 int *arrayCounter = array;
251+ gint value;
252
253 if (!array)
254 break;
255-
256- /* Reads each item from the variant into the position pointed
257- * at by arrayCounter */
258- while (g_variant_iter_loop (iter, variantType, arrayCounter))
259- arrayCounter++;
260+
261+ /* Reads each item from the variant into arrayCounter */
262+ while (g_variant_iter_loop (&iter, "i", &value))
263+ *arrayCounter++ = value;
264
265 list = ccsGetValueListFromIntArray (array, nItems, setting);
266 free (array);
267@@ -358,14 +390,14 @@
268 {
269 double *array = malloc (nItems * sizeof (double));
270 double *arrayCounter = array;
271+ gdouble value;
272
273 if (!array)
274 break;
275-
276- /* Reads each item from the variant into the position pointed
277- * at by arrayCounter */
278- while (g_variant_iter_loop (iter, variantType, arrayCounter))
279- arrayCounter++;
280+
281+ /* Reads each item from the variant into arrayCounter */
282+ while (g_variant_iter_loop (&iter, "d", &value))
283+ *arrayCounter++ = value;
284
285 list = ccsGetValueListFromFloatArray ((float *) array, nItems, setting);
286 free (array);
287@@ -374,24 +406,21 @@
288 case TypeString:
289 case TypeMatch:
290 {
291- char **array = calloc (1, (nItems + 1) * sizeof (char *));
292- char **arrayCounter = array;
293+ gchar **array = g_malloc0 ((nItems + 1) * sizeof (gchar *));
294+ gchar **arrayCounter = array;
295+ gchar *value;
296
297 if (!array)
298- {
299 break;
300- }
301-
302- /* Reads each item from the variant into the position pointed
303- * at by arrayCounter */
304- while (g_variant_iter_loop (iter, variantType, arrayCounter))
305- arrayCounter++;
306+
307+ array[nItems] = NULL;
308+
309+ /* Reads each item from the variant into arrayCounter */
310+ while (g_variant_iter_next (&iter, "s", &value))
311+ *arrayCounter++ = value;
312
313 list = ccsGetValueListFromStringArray (array, nItems, setting);
314- for (i = 0; i < nItems; i++)
315- if (array[i])
316- free (array[i]);
317- free (array);
318+ g_strfreev (array);
319 }
320 break;
321 case TypeColor:
322@@ -402,7 +431,7 @@
323 if (!array)
324 break;
325
326- while (g_variant_iter_loop (iter, variantType, &colorValue))
327+ while (g_variant_iter_loop (&iter, "s", &colorValue))
328 {
329 memset (&array[i], 0, sizeof (CCSSettingColorValue));
330 ccsStringToColor (colorValue,
331@@ -417,7 +446,6 @@
332 }
333
334 free (cleanSettingName);
335- free (variantType);
336
337 if (list)
338 {
339@@ -638,8 +666,7 @@
340 char *pathName)
341 {
342 GSettings *settings = getSettingsObjectForCCSSetting (setting);
343- GVariant *value;
344- gchar *variantType = NULL;
345+ GVariant *value = NULL;
346 CCSSettingValueList list;
347
348 char *cleanSettingName = translateKeyForGSettings (setting->name);
349@@ -651,7 +678,6 @@
350 {
351 case TypeBool:
352 {
353- variantType = "ab";
354 GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("ab"));
355 while (list)
356 {
357@@ -664,7 +690,6 @@
358 break;
359 case TypeInt:
360 {
361- variantType = "ai";
362 GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("ai"));
363 while (list)
364 {
365@@ -677,7 +702,6 @@
366 break;
367 case TypeFloat:
368 {
369- variantType = "ad";
370 GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("ad"));
371 while (list)
372 {
373@@ -690,7 +714,6 @@
374 break;
375 case TypeString:
376 {
377- variantType = "as";
378 GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
379 while (list)
380 {
381@@ -703,7 +726,6 @@
382 break;
383 case TypeMatch:
384 {
385- variantType = "as";
386 GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
387 while (list)
388 {
389@@ -716,7 +738,6 @@
390 break;
391 case TypeColor:
392 {
393- variantType = "as";
394 GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
395 char *item;
396 while (list)
397@@ -732,11 +753,10 @@
398 default:
399 printf("GSettings backend: attempt to write unsupported list type %d!\n",
400 setting->info.forList.listType);
401- variantType = NULL;
402 break;
403 }
404
405- if (variantType != NULL)
406+ if (value)
407 {
408 g_settings_set_value (settings, cleanSettingName, value);
409 g_variant_unref (value);
410@@ -911,7 +931,7 @@
411 }
412
413 static void
414-updateCurrentProfileName (char *profile)
415+updateCurrentProfileName (const char *profile)
416 {
417 GVariant *profiles;
418 char *prof;
419@@ -919,15 +939,15 @@
420 char *currentProfilePath;
421 GVariant *newProfiles;
422 GVariantBuilder *newProfilesBuilder;
423- GVariantIter *iter;
424+ GVariantIter iter;
425 gboolean found = FALSE;
426
427 profiles = g_settings_get_value (compizconfigSettings, "existing-profiles");
428
429 newProfilesBuilder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
430
431- iter = g_variant_iter_new (profiles);
432- while (g_variant_iter_loop (iter, "s", &prof))
433+ g_variant_iter_init (&iter, profiles);
434+ while (g_variant_iter_loop (&iter, "s", &prof))
435 {
436 g_variant_builder_add (newProfilesBuilder, "s", prof);
437
438@@ -941,7 +961,6 @@
439 newProfiles = g_variant_new ("as", newProfilesBuilder);
440 g_settings_set_value (compizconfigSettings, "existing-profiles", newProfiles);
441
442- g_variant_iter_free (iter);
443 g_variant_unref (newProfiles);
444 g_variant_builder_unref (newProfilesBuilder);
445
446@@ -962,8 +981,14 @@
447 {
448 char *profile = strdup (ccsGetProfile (context));
449
450- if (!profile || !strlen (profile))
451- profile = strdup (DEFAULTPROF);
452+ if (!profile)
453+ profile = strdup (DEFAULTPROF);
454+
455+ if (!strlen (profile))
456+ {
457+ free (profile);
458+ profile = strdup (DEFAULTPROF);
459+ }
460
461 if (g_strcmp0 (profile, currentProfile))
462 updateCurrentProfileName (profile);
463@@ -1155,15 +1180,15 @@
464 GVariant *newProfiles;
465 GVariantBuilder *newProfilesBuilder;
466 char *plugin, *prof;
467- GVariantIter *iter;
468+ GVariantIter iter;
469 char *profileSettingsPath = g_strconcat (PROFILEPATH, profile, "/", NULL);
470 GSettings *profileSettings = g_settings_new_with_path (PROFILE_SCHEMA_ID, profileSettingsPath);
471
472 plugins = g_settings_get_value (currentProfileSettings, "plugins-with-set-keys");
473 profiles = g_settings_get_value (compizconfigSettings, "existing-profiles");
474
475- iter = g_variant_iter_new (plugins);
476- while (g_variant_iter_loop (iter, "s", &plugin))
477+ g_variant_iter_init (&iter, plugins);
478+ while (g_variant_iter_loop (&iter, "s", &plugin))
479 {
480 GSettings *settings;
481
482@@ -1189,13 +1214,12 @@
483 }
484
485 /* Remove the profile from existing-profiles */
486- g_variant_iter_free (iter);
487 g_settings_reset (profileSettings, "plugins-with-set-values");
488
489- iter = g_variant_iter_new (profiles);
490+ g_variant_iter_init (&iter, profiles);
491 newProfilesBuilder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
492
493- while (g_variant_iter_loop (iter, "s", &prof))
494+ while (g_variant_iter_loop (&iter, "s", &prof))
495 {
496 if (g_strcmp0 (prof, profile))
497 g_variant_builder_add (newProfilesBuilder, "s", prof);

Subscribers

People subscribed via source and target branches