CVE-2011-0991 (C, published 2011-04-13T21:55Z)
Description: value: Use-after-free vulnerability in Mono, when Moonlight 2.x before 2.4.1 or 3.x before 3.99.3 is used, allows remote attackers to cause a denial of service or possibly have unspecified other impact via vectors related to finalizing and then resurrecting a DynamicMethod instance.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-399 \u2014 Resource Management Errors
Weaknesses in this category are related to improper management of system resources.
Repository: https://github.com/mono/mono
Files changed:
mono/metadata/gc-internal.h
mono/metadata/gc.c
Commit message: Implement a reference queue API.

	* gc.c: A reference queue allows one to queue
	callbcks for when objects are collected.
	It allows for safe cleanup of objects that can
	only be done when it is effectively collected.
	The major difference with regular finalization
	is that the collector makes sure the object
	was collected - and can't be resurrected.

	* gc-internal.h: Export entrypoints for the
	new API.


Vulnerable code:

Fixed code:
typedef void (*mono_reference_queue_callback) (void *user_data);

typedef struct _MonoReferenceQueue MonoReferenceQueue;
typedef struct _RefQueueEntry RefQueueEntry;

struct _RefQueueEntry {
	void *dis_link;
	void *user_data;
	RefQueueEntry *next;
};

struct _MonoReferenceQueue {
	RefQueueEntry *queue;
	mono_reference_queue_callback callback;
	MonoReferenceQueue *next;
	gboolean should_be_deleted;
};

MonoReferenceQueue* mono_gc_reference_queue_new (mono_reference_queue_callback callback) MONO_INTERNAL;
void mono_gc_reference_queue_free (MonoReferenceQueue *queue) MONO_INTERNAL;
gboolean mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data) MONO_INTERNAL;


static CRITICAL_SECTION reference_queue_mutex;
static void reference_queue_proccess_all (void);
		reference_queue_proccess_all ();


	InitializeCriticalSection (&reference_queue_mutex);
	DeleteCriticalSection (&reference_queue_mutex);


static MonoReferenceQueue *ref_queues;

static void
ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
{
	do {
		/* Guard if head is changed concurrently. */
		while (*prev != element)
			prev = &(*prev)->next;
	} while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
}

static void
ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
{
	RefQueueEntry *current;
	do {
		current = *head;
		value->next = current;
	} while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
}

static void
reference_queue_proccess (MonoReferenceQueue *queue)
{
	RefQueueEntry **iter = &queue->queue;
	RefQueueEntry *entry;
	while ((entry = *iter)) {
		if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
			ref_list_remove_element (iter, entry);
			mono_gc_weak_link_remove (&entry->dis_link);
			queue->callback (entry->user_data);
			g_free (entry);
		} else {
			iter = &entry->next;
		}
	}
}

static void
reference_queue_proccess_all (void)
{
	MonoReferenceQueue **iter;
	MonoReferenceQueue *queue = ref_queues;
	for (; queue; queue = queue->next)
		reference_queue_proccess (queue);

restart:
	EnterCriticalSection (&reference_queue_mutex);
	for (iter = &ref_queues; *iter;) {
		queue = *iter;
		if (!queue->should_be_deleted) {
			iter = &queue->next;
			continue;
		}
		if (queue->queue) {
			LeaveCriticalSection (&reference_queue_mutex);
			reference_queue_proccess (queue);
			goto restart;
		}
		*iter = queue->next;
		g_free (queue);
	}
	LeaveCriticalSection (&reference_queue_mutex);
}

/**
 * mono_gc_reference_queue_new:
 * @callback callback used when processing dead entries.
 *
 * Create a new reference queue used to process collected objects.
 * A reference queue let you queue the pair (managed object, user data).
 * Once the managed object is collected @callback will be called
 * in the finalizer thread with 'user data' as argument.
 *
 * The callback is called without any locks held.
 */
MonoReferenceQueue*
mono_gc_reference_queue_new (mono_reference_queue_callback callback)
{
	MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
	res->callback = callback;

	EnterCriticalSection (&reference_queue_mutex);
	res->next = ref_queues;
	ref_queues = res;
	LeaveCriticalSection (&reference_queue_mutex);

	return res;
}

/**
 * mono_gc_reference_queue_add:
 * @queue the queue to add the reference to.
 * @obj the object to be watched for collection
 * @user_data parameter to be passed to the queue callback
 *
 * Queue an object to be watched for collection.
 *
 * @returns false if the queue is scheduled to be freed.
 */
gboolean
mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
{
	RefQueueEntry *head;
	RefQueueEntry *entry;
	if (queue->should_be_deleted)
		return FALSE;

	entry = g_new0 (RefQueueEntry, 1);
	entry->user_data = user_data;
	mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
	ref_list_push (&queue->queue, entry);
	return TRUE;
}

/**
 * mono_gc_reference_queue_free:
 * @queue the queue that should be deleted.
 *
 * This operation signals that @queue should be deleted. This operation is deferred
 * as it happens on the finalizer thread.
 *
 * After this call, no further objects can be queued. It's the responsibility of the
 * caller to make sure that no further attempt to access queue will be made.
 */
void
mono_gc_reference_queue_free (MonoReferenceQueue *queue)
{
	queue->should_be_deleted = TRUE;
}