From 921593b022491ce3dbd61739cc9808a715f25b9f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 24 Feb 2013 22:54:09 +0100 Subject: [PATCH] GList: be more robust We can detect list corruption in some cases. The new test case demonstrates a case where we can warn instead of silently corrupt the list. This was pointed out by Steve Grubb. Also, use the same auxiliary routine in all places where we unlink a list element. --- glib/glist.c | 69 ++++++++++++++++++++++++++++--------------------------- glib/tests/list.c | 27 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/glib/glist.c b/glib/glist.c index f4905f3..620b58d 100644 --- a/glib/glist.c +++ b/glib/glist.c @@ -32,6 +32,7 @@ #include "glist.h" #include "gslice.h" +#include "gmessages.h" #include "gtestutils.h" @@ -420,6 +421,34 @@ g_list_concat (GList *list1, GList *list2) return list1; } +static inline GList* +_g_list_remove_link (GList *list, + GList *link) +{ + if (link->prev) + { + if (link->prev->next == link) + link->prev->next = link->next; + else + g_warning ("corrupted double-linked list detected"); + } + if (link->next) + { + if (link->next->prev == link) + link->next->prev = link->prev; + else + g_warning ("corrupted double-linked list detected"); + } + + if (link == list) + list = list->next; + + link->next = NULL; + link->prev = NULL; + + return list; +} + /** * g_list_remove: * @list: a #GList @@ -436,7 +465,7 @@ g_list_remove (GList *list, gconstpointer data) { GList *tmp; - + tmp = list; while (tmp) { @@ -444,16 +473,9 @@ g_list_remove (GList *list, tmp = tmp->next; else { - if (tmp->prev) - tmp->prev->next = tmp->next; - if (tmp->next) - tmp->next->prev = tmp->prev; - - if (list == tmp) - list = list->next; - + list = _g_list_remove_link (list, tmp); _g_list_free1 (tmp); - + break; } } @@ -465,9 +487,9 @@ g_list_remove (GList *list, * @list: a #GList * @data: data to remove * - * Removes all list nodes with data equal to @data. - * Returns the new head of the list. Contrast with - * g_list_remove() which removes only the first node + * Removes all list nodes with data equal to @data. + * Returns the new head of the list. Contrast with + * g_list_remove() which removes only the first node * matching the given data. * * Returns: new head of @list @@ -500,27 +522,6 @@ g_list_remove_all (GList *list, return list; } -static inline GList* -_g_list_remove_link (GList *list, - GList *link) -{ - if (link) - { - if (link->prev) - link->prev->next = link->next; - if (link->next) - link->next->prev = link->prev; - - if (link == list) - list = list->next; - - link->next = NULL; - link->prev = NULL; - } - - return list; -} - /** * g_list_remove_link: * @list: a #GList diff --git a/glib/tests/list.c b/glib/tests/list.c index 7241784..10d5236 100644 --- a/glib/tests/list.c +++ b/glib/tests/list.c @@ -1,4 +1,5 @@ #include +#include #define SIZE 50 #define NUMBER_MIN 0000 @@ -488,6 +489,31 @@ test_position (void) g_list_free (ll); } +static void +test_double_free (void) +{ + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + { + GList *list, *link; + GList intruder = { NULL, (gpointer)0xDEADBEEF, (gpointer)0xDEADBEEF }; + + list = NULL; + list = g_list_append (list, "a"); + link = list = g_list_append (list, "b"); + list = g_list_append (list, "c"); + + list = g_list_remove_link (list, link); + link->prev = list; + link->next = &intruder; + list = g_list_remove_link (list, link); + + g_list_free (list); + exit (0); + } + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*corrupted double-linked list detected*"); +} + int main (int argc, char *argv[]) { @@ -516,6 +542,7 @@ main (int argc, char *argv[]) g_test_add_func ("/list/delete-link", test_delete_link); g_test_add_func ("/list/prepend", test_prepend); g_test_add_func ("/list/position", test_position); + g_test_add_func ("/list/double-free", test_double_free); return g_test_run (); } -- 2.7.4