changeset 8650:81fd92599c7f HEAD

pgsql: Don't write to freed memory if transaction is aborted while SQL commands are being executed.
author Timo Sirainen <tss@iki.fi>
date Sat, 17 Jan 2009 17:21:29 -0500
parents 67c08c386702
children a498c440eef2
files src/lib-sql/driver-pgsql.c
diffstat 1 files changed, 21 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-sql/driver-pgsql.c	Sat Jan 17 13:56:24 2009 -0500
+++ b/src/lib-sql/driver-pgsql.c	Sat Jan 17 17:21:29 2009 -0500
@@ -70,6 +70,7 @@
 
 struct pgsql_transaction_context {
 	struct sql_transaction_context ctx;
+	int refcount;
 
 	sql_commit_callback_t *callback;
 	void *context;
@@ -845,10 +846,22 @@
 
 	ctx = i_new(struct pgsql_transaction_context, 1);
 	ctx->ctx.db = db;
+	ctx->refcount = 1;
 	return &ctx->ctx;
 }
 
 static void
+driver_pgsql_transaction_unref(struct pgsql_transaction_context *ctx)
+{
+	i_assert(ctx->refcount > 0);
+	if (--ctx->refcount > 0)
+		return;
+
+	i_free(ctx->first_update);
+	i_free(ctx);
+}
+
+static void
 transaction_commit_callback(struct sql_result *result, void *context)
 {
 	struct pgsql_transaction_context *ctx =
@@ -871,13 +884,14 @@
 		callback(ctx->error, context);
 		if (ctx->opened)
 			sql_exec(_ctx->db, "ROLLBACK");
-		i_free(ctx);
+		driver_pgsql_transaction_unref(ctx);
 		return;
 	}
 
 	if (!ctx->opened) {
 		/* nothing done */
 		ctx->callback(NULL, ctx->context);
+		driver_pgsql_transaction_unref(ctx);
 		return;
 	}
 
@@ -897,6 +911,7 @@
 	struct sql_result *result;
 
 	if (ctx->first_update != NULL) {
+		ctx->refcount++;
 		sql_query(_ctx->db, ctx->first_update,
 			  transaction_update_callback, ctx);
 		i_free_and_null(ctx->first_update);
@@ -919,7 +934,7 @@
 		sql_result_free(result);
 	}
 
-	i_free(ctx);
+	driver_pgsql_transaction_unref(ctx);
 	return *error_r == NULL ? 0 : -1;
 }
 
@@ -931,8 +946,7 @@
 
 	if (ctx->opened)
 		sql_exec(_ctx->db, "ROLLBACK");
-	i_free(ctx->first_update);
-	i_free(ctx);
+	driver_pgsql_transaction_unref(ctx);
 }
 
 static void
@@ -944,6 +958,7 @@
 		ctx->failed = TRUE;
 		ctx->error = sql_result_get_error(result);
 	}
+	driver_pgsql_transaction_unref(ctx);
 }
 
 static void
@@ -963,12 +978,14 @@
 			return;
 		}
 		ctx->opened = TRUE;
+		ctx->refcount += 2;
 		sql_query(_ctx->db, "BEGIN", transaction_update_callback, ctx);
 		sql_query(_ctx->db, ctx->first_update,
 			  transaction_update_callback, ctx);
 		i_free_and_null(ctx->first_update);
 	}
 
+	ctx->refcount++;
 	driver_pgsql_query_full(_ctx->db, query,
 				transaction_update_callback, ctx, FALSE);
 }