Fix double free issue 61/116361/4
authorHwankyu Jhun <h.jhun@samsung.com>
Fri, 24 Feb 2017 03:38:11 +0000 (12:38 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Fri, 24 Feb 2017 08:39:00 +0000 (17:39 +0900)
When destroying the hash table, the application has crashed by the
language list. Some nodes of the list are released when the language
is found in the __append_langs function. If the node is released,
we should replace the value of the hash table.

Change-Id: I926d52850b2ac596d777bbc05ed2f947855f9041
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/appcore-i18n.c
src/base/appcore_base.c

index 1003ea8..572bdd8 100644 (file)
 
 #include "appcore-internal.h"
 
+struct lang_info_s {
+       char *parent;
+       GList *list;
+};
+
 static int _set;
 static char locale_dir[PATH_MAX];
 
-static void __free_children_langs(gpointer data)
+static void __destroy_lang_info(gpointer data)
 {
-       GList *list = (GList *)data;
+       struct lang_info_s *info = (struct lang_info_s *)data;
 
-       if (list == NULL)
+       if (info == NULL)
                return;
 
-       g_list_free_full(list, (GDestroyNotify)free);
+       if (info->list)
+               g_list_free_full(info->list, free);
+       if (info->parent)
+               free(info->parent);
+       free(info);
+}
+
+static struct lang_info_s *__create_lang_info(const char *lang)
+{
+       struct lang_info_s *info;
+
+       info = calloc(1, sizeof(struct lang_info_s));
+       if (info == NULL) {
+               _ERR("Out of memory");
+               return NULL;
+       }
+
+       info->parent = strdup(lang);
+       if (info->parent == NULL) {
+               _ERR("Out of memory");
+               free(info);
+               return NULL;
+       }
+
+       return info;
 }
 
 static gint __compare_langs(gconstpointer a, gconstpointer b)
 {
+       if (!a || !b)
+               return -1;
+
        return strcmp(a, b);
 }
 
+static char *__get_string_before(const char *str, const char *delim)
+{
+       char *new_str;
+       char *dup_str;
+       char *token;
+
+       dup_str = strdup(str);
+       if (dup_str == NULL)
+               return NULL;
+
+       token = strtok(dup_str, delim);
+       if (token == NULL) {
+               free(dup_str);
+               return NULL;
+       }
+
+       new_str = strdup(token);
+       free(dup_str);
+
+       return new_str;
+}
+
 static GHashTable *__get_lang_table(void)
 {
        GHashTable *table;
@@ -59,15 +113,14 @@ static GHashTable *__get_lang_table(void)
        char buf[PATH_MAX];
        struct stat stat_buf;
        int ret;
-       char *dup_lang;
-       char *token;
-       GList *list;
+       char *parent_lang;
+       struct lang_info_s *info;
 
        if (locale_dir[0] == 0 || locale_dir[0] == '\0')
                return NULL;
 
        table = g_hash_table_new_full(g_str_hash, g_str_equal,
-                       free, __free_children_langs);
+                       NULL, __destroy_lang_info);
        if (table == NULL) {
                _ERR("Out of memory");
                return NULL;
@@ -89,128 +142,84 @@ static GHashTable *__get_lang_table(void)
                if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
                        continue;
 
-               dup_lang = strdup(dentry->d_name);
-               if (dup_lang == NULL) {
+               parent_lang = __get_string_before(dentry->d_name, "_");
+               if (parent_lang == NULL) {
                        _ERR("Out of memory");
                        break;
                }
 
-               token = strtok(dup_lang, "_");
-               if (token == NULL) {
-                       free(dup_lang);
-                       continue;
-               }
-
-               list = (GList *)g_hash_table_lookup(table, token);
-               if (list == NULL) {
-                       list = g_list_append(list, strdup(dentry->d_name));
-                       g_hash_table_insert(table, strdup(token), list);
-               } else {
-                       list = g_list_append(list, strdup(dentry->d_name));
+               info = g_hash_table_lookup(table, parent_lang);
+               if (info == NULL) {
+                       info = __create_lang_info(parent_lang);
+                       if (info == NULL) {
+                               free(parent_lang);
+                               break;
+                       }
+                       g_hash_table_insert(table, info->parent, info);
                }
-               free(dup_lang);
+               info->list = g_list_append(info->list, strdup(dentry->d_name));
+               free(parent_lang);
        }
        closedir(dp);
 
        return table;
 }
 
-static char *__get_string_before(const char *str, const char *delim)
-{
-       char *new_str;
-       char *dup_str;
-       char *token;
-
-       dup_str = strdup(str);
-       if (dup_str == NULL)
-               return NULL;
-
-       token = strtok(dup_str, delim);
-       if (token == NULL) {
-               free(dup_str);
-               return NULL;
-       }
-
-       new_str = strdup(token);
-       free(dup_str);
-
-       return new_str;
-}
-
 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
 {
-       GList *child_list;
-       GList *child_iter;
+       struct lang_info_s *info;
        GList *found;
-       char *child_lang;
-       char *parent_lang;
+       char *parent_lang = NULL;
        char *extract_lang;
-       char *tmp;
 
        if (lang == NULL)
                return list;
 
-       found = g_list_find_custom(g_list_first(list), lang,
-                       __compare_langs);
-       if (found) {
-               tmp = (char *)found->data;
-               list = g_list_remove(list, tmp);
-               list = g_list_append(list, tmp);
-               return list;
-       }
-
        extract_lang = __get_string_before(lang, ".");
        if (extract_lang == NULL)
                return list;
 
-       parent_lang = __get_string_before(extract_lang, "_");
-       if (parent_lang == NULL) {
-               free(extract_lang);
-               return list;
+       found = g_list_find_custom(list, extract_lang, __compare_langs);
+       if (found) {
+               list = g_list_remove_link(list, found);
+               list = g_list_concat(list, found);
+               goto end;
        }
 
-       child_list = g_hash_table_lookup(table, parent_lang);
-       if (child_list == NULL) {
-               free(parent_lang);
-               free(extract_lang);
-               return list;
-       }
+       parent_lang = __get_string_before(extract_lang, "_");
+       if (parent_lang == NULL)
+               goto end;
+
+       info = g_hash_table_lookup(table, parent_lang);
+       if (info == NULL)
+               goto end;
 
-       found = g_list_find_custom(g_list_first(child_list),
-                       extract_lang, __compare_langs);
+       found = g_list_find_custom(info->list, extract_lang, __compare_langs);
        if (found) {
-               tmp = (char *)found->data;
-               child_list = g_list_remove(child_list, tmp);
-               list = g_list_append(list, tmp);
-               free(parent_lang);
-               free(extract_lang);
-               return list;
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+               goto end;
        }
-       free(extract_lang);
 
-       found = g_list_find_custom(g_list_first(child_list),
-                       parent_lang, __compare_langs);
+       found = g_list_find_custom(info->list, parent_lang, __compare_langs);
        if (found) {
-               tmp = (char *)found->data;
-               child_list = g_list_remove(child_list, tmp);
-               list = g_list_append(list, tmp);
-               free(parent_lang);
-               return list;
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+               goto end;
        }
-       free(parent_lang);
-
-       child_iter = g_list_first(child_list);
-       while (child_iter) {
-               child_lang = (char *)child_iter->data;
-               child_iter = g_list_next(child_iter);
-               if (child_lang) {
-                       list = g_list_append(list, strdup(child_lang));
-                       child_list = g_list_remove(child_list, child_lang);
-                       free(child_lang);
-                       break;
-               }
+
+       found = g_list_first(info->list);
+       if (found) {
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
        }
 
+end:
+       if (extract_lang)
+               free(extract_lang);
+       if (parent_lang)
+               free(parent_lang);
+
        return list;
 }
 
@@ -243,8 +252,7 @@ static GList *__append_default_langs(GList *list)
        GList *found;
 
        for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
-               found = g_list_find_custom(g_list_first(list), langs[i],
-                               __compare_langs);
+               found = g_list_find_custom(list, langs[i], __compare_langs);
                if (found == NULL)
                        list = g_list_append(list, strdup(langs[i]));
        }
index 7a15b33..305c065 100644 (file)
@@ -60,6 +60,11 @@ typedef struct _appcore_base_event_node {
        void *data;
 } appcore_base_event_node;
 
+struct lang_info_s {
+       char *parent;
+       GList *list;
+};
+
 static appcore_base_context __context;
 static GList *__events;
 static GDBusConnection *__bus;
@@ -117,21 +122,70 @@ static void __on_low_battery(keynode_t *key, void *data)
                __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY);
 }
 
-static void __free_children_langs(gpointer data)
+static void __destroy_lang_info(gpointer data)
 {
-       GList *list = (GList *)data;
+       struct lang_info_s *info = (struct lang_info_s *)data;
 
-       if (list == NULL)
+       if (info == NULL)
                return;
 
-       g_list_free_full(list, (GDestroyNotify)free);
+       if (info->list)
+               g_list_free_full(info->list, free);
+       if (info->parent)
+               free(info->parent);
+       free(info);
+}
+
+static struct lang_info_s *__create_lang_info(const char *lang)
+{
+       struct lang_info_s *info;
+
+       info = calloc(1, sizeof(struct lang_info_s));
+       if (info == NULL) {
+               _ERR("Out of memory");
+               return NULL;
+       }
+
+       info->parent = strdup(lang);
+       if (info->parent == NULL) {
+               _ERR("Out of memory");
+               free(info);
+               return NULL;
+       }
+
+       return info;
 }
 
 static gint __compare_langs(gconstpointer a, gconstpointer b)
 {
+       if (!a || !b)
+               return -1;
+
        return strcmp(a, b);
 }
 
+static char *__get_string_before(const char *str, const char *delim)
+{
+       char *new_str;
+       char *dup_str;
+       char *token;
+
+       dup_str = strdup(str);
+       if (dup_str == NULL)
+               return NULL;
+
+       token = strtok(dup_str, delim);
+       if (token == NULL) {
+               free(dup_str);
+               return NULL;
+       }
+
+       new_str = strdup(token);
+       free(dup_str);
+
+       return new_str;
+}
+
 static GHashTable *__get_lang_table(void)
 {
        GHashTable *table;
@@ -140,15 +194,14 @@ static GHashTable *__get_lang_table(void)
        char buf[PATH_MAX];
        struct stat stat_buf;
        int ret;
-       char *dup_lang;
-       char *token;
-       GList *list;
+       char *parent_lang;
+       struct lang_info_s *info;
 
        if (__locale_dir == NULL || __locale_dir[0] == '\0')
                return NULL;
 
        table = g_hash_table_new_full(g_str_hash, g_str_equal,
-                       free, __free_children_langs);
+                       NULL, __destroy_lang_info);
        if (table == NULL) {
                _ERR("Out of memory");
                return NULL;
@@ -171,128 +224,84 @@ static GHashTable *__get_lang_table(void)
                if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
                        continue;
 
-               dup_lang = strdup(dentry->d_name);
-               if (dup_lang == NULL) {
+               parent_lang = __get_string_before(dentry->d_name, "_");
+               if (parent_lang == NULL) {
                        _ERR("Out of memory");
                        break;
                }
 
-               token = strtok(dup_lang, "_");
-               if (token == NULL) {
-                       free(dup_lang);
-                       continue;
-               }
-
-               list = (GList *)g_hash_table_lookup(table, token);
-               if (list == NULL) {
-                       list = g_list_append(list, strdup(dentry->d_name));
-                       g_hash_table_insert(table, strdup(token), list);
-               } else {
-                       list = g_list_append(list, strdup(dentry->d_name));
+               info = g_hash_table_lookup(table, parent_lang);
+               if (info == NULL) {
+                       info = __create_lang_info(parent_lang);
+                       if (info == NULL) {
+                               free(parent_lang);
+                               break;
+                       }
+                       g_hash_table_insert(table, info->parent, info);
                }
-               free(dup_lang);
+               info->list = g_list_append(info->list, strdup(dentry->d_name));
+               free(parent_lang);
        }
        closedir(dp);
 
        return table;
 }
 
-static char *__get_string_before(const char *str, const char *delim)
-{
-       char *new_str;
-       char *dup_str;
-       char *token;
-
-       dup_str = strdup(str);
-       if (dup_str == NULL)
-               return NULL;
-
-       token = strtok(dup_str, delim);
-       if (token == NULL) {
-               free(dup_str);
-               return NULL;
-       }
-
-       new_str = strdup(token);
-       free(dup_str);
-
-       return new_str;
-}
-
 static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
 {
-       GList *child_list;
-       GList *child_iter;
+       struct lang_info_s *info;
        GList *found;
-       char *child_lang;
-       char *parent_lang;
+       char *parent_lang = NULL;
        char *extract_lang;
-       char *tmp;
 
        if (lang == NULL)
                return list;
 
-       found = g_list_find_custom(g_list_first(list), lang,
-                       __compare_langs);
-       if (found) {
-               tmp = (char *)found->data;
-               list = g_list_remove(list, tmp);
-               list = g_list_append(list, tmp);
-               return list;
-       }
-
        extract_lang = __get_string_before(lang, ".");
        if (extract_lang == NULL)
                return list;
 
-       parent_lang = __get_string_before(extract_lang, "_");
-       if (parent_lang == NULL) {
-               free(extract_lang);
-               return list;
+       found = g_list_find_custom(list, extract_lang, __compare_langs);
+       if (found) {
+               list = g_list_remove_link(list, found);
+               list = g_list_concat(list, found);
+               goto end;
        }
 
-       child_list = g_hash_table_lookup(table, parent_lang);
-       if (child_list == NULL) {
-               free(parent_lang);
-               free(extract_lang);
-               return list;
-       }
+       parent_lang = __get_string_before(extract_lang, "_");
+       if (parent_lang == NULL)
+               goto end;
+
+       info = g_hash_table_lookup(table, parent_lang);
+       if (info == NULL)
+               goto end;
 
-       found = g_list_find_custom(g_list_first(child_list),
-                       extract_lang, __compare_langs);
+       found = g_list_find_custom(info->list, extract_lang, __compare_langs);
        if (found) {
-               tmp = (char *)found->data;
-               child_list = g_list_remove(child_list, tmp);
-               list = g_list_append(list, tmp);
-               free(parent_lang);
-               free(extract_lang);
-               return list;
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+               goto end;
        }
-       free(extract_lang);
 
-       found = g_list_find_custom(g_list_first(child_list),
-                       parent_lang, __compare_langs);
+       found = g_list_find_custom(info->list, parent_lang, __compare_langs);
        if (found) {
-               tmp = (char *)found->data;
-               child_list = g_list_remove(child_list, tmp);
-               list = g_list_append(list, tmp);
-               free(parent_lang);
-               return list;
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+               goto end;
        }
-       free(parent_lang);
-
-       child_iter = g_list_first(child_list);
-       while (child_iter) {
-               child_lang = (char *)child_iter->data;
-               child_iter = g_list_next(child_iter);
-               if (child_lang) {
-                       list = g_list_append(list, strdup(child_lang));
-                       child_list = g_list_remove(child_list, child_lang);
-                       free(child_lang);
-                       break;
-               }
+
+       found = g_list_first(info->list);
+       if (found) {
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
        }
 
+end:
+       if (extract_lang)
+               free(extract_lang);
+       if (parent_lang)
+               free(parent_lang);
+
        return list;
 }
 
@@ -325,8 +334,7 @@ static GList *__append_default_langs(GList *list)
        GList *found;
 
        for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
-               found = g_list_find_custom(g_list_first(list), langs[i],
-                               __compare_langs);
+               found = g_list_find_custom(list, langs[i], __compare_langs);
                if (found == NULL)
                        list = g_list_append(list, strdup(langs[i]));
        }