编译器通过禁止分配借来的值来防止什么灾难?

时间:2016-03-25 18:36:28

标签: rust borrowing

来自Programming in Rust (PDF)的示例:

#[derive(Debug)]
enum IntOrString {
    I(isize),
    S(String),
}

fn corrupt_enum() {
    let mut s = IntOrString::S(String::new());
    match s {
        IntOrString::I(_) => (),
        IntOrString::S(ref p) => {
            s = IntOrString::I(0xdeadbeef);
            // Now p is a &String, pointing at memory
            // that is an int of our choosing!
        }
    }
}

corrupt_enum();

编译器不允许这样做:

error[E0506]: cannot assign to `s` because it is borrowed
  --> src/main.rs:13:17
   |
12 |             IntOrString::S(ref p) => {
   |                            ----- borrow of `s` occurs here
13 |                 s = IntOrString::I(0xdeadbeef);
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `s` occurs here

但是假设它确实如此;怎么样呢

  

现在p&String,指的是我们选择的int内存!

是件坏事?

1 个答案:

答案 0 :(得分:4)

让我们为所涉及的类型构成内存布局。 IntOrString将有一个字节来确定它是哪个变体(0 =数字,1 =字符串),然后是4个字节,它们将是一个数字或开头的地址一组UTF-8字符。

让我们在内存中分配s 0x100。变体位于0x100,值为0x101,0x102,0x103,0x104。另外,假设值的内容是指针0xABCD;这是字符串的字节存在的地方。

当使用匹配臂IntOrString::S(ref p)时,p将设置为值0x101 - 它是对值的引用,值从0x101开始。当您尝试使用p时,处理器将转到地址0x101,读取值(地址),然后从该地址读取数据。

如果,编译器允许您此时更改s,则新数据的新字节将替换0x101中存储的值。在该示例中,存储在该值处的“地址”现在指向任意位置(0xDEADBEEF)。如果我们尝试使用“字符串”,我们就会开始读取非常不符合UTF-8数据的内存字节。

这些都不是学术性的,这种确切的问题可能发生在格式良好的C程序中。在的情况下,程序将崩溃。在不好的情况下,可以在程序中读取您不应该的数据。甚至可以注入shellcode,然后让攻击者能够运行他们在你的程序中编写的代码。

请注意,上面的内存布局是非常简化,实际String更大,更复杂。