Group of Software Security In Progress

GoSSIP @ LoCCS.Shanghai Jiao Tong University

Practical Memory Checking With Dr. Memory

论文下载

这篇文章发表在CGO11上,作者是Google的Derek Bruening和MIT的Qin Zhao。DrMemory是在DynamoRIO上开发的内存检查工具。源码在https://github.com/DynamoRIO/drmemory/tree/master/drmemory

Design Overview

DrMemory用Shadow Memory记录目标进程的整个地址空间和每个线程的寄存器,对每个Byte记录三种状态:

  • Unaddressable:不可访问内存(unmaped,栈指针之上的,堆里未分配的)
  • Uninitialized:可访问,未初始化内存
  • Defined:可访问,写过数据的内存

Fig

每个Byte的不同状态用2 bit的编码来记录,可以直接用or来传播状态:

Fig

检查规则:

Fig

优化:

  1. 把整个内存空间分成64KB大小的块,每个块记录一个指向Shadow Memory的指针,相同状态块指向同一个Shadow Memory。
  2. 读写Shadow Memory的过程优化成fastpath和slowpath。Fastpath对一些简单的常用操作做了优化。Slowpath负责处理其他负责的情况。
  3. 相同状态的4字节合并用一个2 bit的状态码表示。普通的Shadow Memory用2 bit 表示4 Bytes。不能合并的4字节用0b10表示。重新分配一个Shadow Memory,每2 bit表示1 Byte。
  4. 固定的Shadow Memory(special block)设成只读的,要改写special block时会触发异常,用异常处理来分配新的Shadow Memory(regular block)。
  5. Callstack优化:对stack里有字符串的情况,和lib里相同的函数callstack相同的情况,对栈帧的Shadow Memory做优化,指向一个相同的special block。
  6. 像strcpy,memset,strlen一类的函数有做过优化,会对Shadow Memory的记录有影响。要替换成普通的实现。
  7. 对栈操作做优化,如:push,pop等。
  8. Shadow Memory block用偏移记录,而不是地址。

Heap Tracing

Hook内存分配的函数,malloc,new,free,delete等。记录下return address,post-hook插桩。分配内存时,多加一个redzone,检测堆溢出。在post-hook插桩的地方更新Shadow Memory。

静态链接的函数通过符号信息区分。

对多线程里栈指针切换做特殊处理。相对偏移修改栈指针的认为是安全的。设置一个阈值,如果栈指针变化范围超过阈值认为是线程切换。

Fig

Syscall Tracing

Linux和ntoskernl的syscall是已知的,可以查文档。Win32k的syscall要用特别的方法处理。对Win32k的syscall,记录下前24个参数,如果参数里有指针,而且不是unaddressable的,就把这个指针指向的地址拷出来备份,不超过2KB,而且没有unaddressable的内存。把其中uninitialized的内存写一个magic。执行完syscall之后,把相应的地址uninitialized的数据做比较,如果有改写过,就标记成defined。

内存泄漏检查

程序退出的时候检查内存泄漏。只关心内存对齐的指针,和defined内存。

  • 如果内存有指针指向,就不是leak。
  • 如果没有指针指向,就是leak。
  • 如果指针指向中间,检查是不是c++的类,如:new[]
  • 检查前面的数据类型是不是size_t,多重继承检查前面的虚表指针是不是合法。

实验结果

与Memcheck的性能比较:

Fig

Windows上的性能:

Fig

各种优化效果和自己的比较:

Fig

内存使用量结果:

Fig