segment fault
来源:互联网 发布:mac 截图 任意区域 编辑:程序博客网 时间:2024/06/12 18:58
分类: C/C++
就转载了其中一篇,与内存管理以及指针越界访问的知识有关。
原文地址:http://blog.csdn.net/redredbird/article/details/6242450
其实也就鸡毛蒜皮的小事,本来不想记录在博客上的,不过这个bug背后隐藏的东西确实比较有记录的价值,如果说解bug就像是解初高中数学题,那么有的bug就像一道出得很漂亮的题,短小精干但背后隐藏的信息量却很大,一下子就让你记住了背后的那些定理概念。
事情是这样的,segment fault,程序被谋杀,现场在libc的calloc里。发生在libc中的segment fault其实不少见,碰得最多的怕就是memcpy,一般都是过大的数据拷贝导致程序的stack corruption,这种情况通过检查程序的backtrace可以看出(被破坏的栈很容易被发现)。但这次的栈调用树却很完整;想想calloc要干 什么事,分配由size指定的内存然后填0而已,于是检查calloc的唯一的外部参数size,不大的一个数,可以说是再平凡不过的一次调用,话说回 来,就算是诡异的size出现,也应该只会导致calloc失败而已(比如请求的size太大),也不该是segment fault这种‘不可捕捉’的运行期错误。每次遇到这种程序的生命停留在系统级library的情况的时候,一定要抑制自己怀疑‘是不是xx库的bug’ 之类的冲动,毕竟这是每天都会被亿万人使用的libc中的calloc,让我发现它的bug估计和中头彩的概率差不多。真正的凶手肯定在其他地方。
怎么解决的过程就不提了,主要是不值得提。总之感谢google大神。
出错程序大概是这样一个模式,看过或写过一些driver的人应该熟悉这种用法,即将data load直接分配在用来管理data load的struct后面:
- struct SomeBody_A {
- /* some fields not too large */
- /*...*/;
- unsigned char *data;
- };
- struct SomeBody_B {
- /* some fields not too large */
- /*...*/;
- unsigned char *data;
- };
- void func1 (void)
- {
- struct SomeBody_A *A_Body;
-
- A_Body = calloc(1, sizeof(struct SomeBody_A) + 100);
- /* access data following up A_Body, but overwrite the memory space allocated, such as... */
- A_Body->data = ((unsigned char *)A_Body)+sizeof(struct SomeBody_A);
- A_Body->data[105] = 0;
- }
- void func2 (void)
- {
- B_Body = calloc(1, sizeof(struct SomeBody_B) + 100);
- /*......*/
- }
- void run (void)
- {
- func1();
- func2();
- }
calloc/malloc等的原理其实是通过内核的brk系统服务申请虚拟内存,申请的单位以4k/页为粒度。然后自己再维护申请回来的 virtual memory,毕竟不是所有程序都会每次都向calloc/malloc请求大于一个页的memory的,所以calloc/malloc通过内核申请的 virtual memory总是会比用户需要的更多(除了brk是以页来满足calloc/malloc请求这个原因以外,calloc/malloc也需要将用户申请 的内存对齐或多申请一些空间做管理用的meta data),然后分成block的形式,再按需分配给需要的应用程序。
跑第一个函数func1()的时候其实已经发生‘内存访问越界’了,之所以在那里没有发生‘命案’的原因就是如前所述:calloc真正分配的虚拟 内存是比用户请求的大的。如果像func1()中那样在后面多写了5个字节是不会导致mmu的页异常的。也就是说,在上面那个程序里,这多写的5个字节除 了程序员自己小心以外,编译期和运行期都是不能帮助你发现它们的,这种错误其实最好是让编译器帮忙识别,但像上面那么做编译器是没法发现的,这种允许程序 员随心所欲操作memory的做法正是c语言这种贴近汇编和硬件的‘高级’语言的强大之处,当然,对于经验不够丰富的人来说,这也为可能出现的各种 segment fault埋下了祸根。
那为什么第二个函数func2()的calloc却遭‘报应’了呢?前面提到calloc/malloc以block的形式来管理已经分配的虚拟内 存,这些blocks被划分为‘allocated’和‘free’两种状态,对用户的分配请求,所要做的自然就是找到一个能满足大小的free的 block,对释放请求也是将对应的'allocate'的block和邻近的'free'的block合并。对这些blocks的管理自然少不了一些维 护它们的‘元数据’本身(如‘下一个free block的地址’等),为了更有效的处理,这些元数据本身也和block放在了一起,比如放在每个block的头或尾。说到这里,真相就很明白了。为了 方便说明,见下图:
假设func1()中调用calloc分配到的block就是Allocate(1),后面的P表示meta data,比如里面有指向最近的free块‘Free(1)’的指针。那么,当func1()中的越界访问发生后,那多写的5个0就把P的指针破坏掉了! 这个破坏当然在func1()中不会导致出错,但是,当到func2()中用calloc分配内存时,calloc试图从Allocated(1)块后面 的P找到最近的free block时,这个指针已经被corruption了,这个track操作本身导致了calloc中的segment fault。
c语言就是这样,你用得到,那么它威力无穷;你用不好,它随时会制造隐藏在你程序里的定时炸弹。
- segment fault
- Segment fault
- Segment fault
- Segment fault
- segment fault
- segment fault
- Segment fault
- segment fault
- segment fault
- 调试segment fault
- ijsgutenprint出现segment fault
- segment fault 理解
- 求解 segment fault 问题
- Segment fault (Core dump)
- Segment Fault(core dumped)
- 重新认识Segment fault
- OK6410 Qt Segment fault
- Segment Fault & Abort trap
- Python的“绑定机制”(self)
- spark 的相关配置
- iOS学习(三)Objective-C 类的声明
- Android性能优化 浅析
- Android Viewflipper详解。。
- segment fault
- Android application详解与用法
- oracle常用的sql
- 常用的正则表达式
- Java 多线程 并发编程
- 在编写html文件当中submit 和 button有什么区别
- onActivityResult
- sys.sysobjects (Transact-SQL)
- C++引用调用、内联函数、带默认形参值的函数