changeset 19533:06825c099fa1

dict: Avoid a crash when dict connection is already closed when async lookup finishes.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 04 Jan 2016 20:51:34 +0200
parents 4cd83ea1a420
children 25e892203e2b
files src/dict/dict-commands.c src/dict/dict-connection.c src/dict/dict-connection.h
diffstat 3 files changed, 28 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/dict/dict-commands.c	Mon Jan 04 13:12:18 2016 -0500
+++ b/src/dict/dict-commands.c	Mon Jan 04 20:51:34 2016 +0200
@@ -75,7 +75,7 @@
 		o_stream_nsend_str(conn->output, cmd->reply);
 		dict_connection_cmd_remove(cmd);
 	}
-	dict_connection_unref(conn);
+	dict_connection_unref_safe(conn);
 }
 
 static void
--- a/src/dict/dict-connection.c	Mon Jan 04 13:12:18 2016 -0500
+++ b/src/dict/dict-connection.c	Mon Jan 04 20:51:34 2016 +0200
@@ -238,8 +238,33 @@
 	return FALSE;
 }
 
+static void dict_connection_unref_safe_callback(struct dict_connection *conn)
+{
+	if (conn->to_unref != NULL)
+		timeout_remove(&conn->to_unref);
+	(void)dict_connection_unref(conn);
+}
+
+void dict_connection_unref_safe(struct dict_connection *conn)
+{
+	if (conn->refcount == 1) {
+		/* delayed unref to make sure we don't try to call
+		   dict_deinit() from a dict-callback. that's too much trouble
+		   for each dict driver to be able to handle. */
+		if (conn->to_unref == NULL) {
+			conn->to_unref = timeout_add_short(0,
+				dict_connection_unref_safe_callback, conn);
+		}
+	} else {
+		(void)dict_connection_unref(conn);
+	}
+}
+
 void dict_connection_destroy(struct dict_connection *conn)
 {
+	i_assert(!conn->destroyed);
+	i_assert(conn->to_unref == NULL);
+
 	conn->destroyed = TRUE;
 	DLLIST_REMOVE(&dict_connections, conn);
 
--- a/src/dict/dict-connection.h	Mon Jan 04 13:12:18 2016 -0500
+++ b/src/dict/dict-connection.h	Mon Jan 04 20:51:34 2016 +0200
@@ -24,6 +24,7 @@
 	struct istream *input;
 	struct ostream *output;
 	struct timeout *to_input;
+	struct timeout *to_unref;
 
 	/* There are only a few transactions per client, so keeping them in
 	   array is fast enough */
@@ -38,6 +39,7 @@
 
 void dict_connection_ref(struct dict_connection *conn);
 bool dict_connection_unref(struct dict_connection *conn);
+void dict_connection_unref_safe(struct dict_connection *conn);
 
 void dict_connection_continue_input(struct dict_connection *conn);