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

(ITS#8777) [LMDB] Closing read cursor uses already freed transaction (MDB_VL32)

Full_Name: Markus Junginger
Version: mdb.master
OS: Ubuntu
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (

Read cursors may outlive a transaction to be renewed with another. Thus, cursors
should be correctly closed even after their last transaction was aborted. But
they access the (already freed) transaction during closing when MDB_VL32 is used
(mdb.master branch).

ASan captures this like this:
==19722==ERROR: AddressSanitizer: heap-use-after-free on address 0x61100001b140
at pc 0x000000ea2387 bp 0x7ffd36e3cd20 sp 0x7ffd36e3cd18
READ of size 8 at 0x61100001b140 thread T0
    #0 0xea2386 in mdb_cursor_unref mdb.c:2082:18
    #1 0xec577b in mdb_cursor_close mdb.c:8538:3

0x61100001b140 is located 128 bytes inside of 250-byte region
freed by thread T0 here:
    #0 0x74f840 in __interceptor_cfree.localalias.0 (...)
    #1 0xe8ea98 in mdb_txn_end mdb.c:3380:3
    #2 0xe8ecc5 in mdb_txn_abort mdb.c:3405:2

Possible fix for line mdb.c:2082 (applying what I found in mdb_cursor_close):
-	if (mc->mc_txn->mt_rpages[0].mid) {
+	if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_rpages[0].mid) {

Code to reproduce using MDB_VL32:
    err = mdb_txn_begin(env, nullptr, MDB_RDONLY, &tx);
    err = mdb_cursor_open(tx, dbi, &cursor);
    // We must have data so get will set C_INITIALIZED
    err = mdb_cursor_get(cursor, &key, &value, MDB_SET_KEY);
    mdb_cursor_close(cursor); // <- still uses tx