About Segmentation Fault in Linux ( SIGSEGV )

    技术2022-05-20  38

    FROM:http://apps.hi.baidu.com/share/detail/14298506 About Segmentation Fault in Linux ( SIGSEGV )        刚拜读了一篇《Segmentation fault in linux.pdf》,下面是自己总结的一些内容 segmentation fault引发内核产生SIGSEGV信号;SIGSEGV是在访问内存时发生的错误,当用户态程序访问不允许访问内存,或者以错误的方式访问允许访问的内存时产生SIGSEGV。       下面来说下常见的几种引发SIGSEGV的情况:  1. 错误的访问类型引发SIGSEGV 1  #include <stdio.h> 2  #include <stdlib.h> 3  int  main()  { 4      char * s  =  "hello world"; 5     s [ 1 ]  =  'H'; 6  } *  上面的函数引发SIGSEGV是因为“hello world”作为一个常量字符串,在编译链接后会放在ELF可执行目标文件的.rodata(存储只读数据,比如printf语句中的格式和开关语句的跳转表)部分,由于这片内存区域是只读的,这就引起可SIGSEGV.2.访问了不属于进程地址空间的内存 1  #include <stdio.h> 2  #include <stdlib.h> 3  4  int  main()  { 5      int * p  = ( int *) 0xC0000fff; 6      *p  =  10; 7  } *    众所周知,linux地址空间顶部的四分之一是预留给内核的。即0xffffffff到0xc0000000是用户代码不可见的存储器,上述程序访问的0xc0000fff刚好访问的是这片地址空间。3.访问了不存在的内存 1  #include <stdio.h> 2  #include <stdlib.h> 3  4  int  main ()  { 5      int  * a  =  NULL; 6      * a  =  1; 7  } *   这个很显然呐....4.栈溢出了,有可能引发SIGSEGV(栈溢出是缓存区溢出的一种) 01  #include <stdio.h> 02  #include <stdlib.h> 03  04  int *  foo()  { 05      int  a  =  10; 06  07      return  & a; 08  } 09  10  int  main()  { 11      int * b; 12  13     b  =  foo(); 14      printf ( "%d /n " ,  *b); 15  } *     如上程序编译时会报:“warning: function returns address of local variable”,这是编译器在提醒你,该程序由栈溢出的危险。按照常理,&a在foo运行结束后应该被是释放,再以*b访问,相对于当前的程序是发生了栈溢出,但是该沉序实际运行正常,这是因为linux中典型的页的大小为4K,当栈溢的幅度小于页的大小时,不会产生SIGSEGV. 01  #include <stdio.h> 02  #include <stdlib.h> 03  04  05  char *  foo()  { 06       char  buf [ 8192 ]; 07  08       memset ( buf ,  0x55 ,  sizeof( buf)); 09       return  buf; 10  } 11  12  int  main()  { 13       char *  c; 14  15       c  =  foo(); 16       printf ( "%#x /n " ,  c [ 5000 ]); 17  } *    虽然上面程序的栈溢已经超出了 4K 大小,可运行仍然正常。这是因为 C 教程中提到的“栈自动释放”实际上是改变栈指针,而其指向的内存,并不是在函数返回时就被回收了。在我们的例子中,所访问的栈溢处内存仍然存在。无效的栈内存(即栈指针范围外未被回收的栈内存)是由操作系统在需要时回收的,这是无法预测的,也就无法预测何时访问非法的栈内容会引发 SIGSEGV。*    再看下面的程序,它访问一个未分配的栈内存: 01  #include <stdio.h> 02  #include <stdlib.h> 03  04  int  main()  { 05      char *  c; 06  07      c  = ( char *) & c  –  8192  * 2; 08      * c  =  'a'; 09      printf ( "%c /n " ,  * c); 10  } *    该函数的栈溢为16K,但是我们依旧没有看到SIGSEGIV,这是因为在内核的page fault处理函数规定,栈溢小于64k左右都是没有问题的,栈会自动扩展。5.关于堆 01  #include <stdio.h> 02  #include <stdlib.h> 03  04  #define K 1024 05  int  main ()  { 06      char *  c; 07      int  i  =  0; 08  09      c  =  malloc ( 1); 10      while ( 1)  { 11         c  +=  i * K; 12         * c  =  'a'; 13         printf ( "overflow %dK /n " ,  i); 14         i  ++; 15      } 16  } *    如上函数,对应malloc的大小不同,SIGSEGV推迟溢出的大小也不同。*    再看一个: 01  #include <stdio.h> 02  #include <stdlib.h> 03  #define K 1024 04  int  main ()  { 05      int *  a; 06  07      a  =  malloc ( sizeof( int)); 08      * a  =  100; 09      printf ( "%d /n " ,  * a); 10      free ( a); 11      printf ( "%d /n " ,  * a); 12  } *   上面的程序不一定会出现SIGSEGV,至于这其中的原委,看了栈溢出的话大家应该已经明白了吧:free后的内存空间不会立即归还给操作系统。6.函数跳转到非法的地址上执行。*   这个跟上次的那个缓存区溢出实验中  Featherain的做法 差不多,就是通过某种手段把函数的返回地址给改了...


    最新回复(0)