Code that attempts to use an item (e.g., a row) returned by an iterator after the iterator has advanced to the next item will be accessing freed memory and experience undefined behaviour. Code that uses the item and then advances the iterator is unaffected. This problem has always existed.
This is a use-after-free bug, so it's rated high severity. If your code uses a pre-3.0.0 version of cassandra-rs, and uses an item returned by a cassandra-rs iterator after calling next() on that iterator, then it is vulnerable. However, such code will almost always fail immediately - so we believe it is unlikely that any code using this pattern would have reached production. For peace of mind, we recommend you upgrade anyway.
Version 3.0 fixes a soundness issue with the previous API. The iterators in the underlying Cassandra driver invalidate the current item when `next()` is called, and this was not reflected in the Rust binding prior to version 3.
To deal with this, the various iterators (`ResultIterator`, `RowIterator`, `MapIterator`, `SetIterator`, `FieldIterator`, `UserTypeIterator`, `KeyspaceIterator`, `FunctionIterator`, `AggregateIterator`, `TableIterator`, `ColumnIterator`) no longer implement `std::iter::Iterator`. Instead, since this is a [lending iterator,](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#generic-associated-types-gats) these types all implement a new `LendingIterator` trait. We define this ourselves because there is currently no widely-used crate that implements it.
观察修复的内容,可以找到大致有两类修复代码:
一类则是增加了生命周期的声明:
1 2 3 4 5 6 7 8 9 10 11
/// A field's metadata - pubstructField { + // + // Borrowed from wherever the value is borrowed from. + pubstructField<'a> { /// The field's name pub name: String, /// The field's value - pub value: Value, + pub value: Value<'a>, }
- // The underlying C type has no thread-local state, but does not support access - // from multiple threads: https://datastax.github.io/cpp-driver/topics/#thread-safety - unsafeimplSendforRowIterator {} + // The underlying C type has no thread-local state, and forbids only concurrent + // mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety + unsafeimplSendforRowIterator<'_> {} + unsafeimplSyncforRowIterator<'_> {}
Version 3.0 fixes a soundness issue with the previous API. The iterators in the underlying Cassandra driver invalidate the current item when `next()` is called, and this was not reflected in the Rust binding prior to version 3.
To deal with this, the various iterators (`ResultIterator`, `RowIterator`, `MapIterator`, `SetIterator`, `FieldIterator`, `UserTypeIterator`, `KeyspaceIterator`, `FunctionIterator`, `AggregateIterator`, `TableIterator`, `ColumnIterator`) no longer implement `std::iter::Iterator`. Instead, since this is a [lending iterator,](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#generic-associated-types-gats) these types all implement a new `LendingIterator` trait. We define this ourselves because there is currently no widely-used crate that implements it.
error[E0597]: `test1` does not live long enough --> src/main.rs:226:17 | 225 | let test1 = Test1::new(); | ----- binding `test1` declared here 226 | test2 = &test1; | ^^^^^^ borrowed value does not live long enough 227 | } | - `test1` dropped here while still borrowed 228 | 229 | println!("Test1 is {:?}", test2); | ----- borrow later used here
error[E0597]: `test1` does not live long enough --> src/main.rs:213:21 | 212 | let test1 = Test1::new(); | ----- binding `test1` declared here 213 | let test2 = test1.get_test2(); | ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough 214 | test3 = test2; 215 | } | - `test1` dropped here while still borrowed ... 219 | } | - borrow might be used here, when `test3` is dropped and runs the `Drop` code for type `Test2` | = note: values in a scope are dropped in the opposite order they are defined
- // The underlying C type has no thread-local state, but does not support access - // from multiple threads: https://datastax.github.io/cpp-driver/topics/#thread-safety - unsafeimpl<'a> SendforResultIterator<'a> {} + // The underlying C type has no thread-local state, and forbids only concurrent + // mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety + unsafeimplSendforResultIterator<'_> {} + unsafeimplSyncforResultIterator<'_> {}
implCassResult { /// Gets the number of rows for the specified result. // ...
/// Creates a new iterator for the specified result. This can be /// used to iterate over rows in the result. pubfniter(&self) -> ResultIterator { unsafe { ResultIterator( cass_iterator_from_result(self.0), cass_result_row_count(self.0), PhantomData, ) } } }
error[E0277]: `cassandra_cpp::cassandra::result::ResultIterator<'_>` is not an iterator --> examples/simple2.rs:19:16 | 19 | for row in result.iter() { | ^^^^^^^^^^^^^ `cassandra_cpp::cassandra::result::ResultIterator<'_>` is not an iterator | = help: the trait `Iterator` is not implemented for `cassandra_cpp::cassandra::result::ResultIterator<'_>` = note: required for `cassandra_cpp::cassandra::result::ResultIterator<'_>` to implement `IntoIterator`
/// An iterator over the results of a query. The result holds the data, so /// the result must last for at least the lifetime of the iterator. /// /// This is a lending iterator (you must stop using each item before you move to /// the next), and so it does not implement `std::iter::Iterator`. The best way /// to use it is as follows:
error[E0597]: `iter` does not live long enough --> example.rs | 21 | letmut iter = result.iter(); | -------- binding `iter` declared here 22 | whileletSome(row) = iter.next() { | ^^^^ borrowed value does not live long enough ... 28 | } | - `iter` dropped here while still borrowed 29 | 30 | println!("here will cause problem {:?}", tmp_row); |