什么是内存不安全的情况

rust Apr 29, 2021

linux进程内存划分

一个进程在执行时,所占用的内存虚拟空间被划分为好几个区域,每个区域我们称为段。
常见的几个段:

  1. 代码段: 编译后机器码所在的区域。一般是只读的。
  2. bss段:存放为初始化的全局变量和静态变量的区域。
  3. 数据段: 存放初始化的全局变量和静态变量的区域。
  4. 函数调用栈:存放函数参数,局部变量的区域(在java里面就是栈帧)
  5. 堆:存放动态分配内存的区域。

堆和栈的区别

  1. 栈上保存的局部变量在退出当前作用域的时候会自动释放
  2. 堆上分配的空间没有作用域,需要手动释放。
  3. 栈上分配的空间大小是编译阶段就可以确定的。
  4. 栈有一个确定的最大长度,超过了就产生stackoverflow错误
  5. 堆的空间会大一些,堆上的内存耗尽了,就会产生outofmemory错误。

段错误

进程空间中的每个段通过硬件MMU映射到真正的物理空间;在这个映射过程中,我们还可以给不同的段设置不同的访问权限,比如代码段就是只能读不能写;进程在执行过程中,如果违反了这些权限,CPU会直接产生一个硬件异常;硬件异常会被操作系统内核处理,一般内核会向对应的进程发送一条信号;如果没有实现自己特殊的信号处理函数,默认情况下,这个进程会直接非正常退出;如果操作系统打开了core dump功能,在进程退出的时候操作系统会把它当时的内存状态、寄存器状态以及各种相关信息保存到一个文件中,供用户以后调试使用。
Rust的主要设计目标之一,是在不用自动垃圾回收机制的前提下避免产生segfault

内存安全

Memory safety is the state of being protected from various software bugsand security vulnerabilities when dealing with memory access, such as bufferoverflows and dangling pointers.

内存安全流派

  1. rust借鉴了cyclone,形成了通过编译器的内存所有者,生命周期,安全检查的方式从根本上解决内存安全的问题。
  2. 还有一派就是比c和cpp更细致的内存管理,当出现问题的时候能快速定位。(我没用过类似的语言,所以就不推荐了)

内存不安全的例子

  1. 空指针:解引用空指针是不安全的。这块地址空间一般是受保护的,对空指针解引用在大部分平台会产生segfault
  2. 野指针:野指针是未初始化的指针。它的值是取决于它这个位置以前留下的内容。所以是无法预料的,对它解引用可能会造成segfault,也可能不会,靠运气。
  3. 悬空指针:悬空指针指的是内存空间在被释放了之后,继续使用。它跟野指针类似,同样会读写已经不属于这个指针的内容。
  4. 使用未初始化的指针:不只是指针类型,任何一种类型不初始化就直接使用都是危险的,造成的后果我们完全无法预测。
  5. 非法释放:内存分配和释放要配对。如果对同一个指针释放两次,会制造出内存错误。如果指针并不是内存分配器返回的值,对其执行释放操作,也是危险的。
  6. 缓冲区溢出:指针访问越界了,结果也是类似于野指针,会读取或者修改临近内存空间的值,造成危险。
  7. 执行非法函数指针:如果一个函数指针不是准确地指向一个函数地址,那么调用这个函数指针会导致一段随机数据被当成指令来执行,是非常危险的。
  8. 数据竞争:在有并发的场景下,针对同一块内存同时读写,且没有同步措施。会导致和预期不同的结果。

ref

  1. 深入浅出rust
  2. cyclone https://cyclone.thelanguage.org/
  3. zig https://en.wikipedia.org/wiki/Zig_(programming_language)
  4. valgrind https://valgrind.org/info/about.html