内存的初始化与清零问题
貌似三年没写随笔了,就记录一下最近在搞的事情吧。
我们在写程序的时候,常常会忽略一件事情,那就是即时抹去敏感数据,例如用户口令和密钥,想象一下,在你的程序结束后,密钥被留在了空闲内存区,这时候如果有别的进程申请到它,或者有攻击者读到了内存数据,那就万般皆空了。
事实上,系统开发者们也考虑过这一点,并做了一定程度的防护——在Windows下,系统会在进程结束后自动清零被归还的内存,但是Linux系统不会,可能是出于性能考虑,Linux采用一种Lazy的思想来清零数据。当进程申请一块内存时,Linux只是分配一个虚拟的零页给它,这个时候程序再怎么读也只能从这块buffer中读到0,只有当写入动作发生(例如赋值给buffer中的某一字节)时,系统才会分配一块真正的物理内存(感兴趣的同志可以写个demo试试,只是持续申请内存而不赋值,再看看资源管理器,分给这个demo的内存应该是没有增加的),再将之前的虚拟内存映射并且覆盖写到它上面,Linux的页大小为4KB,此时会进行一次4KB大小的内存写入,举个例子:
int main() { unsigned char * buffer = malloc(1024); for (int i=0; i<sizeof buffer; i++) printf("%02X ", buffer[i]); buffer[3] = 0xff; for (int i=0; i<sizeof buffer; i++) printf("%02X ", buffer[i]); free(buffer); return 0; }
假设上面程序在Linux下运行,虽然第3行申请到的buffer没有初始化,但由于 copy-on-write 原则,第5、6行读到的值必然都是0x0,而且此时并没有实际的物理内存被分配给进程,只有第8行执行完毕时,系统才会分配一个page给当前进程,并且这个page的内容会被覆盖写为 00 00 00 ff 00 00 00......
这种机制可以防止一个进程读到其他进程释放掉的内存中的敏感数据,但是,所谓“防君子不防小人”,它能有效防止“合法”读取内存数据,但根本防不了 DMA、cold-boot这类攻击行为。所以,对敏感数据,还是要及时擦除才对。
然而,内存的清零并不是调用一下memset那么简单,如果在上述程序的第12行 free之前加上一句 memset(buffer, 0, sizeof buffer),那只要编译器开了优化,它就会被当作死代码清除掉的——Dead Store Elimination 在GCC -O1时就会被开启,而GCC默认的优化选项是-O2。
关于内存的清零,有一篇 USENIX Security 2017的文章讲的比较全面,这里贴出链接 https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf ,感兴趣的同志可以看看,有时间我再详细说一下这个问题。
- 解决solaris10上因为共享内存不足导致不能初始化sybase15.0.3服务的问题
- String对象内存分配和基本数据类型的默认值与初始化相关问题
- 内存初始化问题
- 出现“烫”“屯”字样,则说明你的程序是VC编写的DEBUG版程序,“烫”是未初始化的栈空间,“屯”是申请后未做过内存清零或COPY的堆内存。 写入文件是“屯”,则需要找下你申请的内存是不是没经过内存复
- core问题实例:未初始化的指针踩内存导致core堆栈显示错乱
- [置顶] 记录一个指针问题(内存空间的初始化)
- 经典String str = new String("abc")内存分配问题,研究下字符串到底该怎么样初始化,顺便很好的解释下Java的intern()(在文章结尾)
- 转:C/C++变量在内存中的位置以及初始化问题
- C/C++变量在内存中的位置及初始化问题
- 关于,函数调用是传值调用,初始化函数中重新分配内存,导致形参的值和实参的值不一致 问题分析
- 关于C++的new是否会对内存初始化的问题
- 关于C++的new是否会对内存初始化的问题
- 关于C++的new是否会对内存初始化的问题
- Viewpager + fragment,其中一些fragment不被保存在内存,切换导致初始化问题
- C/C++变量在内存中的位置以及初始化问题
- 尚硅谷第四课0722班 java-特殊流程控制 -数组元素的默认初始化-数组操作常见问题-Java内存的结构
- c++ 内存管理方式(解释《c++ primer》第二章关于变量默认初始化的问题)
- 关于C++的new是否会对内存初始化的问题
- tensorflow初始化参数内存占满问题
- 二维数组的列排序 考虑问题的全局性 声明数组必须要分配内存并清零