我试图了解Rust的终身注释是如何帮助捕获免费使用(UAF)问题的,而不会在功能上妥协。下面是我的代码:
use std::rc::Rc;
struct Foo {
data: usize,
}
impl Foo {
pub fn begin_foo(&self) {
println!("Begin Foo");
}
pub fn end_foo(&self) {
println!("End Foo");
}
}
struct Bar {
foo: Option<Foo>,
}
impl Bar {
pub fn new(foo: Foo) -> Self {
Bar {
foo : Some(foo),
}
}
pub fn get_foo(&self) -> Option<& Foo>
{
match &self.foo {
Some(foo) => return Some(&foo),
None => return None,
}
}
}
struct FooBar<'a> {
bar : Option<Rc<Bar>>,
foo : Option<&'a Foo>,
}
impl<'a> FooBar<'a> {
pub fn new(bar : Option<Rc<Bar>>) -> Self {
FooBar {
bar : bar,
foo : None,
}
}
pub fn update_foo_1(&'a mut self)
{
if let Some(b) = &self.bar {
self.foo = b.get_foo();
}
}
// ERROR1
/*
pub fn update_foo_2(&mut self)
{
if let Some(b) = &self.bar {
self.foo = b.get_foo();
}
}
*/
pub fn invalidate_bar_1(&mut self) {
self.bar.take();
}
// Inorder to drop the RC, we have to pass self and not &self, which consumes the object.
pub fn invalidate_bar_2(self) {
if let Some(bar) = &self.bar {
println!("Strong ref count = {}", Rc::strong_count(bar));
drop(self.bar.unwrap());
}
}
pub fn use_foo(&self) {
match self.foo {
Some(foo) => println!("Foo.data = {}", foo.data),
None => println!("Foo is None"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn works()
{
println!("Works...");
let foo = Foo {data : 42};
let bar = Bar::new(foo);
let bar_rc = Rc::new(bar);
assert_eq!(1, Rc::strong_count(&bar_rc));
let mut foo_bar: FooBar = FooBar::new(Some(bar_rc));
foo_bar.update_foo_1();
foo_bar.use_foo();
foo_bar.invalidate_bar_1();
println!("Exiting works()...");
}
#[test]
fn fails()
{
println!("Fails...");
let foo = Foo {data : 42};
let bar = Bar::new(foo);
let bar_rc = Rc::new(bar);
assert_eq!(1, Rc::strong_count(&bar_rc));
let mut foo_bar: FooBar = FooBar::new(Some(bar_rc));
foo_bar.update_foo_1();
foo_bar.invalidate_bar_1();
foo_bar.use_foo();
println!("Exiting fails()...");
}
}要求FooBar保存Bar,但也是对Foo的引用,以避免多次调用Bar::get_foo()。FooBar::invalidate_bar_*()使Bar无效,使引用Foo悬空。
鉴于上述要求,下列调用顺序应有效:
foobar.update_foo();
foobar.use_foo();
foobar.invalidate_foo();以下是无效的调用序列,必须在编译时捕获:
foobar.update_foo();
foobar.invalidate_foo();
foobar.use_foo();但在我上面的代码中,这种情况并不完全发生。我所面临的挑战是:
FooBar::update_foo_1()我被迫用终身&'a注释self。否则,我将得到以下错误:error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/lib.rs:52:26
|
52 | if let Some(b) = &self.bar {
| ^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 50:5...
--> src/lib.rs:50:5
|
50 | / pub fn update_foo_1(& mut self)
51 | | {
52 | | if let Some(b) = &self.bar {
53 | | self.foo = b.get_foo();
54 | | }
55 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:52:26
|
52 | if let Some(b) = &self.bar {
| ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 42:6...
--> src/lib.rs:42:6
|
42 | impl<'a> FooBar<'a> {
| ^^
note: ...so that the expression is assignable
--> src/lib.rs:53:24
|
53 | self.foo = b.get_foo();
| ^^^^^^^^^^^
= note: expected `std::option::Option<&'a Foo>`
found `std::option::Option<&Foo>`当我使用
self进行独占引用的调用:error[E0502]: cannot borrow `foo_bar` as immutable because it is also borrowed as mutable
--> src/lib.rs:104:9
|
103 | foo_bar.update_foo_1();
| ------- mutable borrow occurs here
104 | foo_bar.use_foo();
| ^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here发布于 2020-07-27 08:31:55
通过调用它,您将使FooBar成为一个在安全锈蚀中很难定义的自引用结构。
pub fn update_foo_1(&'a mut self)
{
if let Some(b) = &self.bar {
self.foo = b.get_foo();
}
}编译器在这里正确地给出了错误:
error[E0502]: cannot borrow `foo_bar` as immutable because it is also borrowed as mutable
--> src/lib.rs:104:9
|
103 | foo_bar.update_foo_1();
| ------- mutable borrow occurs here
104 | foo_bar.use_foo();
| ^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here因为如果在更新调用之后允许任何不可变的借款,那就意味着在这一点上没有可变的借款,这也意味着您可以在此之后进行可变的借款。
如果bar字段的强计数为1,然后执行以下操作,会发生什么情况:
drop(foo_bar.bar.take());在foo字段FooBar中将有一个悬空引用,这是Rust禁止的。
发布于 2020-07-27 08:35:50
Rust的生存期注释有助于捕捉免费使用(UAF)问题,而不影响功能。
铁锈的终身注解不会“捕捉”任何东西。他们所做的就是告诉编译器一些它可能无法推断的事情(即借用跨度多长)。
“捕捉”UAF问题的是仿射类型系统,即在移动之后(除非是Copy),源是不可访问的,并且您不能移动活动用户的结构(也就是有活动引用的结构,所有的借方都必须在移动结构之前结束)。
您没有在任何地方使用移动,因此您的FooBar对象始终是有效的。
至于update_foo_1问题,我认为这是你告诉铁锈的合乎逻辑的结果:
pub fn update_foo_1(&'a mut self)这意味着该方法正在创建一个可持续所有'a的借入。
'a是结构的一个参数,因此它需要“超过”结构本身,而且您是说在这个生命周期内只借用结构,这意味着'a与结构完全相同(这是合理的,因为您借用的是结构的一部分),您所做的唯一事情就是静态地将自己锁在使用该结构之外。
在Rust中,表示图形(/自引用结构)是很困难的,这正是你在这里试图做的,但更根本的是,你的整个努力似乎被误导了,而且是建立在误解的基础上。
https://stackoverflow.com/questions/63105314
复制相似问题