DragonFlyBSD Kernel Audit
DF-2804 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/kern_udev.c
+++ b/sys/kern/kern_udev.c
@@ -534,11 +534,26 @@ udev_event_insert(int ev_type, prop_dictionary_t dict)
 static void
 udev_clean_events_locked(void)
 {
 	struct udev_event_kernel *ev;
 
-	while ((ev = TAILQ_FIRST(&udev_evq)) &&
-	       ev->ev.ev_dict != NULL) {
+	/*
+	 * Reap events that every reader has passed (markers have
+	 * ev_dict == NULL and are skipped).  The event's single
+	 * reference on ev_dict is released HERE and nowhere else.
+	 *
+	 * When the last reader is gone (udev_initiated_count == 0) no
+	 * marker can remain in the queue, so drain everything --
+	 * otherwise undelivered events leak their dictionaries forever.
+	 */
+	while ((ev = TAILQ_FIRST(&udev_evq)) != NULL &&
+	       (udev_initiated_count == 0 || ev->ev.ev_dict != NULL)) {
+		if (ev->ev.ev_dict != NULL) {
+			prop_object_release(ev->ev.ev_dict);
+			ev->ev.ev_dict = NULL;
+		}
 		TAILQ_REMOVE(&udev_evq, ev, link);
 		objcache_put(udev_event_kernel_cache, ev);
 		--udev_evqlen;
 	}
 }
@@ -566,7 +581,6 @@ udev_event_externalize(struct udev_event_kernel *ev)
 	if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
 		prop_object_release(dict);
 		return NULL;
 	}
-	prop_object_release(ev->ev.ev_dict);
 
 	xml = prop_dictionary_externalize(dict);