内存越界访问 您所在的位置:网站首页 行程卡显示非法访问是什么情况导致的呢 内存越界访问

内存越界访问

#内存越界访问| 来源: 网络整理| 查看: 265

内存越界访问有两种:一种是读越界,即读了不属于自己的数据,如果所读的内存地址是无效的,程度立刻就崩溃了。如果所读内存地址是有效的,在读的时候不会出问题,但由于读到的数据是随机的,它会产生不可预料的后果。另外一种是写越界,又叫缓冲区溢出,所写入的数据对别人来说是随机的,它也会产生不可预料的后果。

内存越界访问造成的后果非常严重,是程序稳定性的致命威胁之一。更麻烦的是,它造成的后果是随机的,表现出来的症状和时机也是随机的,让BUG的现象和本质看似没有什么联系,这给BUG的定位带来极大的困难。

一些工具可以够帮助检查内存越界访问的问题,但也不能太依赖于工具。内存越界访问通常是动态出现的,即依赖于测试数据,在极端的情况下才会出现,除非精心设计测试数据,工具也无能为力。工具本身也有一些限制,甚至在一些大型项目中,工具变得完全不可用。比较保险的方法还是在编程是就小心,特别是对于外部传入的参数要仔细检查。

我们来看一个例子:

#include #include

int main(int argc, char* argv[]) {     char str[10];     int array[10] = {0,1,2,3,4,5,6,7,8,9};

    int data = array[10];     array[10] = data;

    if(argc == 2)     {         strcpy(str, argv[1]);     }

    return 0; } 这个例子中有两个错误是新手常犯的:

其一:int array[10] 定义了10个元素大小的数组,由于C语言中数组的索引是从0开始的,所以只能访问array[0]到array[9],访问array[10]就造成了越界错误。

其二:strcpy(str, argv[1]);这里是否存在越界错误依赖于外部输入的数据,这样的写法在正常下可能没有问题,但受到一点恶意攻击就完蛋了。除非你确定输入数据是在你控制内的,否则不要用strcpy、strcat和sprintf之类的函数,而要用strncpy、strncat和snprintf代替。

下面是一个简单的例子: int a; char b[16]="abcd"; int c;

a = 1; c = 2; printf("a=%d,c=%d\n", a,c); memset(b, 0,32); //注意这里访问越界了,你只有16字节空间,却修改了32字节 printf("a=%d,c=%d\n", a,c);

你可以看出,在memset前后,两个printf语句打印出来的值并不一样,因为memset越界后修改了a或者c的值(由于不同编译器对变量在空间中顺序的安排可能有不同策略,因此我用两个变量,希望能抓到越界信息。对于VC,debug模式下系统添加了很多填充字节,你可能需要增加越界的数量才能看到效果)

2. 为什么增加一个变量后程序就崩溃了? 增加一个变量后,内存中变量的布局也发生了变化。如果一个内存越界破坏了一个不含指针的结构,程序虽然逻辑不对,但是不至于崩溃。但是如果增加变量后,内存访问越界破坏了一个指针,则会导致程序崩溃。

例如:

int a; char b[128]; //bool c; char* d=new char[128]; int e;

b[136] = '\0'; b[137] = '\0'; b[138] = '\0'; b[139] = '\0'; strcpy(d, "haha"); 注意, b访问越界了8个字节位置处的4个字节。如果没有c,那么越界破坏了e变量,不会导致程序崩溃。但是加上c之后,破坏的变量可能就是d了,由于指针被破坏后,一旦访问就是内存访问违例,导致程序崩溃。

这也解释了为什么交换顺序会导致程序崩溃。如果上面情况没有变量c,你交换e和d,结构也是类似的,程序也一样要崩溃。

3. 为什么有些情况越界了程序也没错? 这主要是说这个话的人对什么是“错”没有正确的认识。程序不是只有崩溃了才是错!你破坏了别的变量,那个变量总有被使用的时候,尽管那个变量不会导致诸如程序崩溃、报警之类的严重错误,但是其计算结果必然是错误的。你说“程序没错”,是因为你根本没有发现错误而已。这种情况甚至比程序直接崩溃还要恶劣,因为程序一旦崩溃你肯定会去查,可以在导致真正严重的问题之前就把问题解决了。而如果计算错误隐藏到很晚,你的损失就可能很大了。(例如,一颗卫星上天了,你才发现一台仪器由于软件故障无法测量真正的数据,那得多少损失?)

4. 如何解决内存访问越界问题? 老实说没有好的方法。遇到这种问题,首先你得找到哪里有内存访问越界,而一个比较麻烦得问题在于,出现错误得地方往往不是真正内存越界得地方。对于内存访问越界,往往需要进行仔细得代码走查、单步跟踪并观察变量以及在调试环境得帮助下对变量进行写入跟踪(如VC6就有一旦变量被修改就break得机制)。

memset()的效率以及源码分析

void *memset(void *s, int ch, size_t n); 作用:将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作。 不知道有没有像我一样把memset当作万能的初始化工具,例如: int arr[n]; memset(arr,1,n*sizeof(int)); 这样得到的arr数组一定不是全0,而是16843009,下面解释原因。 首先,变量类型的本质只是标志从某一内存地址开始读取的位数,强制转换就是改变读取位数的大小。

下面来看memset的实现:(代码来自《C标准库》P398)

[cpp]  view plain copy void *(memset) (void *s,int c,size_t n)   {       const unsigned char uc = c;       unsigned char *su;       for(su = s;0 


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有