Issue 7771 - liblmdb cursor issues
Summary: liblmdb cursor issues
Status: VERIFIED FIXED
Alias: None
Product: LMDB
Classification: Unclassified
Component: liblmdb (show other issues)
Version: unspecified
Hardware: All All
: --- normal
Target Milestone: ---
Assignee: OpenLDAP project
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-12-22 22:09 UTC by Hallvard Furuseth
Modified: 2020-03-12 15:54 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Hallvard Furuseth 2013-12-22 22:09:16 UTC
Full_Name: Hallvard B Furuseth
Version: mdb.master, 4c8f57615c5ca7b014c038e59c1045182e74f5ad
OS: Linux x86_64
URL: ftp://ftp.openldap.org/incoming/Hallvard-Furuseth-131222.c
Submission from: (NULL) (2001:700:100:556::233)
Submitted by: hallvard


mdb_cursor_put() doc bug in lmdb.h:
  "If the function fails for any reason, the state of the
  cursor will be unchanged."
That's quite untrue.  To do that, the function must backup
the cursor before moving it.  (What is a "failure" anyway
- are MDB_KEYEXIST and MDB_NOTFOUND failures?)

Cursor tracking issues with MDB_DUPSORT:

Cursor tracking sometimes ignores xcursors referring to
sub-pages, yet code which uses an xcursor does not always
fix that first.  The enclosed program has several crashes.

I.e. lots more code needs something like
    if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA)
        m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
before using an xcursor.  I tried that everywhere before
using xcursors: That helped, but it was not enough.  E.g.
after mdb_cursor_del() calls mdb_node_shrink(), it tracks
cursors in the same sub-page but not nearby sub-pages.

Also the fix suggested above means readers do more work
that writers did not.  Maybe instead all code which
modified a database, should fix or invalidate that DB's
cursors before returning (even at failure).
Comment 1 Hallvard Furuseth 2014-01-09 20:37:13 UTC
Another problem when an xcursor's mc_pg[0] refers to a sub-page but
has not been kept updated: The comparison 'mc_pg[i] == mp' below
fails and mc_ki[i] does not get modified:

	if (m3->mc_pg[i] == mp && m3->mc_ki[i] >= mc->mc_ki[i]) {
		m3->mc_ki[i]++;
	}

Then it's too late to fixup the sub-page pointer later, the xcursor
refers to the wrong node in the sub-page.  The above code is from
mdb_cursor_put(), but there are other similar cases elsewhere.

-- 
Hallvard

Comment 2 Hallvard Furuseth 2014-01-13 15:00:51 UTC
changed notes
changed state Open to Test
moved from Incoming to Software Bugs
Comment 3 Quanah Gibson-Mount 2014-12-11 01:07:47 UTC
changed state Test to Closed
Comment 4 Hallvard Furuseth 2015-06-22 10:56:51 UTC
changed notes
changed state Closed to Partial
Comment 5 Hallvard Furuseth 2015-10-11 22:28:53 UTC
On 22/12/13 23:09, h.b.furuseth@usit.uio.no wrote:
> I.e. lots more code needs something like
>      if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA)
>          m2->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
> before using an xcursor.  I tried that everywhere before
> using xcursors: That helped, but it was not enough.  E.g.
> after mdb_cursor_del() calls mdb_node_shrink(), it tracks
> cursors in the same sub-page but not nearby sub-pages.

Such fixups do heal this ITS's test program with current mdb.master,
though.  That is, perl -i -pe '
s/\b(\w+)->mc_xcursor->mx_cursor\./mdb_subcursor($1)->/gx;
s/\&(\w+)->mc_xcursor->mx_cursor/mdb_subcursor($1)/gx;' mdb.c

plus this function:

static MDB_cursor *
mdb_subcursor(MDB_cursor *mc)
{
     MDB_xcursor *mx = mc->mc_xcursor;
     MDB_cursor *subc = NULL;
     MDB_page *mp = mc->mc_pg[mc->mc_top];
     if (mx) {
         subc = &mx->mx_cursor;
         if (((mc->mc_flags | subc->mc_flags) & C_INITIALIZED) &&
             mp && !(mp->mp_flags & (P_BRANCH|P_LEAF2|P_SUBP)))
         {
             MDB_node *leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
             if ((leaf->mn_flags & (F_SUBDATA|F_DUPDATA)) == F_DUPDATA)
                 subc->mc_pg[0] = NODEDATA(leaf);
         }
     }
     return subc;
}


Comment 6 Howard Chu 2015-10-11 23:11:08 UTC
changed notes
changed state Partial to Test
Comment 7 Hallvard Furuseth 2015-10-12 09:35:06 UTC
I wrote:
> mdb_subcursor(MDB_cursor *mc)
> (...)
>           if (((mc->mc_flags | subc->mc_flags) & C_INITIALIZED) &&

Oops. For reference, the '|' should be '&'.
mdb.master has now been fixed to work without this function, though.

-- 
Hallvard

Comment 8 OpenLDAP project 2015-11-30 18:26:10 UTC
Partial fix in mdb.master and mdb.RE
Remainder fixed in mdb.master
Comment 9 Quanah Gibson-Mount 2015-11-30 18:26:10 UTC
changed notes
changed state Test to Closed