Skip to content

Potential Memory Leak in Min-Heap of Timer Events #1800

@yanyongcheng

Description

@yanyongcheng

Libevent's timer min-heap only supports one-way expansion and does not support contraction. Therefore, after adding many timer events for a period of time and then deleting these events, the memory of the min-heap remains in an expanded state.

The below function will allocate sufficient memory:

int min_heap_reserve_(min_heap_t* s, unsigned n)
{
	if (s->a < n)
	{
		struct event** p;
		unsigned a = s->a ? s->a * 2 : 8;
		if (a < n)
			a = n;

		if (!(p = (struct event**)mm_realloc(s->p, a * sizeof *p)))
			return -1;
		s->p = p;
		s->a = a;
	}
	return 0;
}

but, the function min_heap_erase_ only sets the index of the removed event to -1,

int min_heap_erase_(min_heap_t* s, struct event* e)
{
	if (-1 != e->ev_timeout_pos.min_heap_idx)
	{
		struct event *last = s->p[--s->n];
		unsigned parent = (e->ev_timeout_pos.min_heap_idx - 1) / 2;

		if (e->ev_timeout_pos.min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last))
			min_heap_shift_up_unconditional_(s, e->ev_timeout_pos.min_heap_idx, last);
		else
			min_heap_shift_down_(s, e->ev_timeout_pos.min_heap_idx, last);
		e->ev_timeout_pos.min_heap_idx = -1;
		return 0;
	}
	return -1;
}

Is this design intentional for a specific purpose? Where is the expanded memory released? Otherwise, if more and more timers keep getting added and then removed, wouldn't the memory usage keep growing? Unless the entire base event loop is released, that memory won't be fully cleaned up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions