rust - C++ 代码的 Rust 等效项,用于演示对 UAF 的保护
问题描述
我试图了解 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()
我被迫self
用生命来注释&'a
。否则我会收到以下错误:
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>`
- 当我使用
&'a
来解决上述错误时,我无法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
解决方案
通过调用它,您正在使 FooBar 成为一个自引用结构,这在安全 rust 中很难定义。
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 所禁止的。
推荐阅读
- node.js - 等待 Electron 窗口成为特定的 URL?
- r - 在 R 中读取目录中的特定文件
- ios - 在 ios 中弹出到下一个 viewController
- python - fit() 到底在这里做什么?
- r - 使用 Rglpk 最小化简单函数
- python - Python 类垃圾收集时调用的方法
- javascript - 使用 Filesaver.js 的 D3JS 图形无法导出 Font Awesome 图标
- sleep - 使用 [nohup ksh] 运行脚本看不到睡眠进程
- systemtap - Systemtap (stap) 探测失败并显示“此语句可能会通过 [-Werror=implicit-fallthrough=]”
- java - 我们如何获得有关 iPad 的独特信息?