ledgerwatch / lmdb-go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bus Error when reading nil values

missinglink opened this issue · comments

I have encountered a signal SIGBUS: bus error using txn.RawRead = true when reading keys where the value was set to nil.

A fix for the issue is the following:

 // val.go
 func getBytes(val *C.MDB_val) []byte {
+       if val.mv_size == 0 {
+               return nil
+       }
        return (*[valMaxSize]byte)(val.mv_data)[:val.mv_size:val.mv_size]
 }

The following minimal diff can be used to trigger the error:

diff --git a/lmdb/env_test.go b/lmdb/env_test.go
index afdde18..ddcfc79 100644
--- a/lmdb/env_test.go
+++ b/lmdb/env_test.go
@@ -560,6 +560,12 @@ func setupFlags(t T, flags uint) *Env {
        if err != nil {
                t.Fatalf("setmaxdbs: %v", err)
        }
+
+       err = env.SetMapSize(3298534883328)
+       if err != nil {
+               t.Fatalf("Failed to set map size: %v", err)
+       }
+
        err = env.Open(path, flags, 0664)
        if err != nil {
                t.Fatalf("open: %s", err)
diff --git a/lmdb/txn_test.go b/lmdb/txn_test.go
index eaadaf7..b796558 100644
--- a/lmdb/txn_test.go
+++ b/lmdb/txn_test.go
@@ -1519,3 +1519,50 @@ func openDBI(env *Env, key string, flags uint) (DBI, error) {
        }
        return db, nil
 }
+
+func TestTxn_NilValue(t *testing.T) {
+       env := setup(t)
+       defer clean(env, t)
+
+       err := env.Update(func(txn *Txn) (err error) {
+               db, err := txn.OpenDBI("example", Create)
+               if err != nil {
+                       return err
+               }
+               for i := 0; i < 100000; i++ {
+                       // --------------- note: value is set to nil ---------------
+                       if err = txn.Put(db, []byte(fmt.Sprintf("key%d", i)), nil, 0); err != nil {
+                               return err
+                       }
+               }
+               return nil
+       })
+       if err != nil {
+               t.Errorf("update: %v", err)
+               return
+       }
+
+       err = env.View(func(txn *Txn) (err error) {
+               txn.RawRead = true                    // <---- disabling RawRead doesn't trigger the error
+
+               db, err := txn.OpenDBI("example", 0)
+               cur, err := txn.OpenCursor(db)
+               if err != nil {
+                       return err
+               }
+
+               for k, _, err := cur.Get(nil, nil, First); k != nil; k, _, err = cur.Get(
nil, nil, Next) {
+                       if err != nil {
+                               return err
+                       }
+                       if !bytes.HasPrefix(k, []byte("key")) {
+                               return fmt.Errorf("key: %q", k)
+                       }
+               }
+
+               return nil
+       })
+       if err != nil {
+               t.Errorf("view: %v", err)
+               return
+       }
+}

@AskAlexSharov I'm happy to draft a PR, do you have any insights into whether this is the ideal solution?

It's because - this bindings was copied from LMDB bindings. and LMDB didn't support nil value (and we never used it in mdbx). Feel free to create PR

This is the lmdb-go repo, sorry for the confusion as I am also using the mdbx repo.