https://bugs.openldap.org/show_bug.cgi?id=9567
Issue ID: 9567 Summary: Double free error after attempt to insert a duplicate entry Product: LMDB Version: 0.9.17 Hardware: All OS: All Status: UNCONFIRMED Severity: normal Priority: --- Component: liblmdb Assignee: bugs@openldap.org Reporter: wietse@porcupine.org Target Milestone: ---
This problem was originally discovered by Adi Prasaja on Ubuntu with lmdb-0.9.24, and reproduced by me on Fedora with lmdb-0.9.25, as well as multiple source repo branches from github.com/openldap.
The problem was introduced with lmdb version 0.9.17 and still exists in the 'head' version 0.9.70 as retrieved from github.com on May 28, 2021.
Quick demo (any Postfix version with LMDB support): create a key-value store from a file containing one (key, value) per line.
$ cat /tmp/aa aa 1 aa 2 $ postmap lmdb:/tmp/aa postmap: warning: lmdb:/tmp/aa: duplicate entry: "aa" <== Postfix message free(): double free detected in tcache 2 <== libc message Aborted (core dumped)
Commenting out the free(env->me_txn0) call mdb_env_close0() will prevent the double free() error. Of course, that results in a memory leak when the input contains no duplicate line.
The remainder of this message show the steps to reproduce this with lmdb version 0.9.70 (from github 'head') on Fedora 32 with postfix-3.7-20210424. These steps have been verified to also work for Postfix 3.2.
The steps assume that the LMDB library and header files are installed in /usr/local, by using the default lmdb Makefile.
$ cat >makemakefiles-lmdb-local <<'EOF' #!/bin/sh
export OPT="" export CCARGS="-DNO_NIS" export AUXLIBS="" # Add LMDB export CCARGS="$CCARGS -DHAS_LMDB" export AUXLIBS_LMDB="-L/usr/local/lib -Wl,-rpath,/usr/local/lib -llmdb"
unset shlib_directory make -f Makefile.in makefiles shared=no dynamicmaps=no CCARGS="-I/usr/local/include $CCARGS" AUXLIBS="$AUXLIBS $AUXLIBS_LMDB" EOF $ make tidy [some output] $ sh makemakefiles-lmdb-local $ make -j8 [lots of output] $ cat >/tmp/aa << EOF aa 1 aa 2 EOF $ valgrind --tool=memcheck bin/postmap lmdb:/tmp/aa ==340318== Memcheck, a memory error detector ==340318== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==340318== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info ==340318== Command: bin/postmap lmdb:/tmp/aa ==340318== postmap: warning: lmdb:/tmp/aa: duplicate entry: "aa" ==340318== Invalid free() / delete / delete[] / realloc() ==340318== at 0x483B9F5: free (vg_replace_malloc.c:538) ==340318== by 0x4858CFD: mdb_env_close0 (in /usr/local/lib/liblmdb.so) ==340318== by 0x4859B0C: mdb_env_close (in /usr/local/lib/liblmdb.so) ==340318== by 0x13CBC3: slmdb_close (slmdb.c:780) ==340318== by 0x13B297: dict_lmdb_close (dict_lmdb.c:475) ==340318== by 0x113DFF: mkmap_close (mkmap_open.c:211) ==340318== by 0x10F080: postmap (postmap.c:557) ==340318== by 0x1108E4: main (postmap.c:1140) ==340318== Address 0x7320c30 is 0 bytes inside a block of size 258 free'd ==340318== at 0x483B9F5: free (vg_replace_malloc.c:538) ==340318== by 0x48561DE: mdb_txn_commit (mdb.c:4130) ==340318== by 0x13CB7D: slmdb_close (slmdb.c:771) ==340318== by 0x13B297: dict_lmdb_close (dict_lmdb.c:475) ==340318== by 0x113DFF: mkmap_close (mkmap_open.c:211) ==340318== by 0x10F080: postmap (postmap.c:557) ==340318== by 0x1108E4: main (postmap.c:1140) ==340318== Block was alloc'd at ==340318== at 0x483CAE9: calloc (vg_replace_malloc.c:760) ==340318== by 0x48599F2: mdb_env_open (in /usr/local/lib/liblmdb.so) ==340318== by 0x13CD91: slmdb_open (slmdb.c:851) ==340318== by 0x13B6C6: dict_lmdb_open (dict_lmdb.c:612) ==340318== by 0x113F50: mkmap_open (mkmap_open.c:283) ==340318== by 0x10EC02: postmap (postmap.c:438) ==340318== by 0x1108E4: main (postmap.c:1140) ==340318== ==340318== ==340318== HEAP SUMMARY: ==340318== in use at exit: 27,608 bytes in 634 blocks ==340318== total heap usage: 1,430 allocs, 797 frees, 3,356,293 bytes allocated ==340318== ==340318== LEAK SUMMARY: ==340318== definitely lost: 0 bytes in 0 blocks ==340318== indirectly lost: 0 bytes in 0 blocks ==340318== possibly lost: 27,582 bytes in 633 blocks ==340318== still reachable: 26 bytes in 1 blocks ==340318== suppressed: 0 bytes in 0 blocks ==340318== Rerun with --leak-check=full to see details of leaked memory ==340318== ==340318== For lists of detected and suppressed errors, rerun with: -s ==340318== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Similar errors happen with lmdb-0.9.17. The problem does not exist in lmdb-0.9.16 or earlier versions.
https://bugs.openldap.org/show_bug.cgi?id=9567
Howard Chu hyc@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Resolution|--- |INVALID Status|UNCONFIRMED |RESOLVED
--- Comment #1 from Howard Chu hyc@openldap.org --- Thanks for the report, but this is a postfix bug, not an LMDB bug.
#### gdb) r Starting program: /home/software/postfix-3.6.0/bin/postmap lmdb:/tmp/aa [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, mdb_txn_end (txn=0x0, mode=0) at mdb.c:3314 3314 { (gdb) bt #0 mdb_txn_end (txn=0x0, mode=0) at mdb.c:3314 #1 0x0000555555597c8b in mdb_txn_abort (txn=0x5555555e7c80) at mdb.c:3428 #2 0x000055555558a497 in slmdb_put (slmdb=0x5555555e7fc8, mdb_key=0x7fffffffe410, mdb_value=0x7fffffffe420, flags=16) at slmdb.c:585 #3 0x0000555555588bf3 in dict_lmdb_update (dict=0x5555555e7f30, name=0x5555555e3050 "aa", value=0x5555555e5753 "2") at dict_lmdb.c:274 #4 0x000055555555cc07 in postmap (map_type=0x7fffffffead8 "lmdb", path_name=0x7fffffffeadd "/tmp/aa", postmap_flags=3, open_flags=578, dict_flags=671745) at postmap.c:546 #5 0x000055555555e6ec in main (argc=2, argv=0x7fffffffe778) at postmap.c:1140 (gdb) up #1 0x0000555555597c8b in mdb_txn_abort (txn=0x5555555e7c80) at mdb.c:3428 3428 mdb_txn_end(txn, MDB_END_ABORT|MDB_END_SLOT|MDB_END_FREE); (gdb) down #0 mdb_txn_end (txn=0x0, mode=0) at mdb.c:3314 3314 { (gdb) n 3315 MDB_env *env = txn->mt_env; (gdb) bt #0 mdb_txn_end (txn=0x5555555e7c80, mode=2097186) at mdb.c:3315 #1 0x0000555555597c8b in mdb_txn_abort (txn=0x5555555e7c80) at mdb.c:3428 #2 0x000055555558a497 in slmdb_put (slmdb=0x5555555e7fc8, mdb_key=0x7fffffffe410, mdb_value=0x7fffffffe420, flags=16) at slmdb.c:585 #3 0x0000555555588bf3 in dict_lmdb_update (dict=0x5555555e7f30, name=0x5555555e3050 "aa", value=0x5555555e5753 "2") at dict_lmdb.c:274 #4 0x000055555555cc07 in postmap (map_type=0x7fffffffead8 "lmdb", path_name=0x7fffffffeadd "/tmp/aa", postmap_flags=3, open_flags=578, dict_flags=671745) at postmap.c:546 #5 0x000055555555e6ec in main (argc=2, argv=0x7fffffffe778) at postmap.c:1140 (gdb) n ####
slmdb.c:585 calls mdb_txn_abort() on a txn which was assigned from slmdb->txn. When mdb_txn_abort() returns, txn is no longer valid. slmdb->txn must be set to NULL here and that's not happening. So slmdb is closing the same txn twice, which is a user error. Please report this to the postfix developers, thanks.
https://bugs.openldap.org/show_bug.cgi?id=9567
--- Comment #2 from wietse@porcupine.org wietse@porcupine.org --- openldap-its@openldap.org:
https://bugs.openldap.org/show_bug.cgi?id=9567
Howard Chu hyc@openldap.org changed:
What |Removed |Added
Resolution|--- |INVALID Status|UNCONFIRMED |RESOLVED
--- Comment #1 from Howard Chu hyc@openldap.org --- Thanks for the report, but this is a postfix bug, not an LMDB bug.
Fix applied; this Postfix code had not changed since it was created early 2014. Interestingly, there was no double free before lmdb-0.9.17. Apparently we got 'lucky' for a couple years until some LMDB internals were changed.
Wietse
https://bugs.openldap.org/show_bug.cgi?id=9567
Quanah Gibson-Mount quanah@openldap.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |VERIFIED