微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

通过暂时假装它是“静态”来伪造引用的最安全方法是什么?

如何解决通过暂时假装它是“静态”来伪造引用的最安全方法是什么?

我的处境不太好,不得不假装一生。看起来有点像这样:

struct Bar<'a> {
    cr: &'a mut char,}

fn foo<D,F>(data: D,f: F) 
where
    D: 'static,// <-- !!!
    F: FnOnce(D),{ ... }


let mut c = '⚠';
let bar = Bar { cr: &mut c };
foo(???,|c| /* I need access to a `Bar` here! */); 

我必须调用奇怪的函数 foo。在传递给它的闭包中,我需要访问通过 Bar 传递的 foo(具有任何生命周期)。 (我知道在这个最小的例子中,我可以直接访问 bar,因为闭包可以访问它们的环境,但让我们假设这里不可能。)不幸的是,foo 需要 D: 'static

当然,在现实中一切都更加复杂。我知道 Barfoo 没有太大意义,但这是我尝试将问题分解为最小示例的尝试。


我如何使这项工作?我相信可以安全地完成这项工作(即没有未定义的行为),但我确定它需要 unsafe 关键字。

基本思想是将 Bar<'not_static> 临时转换为 Bar<'static>,然后确保它的寿命不会超过原始 c。我想知道如何最好地做到这一点。我的想法如下:

let mut c = '⚠';
let bar = Bar { cr: &mut c };
let bar_static: Arc<Bar<'static>> = unsafe {
    let extended = mem::transmute::<Bar<'_>,Bar<'static>>(bar);
    Arc::new(extended)
};

foo(bar_static.clone(),|bar| println!("{}",bar.cr));

if Arc::strong_count(&bar_static) != 1 && Arc::weak_count(&bar_static) != 0 {
    eprintln!("bad!");
    std::process::abort();
}

这个想法是在调用 c 后动态检查是否不再存在对 foo 的引用(除了我们持有的那个)。这应该可以防止 foodata 存储在静态变量或类似的东西中。我不期望它,但我宁愿结束整个过程,而不是在我的程序中存在内存不安全。

有了这个,我想,我可以确保参考(不正确的 'static)不会比实际数据长。但是:

  • 这种推理合理吗?有意义吗?
  • 让我担心的是 rustc 不认为 c 是在 unsafe 块之后借用的。我可以(在我的函数中)任意访问 c,尽管存在指向它的引用。这是“只要我不实际访问 c”的情况吗?或者更确切地说是那些“即时UB”案例之一?
  • mem::transmute 是适合这项工作的工具,还是我应该使用指针强制转换或其他方式?
  • 有什么更好的想法吗?

解决方法

答案是没有安全的方法可以改变对象的生命周期。生命周期是编译器保证该值在其使用期间始终有效。

最安全的方法是更改​​ foo 以包含 data: D 的生命周期。

fn foo<'a,D,F>(data: D,f: F)
where D: 'a,F: FnOnce(D),{
    f(data);
}

另一种方法是通过 Bar 更改 Arc<Mutex<char>> 及其使用方式并将其降级为 Weak<Mutex<char>>

struct Bar {
    c: Weak<Mutex<char>>,}

let t = 't';
let t = Arc::new(Mutex::new(t));

let bar = Bar { c: Arc::downgrade(&t) };

foo(bar,|b| {
    if let Some(c) = b.c.upgrade() {
        let mut c = c.lock().unwrap();
        println!("{}",*c);
        *c = 'b';
    }

});
println!("{}",t.lock().unwrap());

但是,如果您无法更改 fooBar 并且您确定引用的对象不会被删除,那么您可以使用 std::mem::transmute 来更改生命周期你的对象。

如文档中所述:

transmute 非常不安全。有很多方法可以使用此函数导致未定义的行为。 transmute 应该是绝对的最后手段。

unsafe fn to_static<'a>(r: Bar<'a>) -> Bar<'static> {
    std::mem::transmute::<Bar<'a>,Bar<'static>>(r)
}


let mut t = 't';
let bar = Bar { c: &mut t };

let static_bar = unsafe { to_static(bar) };

foo(static_bar,|b: Bar| {
    println!("{}",b.c);
    *b.c = 'b';
});

println!("{}",t);

上面的例子之所以有效,是因为 t 位于调用 foo 的范围内,而且我们知道 t 是静态的,因此使用起来完全没问题。但是,如果 foostatic_bar 发送到另一个线程,并且 t 不是静态的,那么结果是未定义的行为。

如果您能保证 foo 会在它被调用的范围结束之前完成,那么您可以使用 Mutex 来执行以下操作。

let t = 't';
let t = Arc::new(Mutex::new(t));

{ // create scope here

    let mut tlock = t.lock().unwrap(); // MutexGuard<char>
    let t_ref_mut = &mut *tlock;       // get &mut char  -- lifetime of mutex guard
    let bar = Bar { c: t_ref_mut };
    
    let static_bar = unsafe { to_static(bar) };
    
    // foo must be guaranteed to complete before the scope ends.

    foo(static_bar,|b| {
        println!("{}",b.c);
        *b.c = 'b';
    });

} // MutexGuard goes out of scope and lock should be released

// sanity check that lock is released and value is altered
println!("{}",t.lock().unwrap());

可以查看完整的源代码here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?