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

(ITS#7886) mdb_ovpage_free() can break mdb_copy

Full_Name: Hallvard B Furuseth
Version: LMDB_0.9.13
OS: Linux x86_64
Submission from: (NULL) (
Submitted by: hallvard

Allocate an ovpage from mt_next_pgno, mdb_ovpage_free() it
and commit: The datafile may end before MDB_meta.mm_last_pg
since the ovpage was never written.  mdb_env_copyfd() & co
break when they read the file to mm_last_pg.

Same with loose pages if mdb_page_flush() skipped them,
which it could:  ecde3a4008f5080d8264926f49680db27804787f
in my branch mdb/loose3.

Bug demo:

  rm data.mdb; ./a.out && ./mdb_copy . > copy.mdb
  ./mdb_copy: copying failed, error 14 (Bad address)

(Piping it to /dev/null succeeds on Linux, apparently
it doesn't read anything.)

#include "lmdb.h"
#include <stdio.h>
#include <stdlib.h>
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
    "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))

int main(void)
    int rc;
    unsigned sz;
    MDB_env *env;
    MDB_txn *txn;
    MDB_dbi dbi;
    static char buf[20000];
    E(mdb_env_open(env, ".", 0, 0664));
    for (sz = 0; sz<=sizeof(buf); sz = (sz==2 ? sizeof(buf) : sz+1)) {
        E(mdb_txn_begin(env, NULL, 0, &txn));
        E(mdb_dbi_open(txn, NULL, 0, &dbi));
        E(mdb_put(txn, dbi, &(MDB_val){4,&sz}, &(MDB_val){sz,buf}, 0));
        if (sz)
            E(mdb_del(txn, dbi, &(MDB_val){4,&sz}, NULL));
    return 0;