mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-23 05:45:36 +00:00
qapi: Fix memleak in string visitors on int lists
Commit 7f8f9ef1 introduced the ability to store a list of integers as a sorted list of ranges, but when merging ranges, it leaks one or more ranges. It was also using range_get_last() incorrectly within range_compare() (a range is a start/end pair, but range_get_last() is for start/len pairs), and will also mishandle a range ending in UINT64_MAX (remember, we document that no range covers 2**64 bytes, but that ranges that end on UINT64_MAX have end < begin). The whole merge algorithm was rather complex, and included unnecessary passes over data within glib functions, and enough indirection to make it hard to easily plug the data leaks. Since we are already hard-coding things to a list of ranges, just rewrite the thing to open-code the traversal and comparisons, by making the range_compare() helper function give us an answer that is easier to use, at which point we avoid the need to pass any callbacks to g_list_*(). Then by reusing range_extend() instead of duplicating effort with range_merge(), we cover the corner cases correctly. Drop the now-unused range_merge() and ranges_can_merge(). Doing this lets test-string-{input,output}-visitor pass under valgrind without leaks. Backports commit db486cc334aafd3dbdaf107388e37fc3d6d3e171 from qemu
This commit is contained in:
parent
ef357d06bc
commit
c14d8226ab
|
@ -283,6 +283,107 @@ GList *g_list_sort (GList *list, GCompareFunc compare_func)
|
|||
return g_list_sort_real (list, (GFunc) compare_func, NULL);
|
||||
}
|
||||
|
||||
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_delete_link:
|
||||
* @list: a #GList, this must point to the top of the list
|
||||
* @link_: node to delete from @list
|
||||
*
|
||||
* Removes the node link_ from the list and frees it.
|
||||
* Compare this to g_list_remove_link() which removes the node
|
||||
* without freeing it.
|
||||
*
|
||||
* Returns: the (possibly changed) start of the #GList
|
||||
*/
|
||||
GList *
|
||||
g_list_delete_link (GList *list,
|
||||
GList *link_)
|
||||
{
|
||||
list = _g_list_remove_link (list, link_);
|
||||
//_g_list_free1 (link_);
|
||||
g_free (link_);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_list_insert_before:
|
||||
* @list: a pointer to a #GList
|
||||
* @sibling: the list element before which the new element
|
||||
* is inserted or %NULL to insert at the end of the list
|
||||
* @data: the data for the new element
|
||||
*
|
||||
* Inserts a new element into the list before the given position.
|
||||
*
|
||||
* Returns: the new start of the #GList
|
||||
*/
|
||||
GList*
|
||||
g_list_insert_before (GList *list,
|
||||
GList *sibling,
|
||||
gpointer data)
|
||||
{
|
||||
if (!list)
|
||||
{
|
||||
list = g_malloc(sizeof(GList));
|
||||
list->data = data;
|
||||
return list;
|
||||
}
|
||||
else if (sibling)
|
||||
{
|
||||
GList *node;
|
||||
|
||||
node = g_malloc(sizeof(GList));
|
||||
node->data = data;
|
||||
node->prev = sibling->prev;
|
||||
node->next = sibling;
|
||||
sibling->prev = node;
|
||||
if (node->prev)
|
||||
{
|
||||
node->prev->next = node;
|
||||
return list;
|
||||
}
|
||||
else
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GList *last;
|
||||
|
||||
last = list;
|
||||
while (last->next)
|
||||
last = last->next;
|
||||
|
||||
last->next = g_malloc(sizeof(GList));
|
||||
last->next->data = data;
|
||||
last->next->prev = last;
|
||||
last->next->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/* END of g_list related functions */
|
||||
|
||||
/* Singly-linked list */
|
||||
|
|
|
@ -71,10 +71,12 @@ typedef struct _GList {
|
|||
GList *g_list_first(GList *list);
|
||||
void g_list_foreach(GList *list, GFunc func, gpointer user_data);
|
||||
void g_list_free(GList *list);
|
||||
GList* g_list_insert_before(GList *list, GList *sibling, gpointer data);
|
||||
GList *g_list_insert_sorted(GList *list, gpointer data, GCompareFunc compare);
|
||||
#define g_list_next(list) (list->next)
|
||||
GList *g_list_prepend(GList *list, gpointer data);
|
||||
GList *g_list_remove_link(GList *list, GList *llink);
|
||||
GList *g_list_delete_link (GList *list, GList *link_);
|
||||
GList *g_list_sort(GList *list, GCompareFunc compare);
|
||||
|
||||
typedef struct _GSList {
|
||||
|
|
|
@ -28,65 +28,48 @@
|
|||
* - this can not represent a full 0 to ~0x0LL range.
|
||||
*/
|
||||
|
||||
/* 0,1 can merge with 1,2 but don't overlap */
|
||||
static bool ranges_can_merge(Range *range1, Range *range2)
|
||||
/* Return -1 if @a < @b, 1 if greater, and 0 if they touch or overlap. */
|
||||
static inline int range_compare(Range *a, Range *b)
|
||||
{
|
||||
return !(range1->end < range2->begin || range2->end < range1->begin);
|
||||
}
|
||||
|
||||
static void range_merge(Range *range1, Range *range2)
|
||||
{
|
||||
if (range1->end < range2->end) {
|
||||
range1->end = range2->end;
|
||||
}
|
||||
if (range1->begin > range2->begin) {
|
||||
range1->begin = range2->begin;
|
||||
}
|
||||
}
|
||||
|
||||
static gint range_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
Range *ra = (Range *)a, *rb = (Range *)b;
|
||||
if (ra->begin == rb->begin && ra->end == rb->end) {
|
||||
return 0;
|
||||
} else if (range_get_last(ra->begin, ra->end) <
|
||||
range_get_last(rb->begin, rb->end)) {
|
||||
/* Zero a->end is 2**64, and therefore not less than any b->begin */
|
||||
if (a->end && a->end < b->begin) {
|
||||
return -1;
|
||||
} else {
|
||||
}
|
||||
if (b->end && a->begin > b->end) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert @data into @list of ranges; caller no longer owns @data */
|
||||
GList *range_list_insert(GList *list, Range *data)
|
||||
{
|
||||
GList *l, *next = NULL;
|
||||
Range *r, *nextr;
|
||||
GList *l;
|
||||
|
||||
if (!list) {
|
||||
list = g_list_insert_sorted(list, data, range_compare);
|
||||
return list;
|
||||
/* Range lists require no empty ranges */
|
||||
assert(data->begin < data->end || (data->begin && !data->end));
|
||||
|
||||
/* Skip all list elements strictly less than data */
|
||||
for (l = list; l && range_compare(l->data, data) < 0; l = l->next) {
|
||||
}
|
||||
|
||||
nextr = data;
|
||||
l = list;
|
||||
while (l && l != next && nextr) {
|
||||
r = l->data;
|
||||
if (ranges_can_merge(r, nextr)) {
|
||||
range_merge(r, nextr);
|
||||
l = g_list_remove_link(l, next);
|
||||
next = g_list_next(l);
|
||||
if (next) {
|
||||
nextr = next->data;
|
||||
} else {
|
||||
nextr = NULL;
|
||||
}
|
||||
} else {
|
||||
l = g_list_next(l);
|
||||
}
|
||||
if (!l || range_compare(l->data, data) > 0) {
|
||||
/* Rest of the list (if any) is strictly greater than @data */
|
||||
return g_list_insert_before(list, l, data);
|
||||
}
|
||||
|
||||
if (!l) {
|
||||
list = g_list_insert_sorted(list, data, range_compare);
|
||||
/* Current list element overlaps @data, merge the two */
|
||||
range_extend(l->data, data);
|
||||
g_free(data);
|
||||
|
||||
/* Merge any subsequent list elements that now also overlap */
|
||||
while (l->next && range_compare(l->data, l->next->data) == 0) {
|
||||
GList *new_l;
|
||||
|
||||
range_extend(l->data, l->next->data);
|
||||
g_free(l->next->data);
|
||||
new_l = g_list_delete_link(list, l->next);
|
||||
assert(new_l == list);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
Loading…
Reference in a new issue