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

(ITS#8447) LMDB: Overwriting values in MDB_DUPSORT databases broken

Full_Name: Lukas W
Version: mdb.master c367c1f69685a4d307acb8cea6945c1d67e1cc7e
OS: Linux
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (

Replacing values in sub-databases (with MDB_DUPSORT) can lead to the new data's
length being ignored. This specific example reproduces the problem (creating the
entries "1"->"ABC", and "1"->2a2abc"):

	mdb_dbi_open(txn, NULL, MDB_DUPSORT, &dbi);
	key.mv_size = 2;
	key.mv_data = "1";
	data.mv_size = 4;
	data.mv_data = "ABC";
	mdb_put(txn, dbi, &key, &data, 0);
	data.mv_data = "abc";
	mdb_put(txn, dbi, &key, &data, 0);

If one later tries to change a value of one of the existing entries, the new
value is being copied, but the size is not changed. This could lead to database
corruption if the new value is longer than the old one as the length of the new
value is used in memcpy.

	key.mv_ze % = 2;
	key.mv_data = "1";
	data.mv_size = 4;
	data.mv_data = "abc";
	mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);

	data.mv_size = 2;
	data.mv_data = "Q";
	mdb_cursor_put(cursor, &key, &data, MDB_CURRENT);

	mdb_cursor_get(cursor, &key, &data, MDB_GET_CURRENT);
	printf("%s (%d)\n", data.mv_size);

This will output "Q (4)", while it should output "Q (2)". Note that the data in
the DB probably is "Q\0c", printf just stops at the null character.
The value is written in mdb.c:7516. Context:

	/* same size, just replace it. Note that we could
	 * also reuse this node if the new data is smaller,
	 * but instead we opt to shrink the node in that case.
	if (F_ISSET(flags, MDB_RESERVE))
		data->mv_data = olddata.mv_data;
	else if (!(mc->mc_flags & C_SUB))
		memcpy(olddata.mv_data, data->mv_data, data->mv_size);
	else {
7516:		memcpy(NODEKEY(leaf), key->mv_data, key->mv_size);
		goto fix_parent;