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

(ITS#9135) mdb index corruption during legitimate delete



Full_Name: Konstantin Andreev
Version: 2.4.48, git/master/44a7b46/20191203
OS: Solaris 11.3 x64
URL: 
Submission from: (NULL) (79.135.238.172)


Here is a test case.

    1) choose an attribute that has an equality index in your dit;

    2) for this attribute choose a value that does not (and did not) exist in
the dit yet;

Let assume you chose [o=1] attr/val pair.

    3) add MDB_IDL_DB_MAX+1 ( = 0x10000 = 65536 ) entries having [o=1] attr/val
pair.

    4) delete last MDB_IDL_DB_MAX-1 of them in the order inverted as compared to
the order of addition

Now you have two entries having [o=1] attr/val pair. Let assume they are

    | $ ldapsearch -s sub -b 'ou=T' '(o=1)' o
    | dn: cn=1,ou=T
    | o: 1
    | 
    | dn: cn=2,ou=T
    | o: 1

    5) delete one of the remaining entries, e.g. cn=2,ou=T

Repeat the search:

    | $ ldapsearch -s sub -b 'ou=T' '(o=1)' o
    | $

Nope, the remaining entry has been lost from the index forever. But it is still
in the dit:

    | $ ldapsearch -s one -b 'ou=T' '(objectclass=*)' o
    | dn: cn=1,ou=T
    | o: 1
    |
    | dn: ou=A,ou=T
    | ...

You need to slapindex to restore `o' index to a valid state, but you may not
know you need to repair the index.

The probability of this scenario is negligible, because indexed entries never
form continuous block of ids of many-thousand length, but the code for this
scenario exists and has a bug.

Here is a fix for master branch:

--- a/servers/slapd/back-mdb/idl.c
+++ b/servers/slapd/back-mdb/idl.c
@@ -644,12 +644,19 @@ mdb_idl_delete_keys(
 				/* The range has collapsed... */
 					rc = mdb_cursor_del( cursor, MDB_NODUPDATA );
 					if ( rc != 0 ) {
 						err = "c_del dup";
 						goto fail;
 					}
+					data.mv_size = sizeof(ID);
+					data.mv_data = & lo2;
+					rc = mdb_cursor_put( cursor, &key, &data, 0 );
+					if ( rc != 0 ) {
+						err = "c_put enum";
+						goto fail;
+					}
 				} else {
 					/* position on lo */
 					rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
 					if ( id == lo )
 						data.mv_data = &lo2;
 					else {