首先,如果主动使用unsafe关键字则代表编程者完全了解对应的风险并且需要自己保证内存安全性。
1. 心智模型
- safe代码应当能够无条件依赖
unsafe代码。 - 相反
unsafe应当足够鲁棒,尤其是涉及泛型以及和周围状态交互时。
2. 一些rust认为是安全但并不希望出现的情况
- 泄露内存 -> 浪费内存,但没有悬垂指针
- 整型溢出 -> 溢出但没有内存相关的违反
- 获得原始指针 -> 只要不解引用都是安全的
- 死锁 -> hanging,但没有crashing
- 静态条件 -> 不一致状态,但是没有内存问题
2.1. 内存泄露
1 | |
如上,std::mem::forget会释放s1内存,但是其对应的底层系统资源不会被关闭。一个应用场景是rust代码将某个文件描述符交给外部的c代码。
Box::leak会返回可变引用,但是owner同时变成unreachable。通常用于返回的引用需要在剩下整个程序的生命周期存活。如果返回的引用被drop,则会造成内存泄露。
2.2. 获取原始指针
1 | |
MMIO时,需要进行编译器认为unsafe的解引用操作
1 | |
访问原始指针时,编译器依赖于编程人员
- 指针指向内存区域的liveness(i.e. 没有被freed)
- 指针指向内存区域的ownership(i.e. 没有被reallocated)
2.3. 整型溢出
- $+, -, *$能上溢/下溢
- debug模式->panic
- release模式->2的补码wrap
- $«, »$能够移位超过自身类型位数
- debug模式->panic
- release模式->类型位数取模
- $/, \%$ INT_MIN-1会导致panic
2.3.1. 整型溢出的处理
类似于GCC内置尽可能明确操作。
1 | |
2.4. 内联汇编
1 | |
include一个汇编文件
1 | |
2.5. 全局可变静态变量
1 | |
2.6. union访问
1 | |
用union标识Atag
1 | |
2.7. FFI
1 | |
2.8. Transmute
transmute<T, U>将一个T类型的值转换为类型U。注意,T和U的类型大小应当相同。
1 | |
transmute对应move语义,因此drop()不会被调用。
1 | |