mozilla / rkv

A simple, humane, typed key-value storage solution.
https://crates.io/crates/rkv
Apache License 2.0
310 stars 53 forks source link

Fix cursor/iterator lifetime coupling which can result in referencing invalid memory when eliminating dead code #172

Closed victorporof closed 4 years ago

victorporof commented 4 years ago

Signed-off-by: Victor Porof victor.porof@gmail.com

LMDB semantics dictate that a cursor must be valid for calls to mdb_cursor_get. In case of the rust wrappers, this means that the lmdb::RoCursor/lmdb::RwCursor must be valid the entire lifetime of an lmdb::Iter. In other words, cursors must not be dropped while an iterator built from it is alive. Unfortunately, the LMDB crate API does not express this through the type system at all, so we must enforce it somehow.

Before this PR, this worked because dead code wasn't eliminated. The Iter types for single/multi database wrappers owned a C generic type implementing BackendRoCursor, even though they didn't have to.

In case of a safe mode backend, this isn't required, and eliminating the dead code doesn't result in any SIGSEGV. However, in case of an LMDB backend, doing so results in signal: 11, SIGSEGV: invalid memory reference when running tests, even though everything compiles successfully and there's no unsafe code used.

These LMDB specifics shouldn't be expressed at this level, since they impose restrictions on other backends (namely safe mode). Ideally, they should be expressed in the LMDB crate (which, as it stands, allows a plethora of unsafe ways of using its API, without them being marked as such through the type system). However, if we don't want to fix the LMDB crate actually expose a safer API, we can do this at the backend level in the RKV crate.

With this PR:

victorporof commented 4 years ago

Blocks https://github.com/mozilla/rkv/pull/173