内存越界

来源:互联网 发布:基础编程教程 编辑:程序博客网 时间:2024/05/22 10:23

内存越界:

何谓内存访问越界,简单的说,你向系统申请了一块内存,在使用这块内存的时候,超出了你申请的范围

内存越界使用,这样的错误引起的问题存在极大的不确定性,有时大,有时小,有时可能不会对程序的运行产生影响,正是这种不易重现的错误,才是最致命的,一旦出错破坏性极大。

什么原因会造成内存越界使用呢?有以下几种情况,可供参考:

 <textarea readonly="readonly" name="code" class="c++">

类似的还存在隐患的函数还有:strcat,vsprintf等
同样,memcpy, memset, memmove等一些内存操作函数在使用时也一定要注意。
        
当这样的代码一旦运行,错误就在所难免,会带来的后果也是不确定的,通常可能会造成如下后果:

1.破坏了堆中的内存分配信息数据,特别是动态分配的内存块的内存信息数据,因为操作系统在分配和释放内存块时需要访问该数据,一旦该数据被破坏,以下的几种情况都可能会出现。 
        *** glibc detected *** free(): invalid pointer:
        *** glibc detected *** malloc(): memory corruption:
        *** glibc detected *** double free or corruption (out): 0x00000000005c18a0 ***
        *** glibc detected *** corrupted double-linked list: 0x00000000005ab150 ***        

2.破坏了程序自己的其他对象的内存空间,这种破坏会影响程序执行的不正确性,当然也会诱发coredump,如破坏了指针数据。
3.破坏了空闲内存块,很幸运,这样不会产生什么问题,但谁知道什么时候不幸会降临呢?
通常,代码错误被激发也是偶然的,也就是说之前你的程序一直正常,可能由于你为类增加了两个成员变量,或者改变了某一部分代码,coredump就频繁发生,而你增加的代码绝不会有任何问题,这时你就应该考虑是否是某些内存被破坏了。

排查的原则,首先是保证能重现错误,根据错误估计可能的环节,逐步裁减代码,缩小排查空间。
检查所有的内存操作函数,检查内存越界的可能。常用的内存操作函数:
sprintf snprintf 
vsprintf vsnprintf
strcpy strncpy strcat 
memcpy memmove memset bcopy
内存越界的常见原因:


 1 写越界: 向10个字节的数组写入了20个字节;内存操作越界,如char szText[10]; memset(szText,0,30);
 2 错误的函数调用:   sprintf等fmt中的预定义和实际输入的变量数不一致,如sprintf(szData,"Name:%d title:%s",1)
 3 错误的调用方式:  用stdcall 的函数指针 调用pascall的函数


如果有用到自己编写的动态库的情况,要确保动态库的编译与程序编译的环境一致。

一个同行对内存越界处理的过程:

最近做一个C++服务端程序,在使用多线程时,程序有时候会崩溃,从VC的错题提示看是内存访问错误导致程序崩溃,单步执行跟踪也无法定位错误所在。

  根据个人的经验分析,这种错误是内存越界导致其他对象或者堆(heap)被破坏而引起非法内存访问,结果出现不可debug跟踪的程序崩溃。

  这个问题困扰了我几周,我分析程序代码,搜索所有strcpy,memcpy,memset等内存操作相关的函数,确认所有使用这些函数的地方都没有内存越界的问题
  我又分析程序中一些字符数组char[]变量,发现也不存在数组空间不够大的问题。非常头疼,找不出原因,又替换了程序中一个觉得可疑的模块,问题还是存在。

  今天又再继续观察程序运行打印的LOG,发现似乎每次崩溃的时候,好像都是在处理某个客户端请求时发生。
  于是就单步debug该处理过程,这一次果然发现问题了,
  原来该处理过程有用到sprintf函数来把客户端上传的参数组合成一个sql语句,
  因为没有检测参数的合法性,导致参数非法的时候,可能造成内存越界的情况,
  但是不是每次内存越界的结果都会有一样的现象。

下面使用代码来说明这个问题。

int functiontest(char *pszParam, int nLen, int nID)
{
char szSQL[500];
sprintf(szSQL,
" Select * from tabble1 " 
" where cond1 = %s and id = %d ",
pszParam,    //当pszParam为未初始化的野指针,可能导致内存越界
nID
);
//以下代码省略
//......
}

没错,就是上述使用sprintf()函数时产生了内存越界的错误,但是该函数本身的执行并不一定马上报错或者引起程序崩溃,
因为pszParam为野指针, strlen(pszParam)则是未确定的长度,
所以在有些情况下,strlen(pszParam)的长度可能远大于szSQL数组的大小512,sprintf()就会有内存越界的操作,
结果就会导致未定义的异常,我的程序里就导致了后续其他函数内存访问错误,直接程序崩溃。

其实在该函数中我忽略了nLen参数的意义,该参数是指pszParam的长度,
但是nLen == 0 的情况我确没有判断并加以处理,所以导致内存越界程序崩溃。

内存越界的一种定位方法:

就是查看确定越界的那段内存,然后查看内存的实际使用情况,看看是否有异常!!

多是数组出现越界!!

或者是一些字符串的拷贝问题。


上述代码明显内存越界,一个watch搞定。
结果:
[root@localhost root]# ./he
badstr = abc
fd = 1
fd = 2
fd = 0
badstr = 
Segmentation fault

如下操作:

设置断点:break 33

查看逆序的调用链:where

将调用栈上移一个函数调用:up

将调用栈下移一个函数调用:down

在使用watch时步骤如下:

1. 使用break在要观察的变量所在处设置断电;

2. 使用run执行,直到断点;

3. 使用watch设置观察点;

4. 使用continue观察设置的观察点是否有变化。


[root@localhost root]# ./gdb test
GNU gdb Red Hat Linux (6.6-8.fc7rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License,
welcome to change it and/or distribute copies of it under certain
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" fo
This GDB was configured as "i386-redhat-linux-gnu"...
Using host libthread_db library "/lib/i686/nosegneg/libthread_db.
(gdb) b main
Breakpoint 1 at 0x80484cb: file test.cpp, line 13.
(gdb) r
Starting program: /home/qiyk/test
Breakpoint 1, main () at test.cpp:13
13          char badstr[32] = "abc";
(gdb) n
14          int fd = 1;
(gdb) watch fd
Hardware watchpoint 2: fd
(gdb) c
Continuing.
Hardware watchpoint 2: fd[第一次人为修改,此处中断]
Old value = 6317008
New value = 1
main () at test.cpp:15
15          printf("badstr = %s\n", badstr);
(gdb) c
Continuing.
badstr = abc
fd = 1
Hardware watchpoint 2: fd[第二次人为修改,此处中断]
Old value = 1
New value = 2
main () at test.cpp:18
18          printf("fd = %d\n", fd);
(gdb) c
Continuing.
fd = 2
Hardware watchpoint 2: fd[第三次意外修改,此处中断]
Old value = 2
New value = 0
0x004ea367 in memset () from /lib/i686/nosegneg/libc.so.6
(gdb) bt[查看现场堆栈]
#0  0x004ea367 in memset () from /lib/i686/nosegneg/libc.so.6
#1  0x080484b8 in fn (str=0xbf92bd20 "") at test.cpp:7
#2  0x0804854d in main () at test.cpp:19
(gdb) up
#1  0x080484b8 in fn (str=0xbf92bd20 "") at test.cpp:7
7           memset(str, 0, 64);[问题点出现:str越界,导致fd值变为0]
(gdb) q
The program is running.  Exit anyway? (y or n) y

0 0
原创粉丝点击