dovecot-2.2: auth: Use refcounting for LDAPMessage to make sure ...

dovecot at dovecot.org dovecot at dovecot.org
Wed Dec 11 18:39:52 EET 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/d9cf369a8b6d
changeset: 17057:d9cf369a8b6d
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Dec 11 18:39:36 2013 +0200
description:
auth: Use refcounting for LDAPMessage to make sure it always gets freed correctly.
This may fix some memory leaks in some (error?) cases.

diffstat:

 src/auth/db-ldap.c |  95 +++++++++++++++++++++++++++++++----------------------
 src/auth/db-ldap.h |   4 +-
 2 files changed, 57 insertions(+), 42 deletions(-)

diffs (291 lines):

diff -r 3f7cc2dd6410 -r d9cf369a8b6d src/auth/db-ldap.c
--- a/src/auth/db-ldap.c	Wed Dec 11 18:39:08 2013 +0200
+++ b/src/auth/db-ldap.c	Wed Dec 11 18:39:36 2013 +0200
@@ -50,6 +50,11 @@
 #  define LDAP_OPT_SUCCESS LDAP_SUCCESS
 #endif
 
+struct db_ldap_result {
+	int refcount;
+	LDAPMessage *msg;
+};
+
 struct db_ldap_value {
 	const char **values;
 	bool used;
@@ -475,14 +480,14 @@
 }
 
 static void db_ldap_default_bind_finished(struct ldap_connection *conn,
-					  LDAPMessage *res)
+					  struct db_ldap_result *res)
 {
 	int ret;
 
 	i_assert(conn->pending_count == 0);
 	conn->default_bind_msgid = -1;
 
-	ret = ldap_result2error(conn->ld, res, FALSE);
+	ret = ldap_result2error(conn->ld, res->msg, FALSE);
 	if (db_ldap_connect_finish(conn, ret) < 0) {
 		/* lost connection, close it */
 		db_ldap_conn_close(conn);
@@ -551,14 +556,14 @@
 
 static int db_ldap_fields_get_dn(struct ldap_connection *conn,
 				 struct ldap_request_search *request,
-				 LDAPMessage *res)
+				 struct db_ldap_result *res)
 {
 	struct auth_request *auth_request = request->request.auth_request;
 	struct ldap_request_named_result *named_res;
 	struct db_ldap_result_iterate_context *ldap_iter;
 	const char *name, *const *values;
 
-	ldap_iter = db_ldap_result_iterate_init_full(conn, request, res,
+	ldap_iter = db_ldap_result_iterate_init_full(conn, request, res->msg,
 						     TRUE, TRUE);
 	while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) {
 		if (values[1] != NULL) {
@@ -654,7 +659,7 @@
 }
 
 static int db_ldap_search_save_result(struct ldap_request_search *request,
-				      LDAPMessage *res)
+				      struct db_ldap_result *res)
 {
 	struct ldap_request_named_result *named_res;
 
@@ -669,12 +674,13 @@
 			return -1;
 		named_res->result = res;
 	}
+	res->refcount++;
 	return 0;
 }
 
 static int db_ldap_search_next_subsearch(struct ldap_connection *conn,
 					 struct ldap_request_search *request,
-					 LDAPMessage *res)
+					 struct db_ldap_result *res)
 {
 	struct ldap_request_named_result *named_res;
 	const struct ldap_field *field;
@@ -716,7 +722,7 @@
 static bool
 db_ldap_handle_request_result(struct ldap_connection *conn,
 			      struct ldap_request *request, unsigned int idx,
-			      LDAPMessage *res)
+			      struct db_ldap_result *res)
 {
 	struct ldap_request_search *srequest = NULL;
 	const struct ldap_request_named_result *named_res;
@@ -731,7 +737,7 @@
 		conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
 	} else {
 		srequest = (struct ldap_request_search *)request;
-		switch (ldap_msgtype(res)) {
+		switch (ldap_msgtype(res->msg)) {
 		case LDAP_RES_SEARCH_ENTRY:
 		case LDAP_RES_SEARCH_RESULT:
 			break;
@@ -740,16 +746,16 @@
 			return FALSE;
 		default:
 			i_error("LDAP: Reply with unexpected type %d",
-				ldap_msgtype(res));
+				ldap_msgtype(res->msg));
 			return TRUE;
 		}
 	}
-	if (ldap_msgtype(res) == LDAP_RES_SEARCH_ENTRY) {
+	if (ldap_msgtype(res->msg) == LDAP_RES_SEARCH_ENTRY) {
 		ret = LDAP_SUCCESS;
 		final_result = FALSE;
 	} else {
 		final_result = TRUE;
-		ret = ldap_result2error(conn->ld, res, 0);
+		ret = ldap_result2error(conn->ld, res->msg, 0);
 	}
 	if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
 		/* handle search failures here */
@@ -778,14 +784,13 @@
 					"LDAP search returned multiple entries");
 				res = NULL;
 			} else {
-				/* wait for finish, don't free the result yet */
+				/* wait for finish */
 				return FALSE;
 			}
 		} else {
 			ret = db_ldap_search_next_subsearch(conn, srequest, res);
 			if (ret > 0) {
-				/* free this result, but not the others */
-				ldap_msgfree(res);
+				/* more LDAP queries left */
 				return FALSE;
 			}
 			if (ret < 0)
@@ -806,9 +811,9 @@
 
 	T_BEGIN {
 		if (res != NULL && srequest != NULL && srequest->result != NULL)
-			request->callback(conn, request, srequest->result);
+			request->callback(conn, request, srequest->result->msg);
 
-		request->callback(conn, request, res);
+		request->callback(conn, request, res->msg);
 	} T_END;
 
 	if (idx > 0) {
@@ -820,67 +825,71 @@
 	return TRUE;
 }
 
-static void db_ldap_request_free(struct ldap_request *request, LDAPMessage *res)
+static void db_ldap_result_unref(struct db_ldap_result **_res)
+{
+	struct db_ldap_result *res = *_res;
+
+	*_res = NULL;
+	i_assert(res->refcount > 0);
+	if (--res->refcount == 0) {
+		ldap_msgfree(res->msg);
+		i_free(res);
+	}
+}
+
+static void
+db_ldap_request_free(struct ldap_request *request)
 {
 	if (request->type == LDAP_REQUEST_TYPE_SEARCH) {
 		struct ldap_request_search *srequest =
 			(struct ldap_request_search *)request;
-		const struct ldap_request_named_result *named_res;
+		struct ldap_request_named_result *named_res;
 
-		if (srequest->result == res)
-			res = NULL;
-		if (srequest->result != NULL) {
-			ldap_msgfree(srequest->result);
-			srequest->result = NULL;
-		}
+		if (srequest->result != NULL)
+			db_ldap_result_unref(&srequest->result);
 
 		if (array_is_created(&srequest->named_results)) {
-			array_foreach(&srequest->named_results, named_res) {
-				if (named_res->result == res)
-					res = NULL;
+			array_foreach_modifiable(&srequest->named_results, named_res) {
 				if (named_res->result != NULL)
-					ldap_msgfree(named_res->result);
+					db_ldap_result_unref(&named_res->result);
 			}
 			array_clear(&srequest->named_results);
 		}
 	}
-	if (res != NULL)
-		ldap_msgfree(res);
 }
 
 static void
-db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
+db_ldap_handle_result(struct ldap_connection *conn, struct db_ldap_result *res)
 {
 	struct auth_request *auth_request;
 	struct ldap_request *request;
 	unsigned int idx;
 	int msgid;
 
-	msgid = ldap_msgid(res);
+	msgid = ldap_msgid(res->msg);
 	if (msgid == conn->default_bind_msgid) {
 		db_ldap_default_bind_finished(conn, res);
-		ldap_msgfree(res);
 		return;
 	}
 
 	request = db_ldap_find_request(conn, msgid, &idx);
 	if (request == NULL) {
 		i_error("LDAP: Reply with unknown msgid %d", msgid);
-		ldap_msgfree(res);
 		return;
 	}
 	/* request is allocated from auth_request's pool */
 	auth_request = request->auth_request;
 	auth_request_ref(auth_request);
 	if (db_ldap_handle_request_result(conn, request, idx, res))
-		db_ldap_request_free(request, res);
+		db_ldap_request_free(request);
 	auth_request_unref(&auth_request);
 }
 
 static void ldap_input(struct ldap_connection *conn)
 {
 	struct timeval timeout;
-	LDAPMessage *res;
+	struct db_ldap_result *res;
+	LDAPMessage *msg;
 	time_t prev_reply_diff;
 	int ret;
 
@@ -889,18 +898,22 @@
 			return;
 
 		memset(&timeout, 0, sizeof(timeout));
-		ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &res);
+		ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &msg);
 #ifdef OPENLDAP_ASYNC_WORKAROUND
 		if (ret == 0) {
 			/* try again, there may be another in buffer */
 			ret = ldap_result(conn->ld, LDAP_RES_ANY, 0,
-					  &timeout, &res);
+					  &timeout, &msg);
 		}
 #endif
 		if (ret <= 0)
 			break;
 
+		res = i_new(struct db_ldap_result, 1);
+		res->refcount = 1;
+		res->msg = msg;
 		db_ldap_handle_result(conn, res);
+		db_ldap_result_unref(&res);
 	} while (conn->io != NULL);
 
 	prev_reply_diff = ioloop_time - conn->last_reply_stamp;
@@ -1502,8 +1515,10 @@
 	if (array_is_created(&ldap_request->named_results)) {
 		array_foreach(&ldap_request->named_results, named_res) {
 			suffix = t_strdup_printf("@%s", named_res->field->name);
-			if (named_res->result != NULL)
-				get_ldap_fields(ctx, conn, named_res->result, suffix);
+			if (named_res->result != NULL) {
+				get_ldap_fields(ctx, conn,
+						named_res->result->msg, suffix);
+			}
 		}
 	}
 	return ctx;
diff -r 3f7cc2dd6410 -r d9cf369a8b6d src/auth/db-ldap.h
--- a/src/auth/db-ldap.h	Wed Dec 11 18:39:08 2013 +0200
+++ b/src/auth/db-ldap.h	Wed Dec 11 18:39:36 2013 +0200
@@ -110,7 +110,7 @@
 struct ldap_request_named_result {
 	const struct ldap_field *field;
 	const char *dn;
-	LDAPMessage *result;
+	struct db_ldap_result *result;
 };
 
 struct ldap_request_search {
@@ -121,7 +121,7 @@
 	char **attributes; /* points to pass_attr_names / user_attr_names */
 	const ARRAY_TYPE(ldap_field) *attr_map;
 
-	LDAPMessage *result;
+	struct db_ldap_result *result;
 	ARRAY(struct ldap_request_named_result) named_results;
 	unsigned int name_idx;
 


More information about the dovecot-cvs mailing list