Full_Name: Lukas W
Version: mdb.master c367c1f69685a4d307acb8cea6945c1d67e1cc7e
OS: Linux
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (114.23.231.86)
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);
This is a misuse of MDB_CURRENT: as documented, the new value is supposed to
be the same size as the existing value.
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;
}
[…]
--
-- Howard Chu
CTO, Symas Corp.
http://www.symas.com
Director, Highland Sun
http://highlandsun.com/hyc/
Chief Architect, OpenLDAP
http://www.openldap.org/project/