From 4a0b30499caab8628efcedb8be5643fead7cd17e Mon Sep 17 00:00:00 2001
From: Hallvard Furuseth <hallvard@openldap.org>
Date: Thu, 12 Apr 2012 07:34:13 +0200
Subject: [PATCH] libmdb: Do not use the MDB_txbody.mtb_txnid field.

Get txn->mt_txnid from the meta page instead.  Otherwise it
and txn->mt_toggle could become inconsistent, since read-only
txns fetched both from MDB_txbody without mutex proteciton.
---
 libraries/libmdb/mdb.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/libraries/libmdb/mdb.c b/libraries/libmdb/mdb.c
index ba6ded3226..8280e22afb 100644
--- a/libraries/libmdb/mdb.c
+++ b/libraries/libmdb/mdb.c
@@ -511,11 +511,17 @@ typedef struct MDB_txbody {
 		 */
 	pthread_mutex_t	mtb_mutex;
 #endif
+#if MDB_VERSION == 1
 		/**	The ID of the last transaction committed to the database.
 		 *	This is recorded here only for convenience; the value can always
 		 *	be determined by reading the main database meta pages.
+		 *
+		 *	Value is unused, but maintained for backwards compatibility.
+		 *	Drop this or mtb_me_toggle when changing MDB_VERSION.
+		 *	(Reading both should have been done atomically.)
 		 */
 	txnid_t		mtb_txnid;
+#endif
 		/** The number of slots that have been used in the reader table.
 		 *	This always records the maximum count, it is not decremented
 		 *	when readers release their slots.
@@ -536,7 +542,9 @@ typedef struct MDB_txninfo {
 #define mti_version	mt1.mtb.mtb_version
 #define mti_mutex	mt1.mtb.mtb_mutex
 #define mti_rmname	mt1.mtb.mtb_rmname
+#if MDB_VERSION == 1
 #define mti_txnid	mt1.mtb.mtb_txnid
+#endif
 #define mti_numreaders	mt1.mtb.mtb_numreaders
 #define mti_me_toggle	mt1.mtb.mtb_me_toggle
 		char pad[(sizeof(MDB_txbody)+CACHELINE-1) & ~(CACHELINE-1)];
@@ -1565,18 +1573,19 @@ mdb_txn_renew0(MDB_txn *txn)
 			pthread_setspecific(env->me_txkey, r);
 		}
 		txn->mt_toggle = env->me_txns->mti_me_toggle;
-		txn->mt_txnid = env->me_txns->mti_txnid;
+		txn->mt_txnid = r->mr_txnid = env->me_metas[txn->mt_toggle]->mm_txnid;
+
 		/* This happens if a different process was the
 		 * last writer to the DB.
 		 */
 		if (env->me_wtxnid < txn->mt_txnid)
 			mt_dbflag = DB_STALE;
-		r->mr_txnid = txn->mt_txnid;
 		txn->mt_u.reader = r;
 	} else {
 		LOCK_MUTEX_W(env);
 
-		txn->mt_txnid = env->me_txns->mti_txnid;
+		txn->mt_toggle = env->me_txns->mti_me_toggle;
+		txn->mt_txnid = env->me_metas[txn->mt_toggle]->mm_txnid;
 		if (env->me_wtxnid < txn->mt_txnid)
 			mt_dbflag = DB_STALE;
 		txn->mt_txnid++;
@@ -1584,7 +1593,6 @@ mdb_txn_renew0(MDB_txn *txn)
 		if (txn->mt_txnid == mdb_debug_start)
 			mdb_debug = 1;
 #endif
-		txn->mt_toggle = env->me_txns->mti_me_toggle;
 		txn->mt_u.dirty_list = env->me_dirty_list;
 		txn->mt_u.dirty_list[0].mid = 0;
 		txn->mt_free_pgs = env->me_free_pgs;
-- 
GitLab