]> git.sur5r.net Git - openldap/commitdiff
ITS#7512 Plug mdb_txn_abort(nested txn) page leaks.
authorHallvard Furuseth <hallvard@openldap.org>
Tue, 19 Feb 2013 21:03:04 +0000 (22:03 +0100)
committerHallvard Furuseth <hallvard@openldap.org>
Tue, 19 Feb 2013 21:03:04 +0000 (22:03 +0100)
Also catch mdb_cursor_shadow() errors.

libraries/liblmdb/mdb.c

index f1b7480673e569e7553626aad2f4d676467b1142..ab1e497395af4bcdc49a44040e9bc6caf61a0f46 100644 (file)
@@ -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;
        }