ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C++使用过程中的溢出问题详解

C++使用过程中的溢出问题详解 内容在C/C程序里有一类非常典型的问题那就是溢出问题。现在分别来分析一下常见的数组溢出整数溢出缓冲区溢出栈溢出和指针溢出等。1、数组溢出在C语言中数组的元素下标是从0开始计算的所以对于n个元素的数组a[n] 遍历它的时候是a[0],a[1],...,a[n-1]如果遍历到a[n]数组就溢出了。12345678voidprint_array(inta[],intn){for(inti 0; i n; i){a[i] a[i1];//当i n-1时就发生了数组越界printf(“%d\n”, a[i]);}}上面的循环判断应该改为for (int i 0; i n-1; i)2、整数溢出整数的溢出分为下溢出和上溢出。比如对于有符号的char(signed char)类型来说它能表示的范围为[-128,127]之间而对于无符号的char(unsigned char)来说 它能表示的范围为[0,255]。那么对于下面的代码12345678signedcharc1 127;c1 c11;//发生上溢出c1的值将变为-128signedcharc2 -128;c2 c2-1;//发生下溢出c2的值将变为127unsignedcharc3 255;c3 c31;//发生上溢出c3的值将变为0unsignedcharc4 0;c4 c4-1;//发生下溢出c4的值将变为255从上面的例子可以看出当一个整数向上溢出将会变为最小值而向下溢出将会变为最大值。来看下面的溢出代码该代码负责提供一个小写字母转换表但存在一个整数溢出问题1234567891011voidBuildToLowerTable(void)/* ASCII版本*/{unsignedcharch;/* 首先将每个字符置为它自己 *//*ch为unsigned char无符号数当ch值为UCHAR_MAX ch将会发生向上溢出变为0导致循环无法退出。*/forch0; ch UCHAR_MAX;chchToLower[ch] ch;/* 将大写字母改为小写字母 */for( ch ‘A; ch ‘Z; ch )chToLower[ch] ch a– ‘A;}该代码负责在内存中查找指定的字符ch但也存在一个溢出问题123456789101112void*memchr(void*pv, unsignedcharch,size_tsize ){unsignedchar*pch (unsignedchar*) pv;/*当size的值为0的时候由于size是无符号整数因此会发生下溢出变为一个最大的整数 循环也将无法退出*/while( -- size 0 ){if( *pch ch )return(pch );pch;}return( NULL );}3、缓冲区溢出缓冲区溢出一般是调用了一些不安全的字符串操作函数比如strcpystrcat等这些字符串操作函数在拷贝或者修改目标位置的时候并不判断长度是否会超过目标缓存或者设置参数超过了目标缓存能容纳的大小而造成的溢出问题。1234567891011121314151617181920212223voidfunc1(char* s){charbuf[10];/*此时buf只有10个字节如果传入的s超过10个字节就会造成溢出*/strcpy(buf, s);}voidfunc2(void){printf(Hacked by me.\n);exit(0);}intmain(intargc,char* argv[]){charbadCode[] aaaabbbb2222cccc4444ffff;DWORD* pEIP (DWORD*)badCode[16];*pEIP (DWORD)func2;/*badCode字符串超过了10个字节传递给func1会造成栈上缓冲区溢出而且由于badCode经过精心构造在溢出的时候根据函数的调用约定规则会覆盖栈上的返回地址指向了func2。所以在func1退出的时候会直接调用func2*/func1(badCode);return0;}4、栈溢出无论是内核栈还是应用层的栈都是有一定大小限制的。如果在栈上分配的空间大于了这个限制就会造成栈大小溢出破坏栈上的数据。比如局部变量过多或者递归调度嵌套太深都会造成栈溢出。比如1234567891011121314intinit_module(void){charbuf[10000];//buf[]分配在栈上但10000的空间超过了栈的默认大小8KB。//所以发生溢出memset(buf,0,10000);printk(kernel stack.\n);return0;}voidcleanup_module(void){printk(goodbye.\n);}MODULE_LICENSE(GPL);//应用栈的大小对少内核栈的大小多少什么时候容易栈溢出5、指针溢出一块长度为size大小的内存bufferbuffer的首地址为p那么buffer最后一个字节的地址psize-1而不是psize。如果写成了psize就会造成溢出比如下面的代码123456789101112void*memchr(void*pv, unsignedcharch,size_tsize ){unsignedchar*pch ( unsignedchar* )pv;unsignedchar*pchEnd pch size;while( pch pchEnd ){if( *pch ch )return( pch );pch ;}return( NULL );}上面的代码用于查找内存中特定的字符位置。对于其中的while循环平时执行似乎都没有任何问题。但是考虑一种特别情况即pv所指的内存位置为末尾若干字节那么因为pchEnd pchsize所以pchEnd指向最后一个字符的下一个字节将会超出内存的范围即pchEnd所指的位置已经不存在。知道了问题所在那么可以将内存的结尾计算方式改为1234567pchEnd pv size – 1;while( pch pchEnd ){if( *pch ch )return( pch );pch ;}pchEnd指向了最后一个字节。但是检查循环内部的执行情况可知由于pch每增加到pchEnd1时都会发生上溢。因此循环将无法退出。 于是可以将程序修改为下面的代码。将用size变量来控制循环的退出。这样就不会存在任何问题了。1234567891011void*memchr(void*pv, unsignedcharch,size_tsize ){unsignedchar*pch ( unsignedchar* )pv;while( size -- 0 ){if( *pch ch )return( pch );pch ;}return( NULL );}大家知道--size的效率一般比size--的效率高。那么是否可以将循环的判断条件改为下面的语句呢while --size 0 ……实际上这是不行的。因为当size0时由于size是无符号数那么它将发生下溢变成了size所能表示的最大正数循环也将无法退出。
返回列表