来自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内存!
是件坏事?
答案 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
更大,更复杂。