[Date Prev][Date Next] [Chronological] [Thread] [Top]

(ITS#7971) LMDB: Uncarefully appointment when beginning a readonly txn.

Full_Name: Leonid Yuriev
Version: 2.4.40
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (

In continue of ITS#7969 and ITS#7970.

An additional pages may be allocated unreasonable from map. Also the
MDB_MAP_FULL error may returned while the space is really available.

As we can see on lines 2500-2550 of mdb.c the field mr_txnid updated after the
mr_pid, which is also is a flag of that reader's slot is occupied and other
fields are valid, e.g. it is:
ti->mti_readers[i].mr_pid = pid;	/* setup reader's pid, and indicate that other
fields is valid. /
ti->mti_readers[i].mr_tid = tid; 	/* setup reader's thread id. */
...						/* do a little bit of something. /
r->mr_txnid = ti->mti_txnid;		/ * update a "detent" for reclaiming.  */

The problem is that not all cases, the value mr_txnid correctly before being
updated, but will be counted immediately after setup pid in reader's slot. Valid
value for mr_txnid can be any number not less than the last transaction to now.
We can easily verify that the MRC may be incorrect adding mdb_eassert(env,
r->mr_txnid >= ti->mti_txnid) after updating mti_numreaders.
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index 262373e..f2ed653 100644
--- a/libraries/liblmdb/mdb.c
+++ b/libraries/liblmdb/mdb.c
@@ -2522,6 +2522,7 @@ mdb_txn_renew0(MDB_txn *txn)
 					return rc;
+			mdb_eassert(env, r->mr_txnid >= ti->mti_txnid);
 			do	/* ITS# */
 				r->mr_txnid = ti->mti_txnid;

And then by 'make test' got:

#2  0x0000000000403722 in mdb_assert_fail (env=env@entry=0x134e010,
expr_txt=expr_txt@entry=0x40fd14 "r->mr_txnid >= ti->mti_txnid",
func=func@entry=0x41067f <__FUNCTION__.7150> "mdb_txn_renew0", 
    line=line@entry=2525, file=0x40fd08 "mdb.c") at mdb.c:1347
        buf = "mdb.c:2525: Assertion 'r->mr_txnid >= ti->mti_txnid' failed in
'\000' <repeats 15 times>,
#3  0x0000000000403de8 in mdb_txn_renew0 (txn=txn@entry=0x134e210) at
        r = 0x7fbbdf32b080
        env = 0x134e010
        ti = 0x7fbbdf32b000
        meta = <optimised out>
        i = <optimised out>
        nr = <optimised out>
        x = <optimised out>
        rc = <optimised out>
        new_notls = 0
        __FUNCTION__ = "mdb_txn_renew0"
#4  0x00000000004045b8 in mdb_txn_begin (env=0x134e010, parent=parent@entry=0x0,
flags=flags@entry=131072, ret=ret@entry=0x7ffff7cef2f0) at mdb.c:2706
        txn = 0x134e210
        ntxn = <optimised out>
        rc = <optimised out>
        size = <optimised out>
        tsize = 136

There is a possibility that the writer will call mdb_page_alloc() while the
value of mr is wrong. In such case it is possible then reclaiming couldn't be
done and a new page will be allocated from the map. But also possible the

Thus, because of this bug additional pages may be allocated unreasonable, and
the MDB_MAP_FULL error may happen while the space is available.