Add MDB_CLEANMEM option.

Initialize unused portions of DB pages, for security or to silence
checkers like Purify and Valgrind. Like BDB configure --enable-umrw.
vmware
Hallvard Furuseth 11 years ago
parent fc87d405f1
commit 92f71f50f9
  1. 26
      libraries/liblmdb/lmdb.h
  2. 37
      libraries/liblmdb/mdb.c
  3. 2
      libraries/liblmdb/mtest.c
  4. 2
      libraries/liblmdb/mtest2.c

@ -70,6 +70,12 @@
* access to locks and lock file. Exceptions: On read-only filesystems * access to locks and lock file. Exceptions: On read-only filesystems
* or with the #MDB_NOLOCK flag described under #mdb_env_open(). * or with the #MDB_NOLOCK flag described under #mdb_env_open().
* *
* - By default, unused portions of the datafile may receive garbage data
* from memory freed by other code. (This does not happen when using
* the #MDB_WRITEMAP flag.) Applications handling sensitive data
* which must not be written, and which don't use #MDB_WRITEMAP,
* need to prevent this with the #MDB_CLEANMEM flag.
*
* - A thread can only use one transaction at a time, plus any child * - A thread can only use one transaction at a time, plus any child
* transactions. Each transaction belongs to one thread. See below. * transactions. Each transaction belongs to one thread. See below.
* The #MDB_NOTLS flag changes this for read-only transactions. * The #MDB_NOTLS flag changes this for read-only transactions.
@ -277,6 +283,8 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
#define MDB_NOLOCK 0x400000 #define MDB_NOLOCK 0x400000
/** don't do readahead (no effect on Windows) */ /** don't do readahead (no effect on Windows) */
#define MDB_NORDAHEAD 0x800000 #define MDB_NORDAHEAD 0x800000
/** don't write uninitialized malloc'd memory to datafile */
#define MDB_CLEANMEM 0x1000000
/** @} */ /** @} */
/** @defgroup mdb_dbi_open Database Flags /** @defgroup mdb_dbi_open Database Flags
@ -546,6 +554,22 @@ int mdb_env_create(MDB_env **env);
* supports it. Turning it off may help random read performance * supports it. Turning it off may help random read performance
* when the DB is larger than RAM and system RAM is full. * when the DB is larger than RAM and system RAM is full.
* The option is not implemented on Windows. * The option is not implemented on Windows.
* <li>#MDB_CLEANMEM
* Don't write uninitialized memory to unused spaces in the datafile.
* By default, memory for pages written to the datafile is obtained
* using malloc, and only the portions that LMDB uses are modified.
* Unused portions of a page may contain leftover data from other
* code that used the heap and subsequently freed that memory.
* That can be a problem for applications which handle sensitive data
* like passwords, and it makes memory checkers like Valgrind noisy.
* With this flag, unused portions of pages will be initialized to
* zero. This flag is not needed with #MDB_WRITEMAP, which writes
* directly to the mmap instead of using malloc for pages. The
* initialization is also skipped if #MDB_RESERVE is used; the
* caller is expected to overwrite all of the memory that was
* reserved in that case.
* This flag may be changed at any time using #mdb_env_set_flags().
* It comes at some performance cost.
* </ul> * </ul>
* @param[in] mode The UNIX permissions to set on created files. This parameter * @param[in] mode The UNIX permissions to set on created files. This parameter
* is ignored on Windows. * is ignored on Windows.
@ -1131,6 +1155,8 @@ int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
* reserved space, which the caller can fill in later - before * reserved space, which the caller can fill in later - before
* the next update operation or the transaction ends. This saves * the next update operation or the transaction ends. This saves
* an extra memcpy if the data is being generated later. * an extra memcpy if the data is being generated later.
* MDB does nothing else with this memory, even if #MDB_CLEANMEM is
* set - the caller is expected to modify all of the space requested.
* <li>#MDB_APPEND - append the given key/data pair to the end of the * <li>#MDB_APPEND - append the given key/data pair to the end of the
* database. No key comparisons are performed. This option allows * database. No key comparisons are performed. This option allows
* fast bulk loading when keys are already known to be in the * fast bulk loading when keys are already known to be in the

@ -1322,7 +1322,12 @@ mdb_page_malloc(MDB_txn *txn, unsigned num)
{ {
MDB_env *env = txn->mt_env; MDB_env *env = txn->mt_env;
MDB_page *ret = env->me_dpages; MDB_page *ret = env->me_dpages;
size_t sz = env->me_psize; size_t psize = env->me_psize, sz = psize, off;
/* For #MDB_CLEANMEM, psize counts how much to init.
* For a single page alloc, we init everything after the page header.
* For multi-page, we init the final page; if the caller needed that
* many pages they will be filling in at least up to the last page.
*/
if (num == 1) { if (num == 1) {
if (ret) { if (ret) {
VGMEMP_ALLOC(env, ret, sz); VGMEMP_ALLOC(env, ret, sz);
@ -1330,10 +1335,16 @@ mdb_page_malloc(MDB_txn *txn, unsigned num)
env->me_dpages = ret->mp_next; env->me_dpages = ret->mp_next;
return ret; return ret;
} }
psize -= off = PAGEHDRSZ;
} else { } else {
sz *= num; sz *= num;
off = sz - psize;
} }
if ((ret = malloc(sz)) != NULL) { if ((ret = malloc(sz)) != NULL) {
if (env->me_flags & MDB_CLEANMEM) {
memset((char *)ret + off, 0, psize);
ret->mp_pad = 0;
}
VGMEMP_ALLOC(env, ret, sz); VGMEMP_ALLOC(env, ret, sz);
} }
return ret; return ret;
@ -2486,7 +2497,7 @@ mdb_freelist_save(MDB_txn *txn)
int rc, maxfree_1pg = env->me_maxfree_1pg, more = 1; int rc, maxfree_1pg = env->me_maxfree_1pg, more = 1;
txnid_t pglast = 0, head_id = 0; txnid_t pglast = 0, head_id = 0;
pgno_t freecnt = 0, *free_pgs, *mop; pgno_t freecnt = 0, *free_pgs, *mop;
ssize_t head_room = 0, total_room = 0, mop_len; ssize_t head_room = 0, total_room = 0, mop_len, clean_limit;
mdb_cursor_init(&mc, txn, FREE_DBI, NULL); mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
@ -2497,9 +2508,15 @@ mdb_freelist_save(MDB_txn *txn)
return rc; return rc;
} }
/* MDB_RESERVE cancels CLEANMEM in ovpage malloc (when no WRITEMAP) */
clean_limit = (env->me_flags & (MDB_CLEANMEM|MDB_WRITEMAP)) == MDB_CLEANMEM
? maxfree_1pg : SSIZE_MAX;
for (;;) { for (;;) {
/* Come back here after each Put() in case freelist changed */ /* Come back here after each Put() in case freelist changed */
MDB_val key, data; MDB_val key, data;
pgno_t *pgs;
ssize_t j;
/* If using records from freeDB which we have not yet /* If using records from freeDB which we have not yet
* deleted, delete them and any we reserved for me_pghead. * deleted, delete them and any we reserved for me_pghead.
@ -2583,7 +2600,12 @@ mdb_freelist_save(MDB_txn *txn)
rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE);
if (rc) if (rc)
return rc; return rc;
*(MDB_ID *)data.mv_data = 0; /* IDL is initially empty */ /* IDL is initially empty, zero out at least the length */
pgs = (pgno_t *)data.mv_data;
j = head_room > clean_limit ? head_room : 0;
do {
pgs[j] = 0;
} while (--j >= 0);
total_room += head_room; total_room += head_room;
} }
@ -3943,8 +3965,9 @@ fail:
* at runtime. Changing other flags requires closing the * at runtime. Changing other flags requires closing the
* environment and re-opening it with the new flags. * environment and re-opening it with the new flags.
*/ */
#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC) #define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_CLEANMEM)
#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY|MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD) #define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY|MDB_WRITEMAP| \
MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD)
int int
mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode) mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
@ -5847,12 +5870,14 @@ more:
if (NODESIZE + sizeof(indx_t) + NODEKSZ(leaf) + xdata.mv_size if (NODESIZE + sizeof(indx_t) + NODEKSZ(leaf) + xdata.mv_size
>= env->me_nodemax) { >= env->me_nodemax) {
/* yes, convert it */ /* yes, convert it */
dummy.md_flags = 0;
if (mc->mc_db->md_flags & MDB_DUPFIXED) { if (mc->mc_db->md_flags & MDB_DUPFIXED) {
dummy.md_pad = fp->mp_pad; dummy.md_pad = fp->mp_pad;
dummy.md_flags = MDB_DUPFIXED; dummy.md_flags = MDB_DUPFIXED;
if (mc->mc_db->md_flags & MDB_INTEGERDUP) if (mc->mc_db->md_flags & MDB_INTEGERDUP)
dummy.md_flags |= MDB_INTEGERKEY; dummy.md_flags |= MDB_INTEGERKEY;
} else {
dummy.md_pad = 0;
dummy.md_flags = 0;
} }
dummy.md_depth = 1; dummy.md_depth = 1;
dummy.md_branch_pages = 0; dummy.md_branch_pages = 0;

@ -28,7 +28,7 @@ int main(int argc,char * argv[])
MDB_cursor *cursor, *cur2; MDB_cursor *cursor, *cur2;
int count; int count;
int *values; int *values;
char sval[32]; char sval[32] = "";
srandom(time(NULL)); srandom(time(NULL));

@ -31,7 +31,7 @@ int main(int argc,char * argv[])
MDB_cursor *cursor; MDB_cursor *cursor;
int count; int count;
int *values; int *values;
char sval[32]; char sval[32] = "";
srandom(time(NULL)); srandom(time(NULL));

Loading…
Cancel
Save