From f43ae20be7cb1b45b9a4156a32e6fdcefbe4f63d Mon Sep 17 00:00:00 2001 From: Hallvard Furuseth Date: Tue, 19 Feb 2013 22:03:04 +0100 Subject: [PATCH] ITS#7512 Plug mdb_txn_abort(nested txn) page leaks. Also catch mdb_cursor_shadow() errors. --- libraries/liblmdb/mdb.c | 50 ++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c index f1b7480..ab1e497 100644 --- a/libraries/liblmdb/mdb.c +++ b/libraries/liblmdb/mdb.c @@ -911,6 +911,13 @@ typedef struct MDB_xcursor { unsigned char mx_dbflag; } MDB_xcursor; + /** State of FreeDB old pages, stored in the MDB_env */ +typedef struct MDB_pgstate { + txnid_t mf_pglast; /**< ID of last old page record we used */ + pgno_t *mf_pghead; /**< old pages reclaimed from freelist */ + pgno_t *mf_pgfree; /**< memory to free when dropping me_pghead */ +} MDB_pgstate; + /** The database environment. */ struct MDB_env { HANDLE me_fd; /**< The main data file */ @@ -937,12 +944,13 @@ struct MDB_env { size_t me_mapsize; /**< size of the data memory map */ off_t me_size; /**< current file size */ pgno_t me_maxpg; /**< me_mapsize / me_psize */ - txnid_t me_pglast; /**< ID of last old page record we used */ MDB_dbx *me_dbxs; /**< array of static DB info */ uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */ - pgno_t *me_pghead; /**< old pages reclaimed from freelist */ - pgno_t *me_pgfree; /**< memory to free when dropping me_pghead */ pthread_key_t me_txkey; /**< thread-key for readers */ + MDB_pgstate me_pgstate; /**< state of old pages from freeDB */ +# define me_pglast me_pgstate.mf_pglast +# define me_pghead me_pgstate.mf_pghead +# define me_pgfree me_pgstate.mf_pgfree MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */ /** IDL of pages that became unused in a write txn */ MDB_IDL me_free_pgs; @@ -958,6 +966,13 @@ struct MDB_env { sem_t *me_wmutex; #endif }; + + /** Nested transaction */ +typedef struct MDB_ntxn { + MDB_txn mnt_txn; /* the transaction */ + MDB_pgstate mnt_pgstate; /* parent transaction's saved freestate */ +} MDB_ntxn; + /** max number of pages to commit in one writev() call */ #define MDB_COMMIT_PAGES 64 #if defined(IOV_MAX) && IOV_MAX < MDB_COMMIT_PAGES @@ -1855,7 +1870,8 @@ int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) { MDB_txn *txn; - int rc, size; + MDB_ntxn *ntxn; + int rc, size, tsize = sizeof(MDB_txn); if (env->me_flags & MDB_FATAL_ERROR) { DPUTS("environment had fatal error, must shutdown!"); @@ -1871,8 +1887,9 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) { return EINVAL; } + tsize = sizeof(MDB_ntxn); } - size = sizeof(MDB_txn) + env->me_maxdbs * (sizeof(MDB_db)+1); + size = tsize + env->me_maxdbs * (sizeof(MDB_db)+1); if (!(flags & MDB_RDONLY)) size += env->me_maxdbs * sizeof(MDB_cursor *); @@ -1880,7 +1897,7 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) DPRINTF("calloc: %s", strerror(ErrCode())); return ENOMEM; } - txn->mt_dbs = (MDB_db *)(txn+1); + txn->mt_dbs = (MDB_db *) ((char *)txn + tsize); if (flags & MDB_RDONLY) { txn->mt_flags |= MDB_TXN_RDONLY; txn->mt_dbflags = (unsigned char *)(txn->mt_dbs + env->me_maxdbs); @@ -1913,8 +1930,22 @@ mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) txn->mt_dbxs = parent->mt_dbxs; memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); memcpy(txn->mt_dbflags, parent->mt_dbflags, txn->mt_numdbs); - mdb_cursor_shadow(parent, txn); rc = 0; + ntxn = (MDB_ntxn *)txn; + ntxn->mnt_pgstate = env->me_pgstate; /* save parent me_pghead & co */ + if (env->me_pghead) { + size = MDB_IDL_SIZEOF(env->me_pghead); + env->me_pghead = malloc(size); + if (env->me_pghead) + memcpy(env->me_pghead, ntxn->mnt_pgstate.mf_pghead, size); + else + rc = ENOMEM; + } + env->me_pgfree = env->me_pghead; + if (!rc) + rc = mdb_cursor_shadow(parent, txn); + if (rc) + mdb_txn_reset0(txn); } else { rc = mdb_txn_renew0(txn); } @@ -1971,8 +2002,11 @@ mdb_txn_reset0(MDB_txn *txn) } } + free(env->me_pgfree); + if (txn->mt_parent) { txn->mt_parent->mt_child = NULL; + env->me_pgstate = ((MDB_ntxn *)txn)->mnt_pgstate; mdb_midl_free(txn->mt_free_pgs); free(txn->mt_u.dirty_list); return; @@ -1981,7 +2015,6 @@ mdb_txn_reset0(MDB_txn *txn) env->me_free_pgs = txn->mt_free_pgs; } - free(txn->mt_env->me_pgfree); txn->mt_env->me_pghead = txn->mt_env->me_pgfree = NULL; txn->mt_env->me_pglast = 0; @@ -2107,6 +2140,7 @@ mdb_txn_commit(MDB_txn *txn) dst[0].mid = x; free(txn->mt_u.dirty_list); txn->mt_parent->mt_child = NULL; + free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pgfree); free(txn); return MDB_SUCCESS; }