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.
This commit is contained in:
Matthias Clasen 2013-02-24 22:54:09 +01:00
parent 6833267a07
commit 921593b022
2 changed files with 62 additions and 34 deletions

View File

@ -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

View File

@ -1,4 +1,5 @@
#include <glib.h>
#include <stdlib.h>
#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 ();
}