经典错误案例-四
案例4:被改变的入口参数
错误引用:不祥
现象:
某函数Method(int nParamSize, char *pszParamBuf)。调用Method时送入nPathSize, pszFilePath作为入口参数,在函数返回之后。发现nPathSize被变更。
现象分析+排错过程:
现象1:首先代码复查,是否由于笔误错误使用了引用类型。
代码复查无误,则需检查Method函数内部进行了哪些操作。因C/C++规定,函数的入口参数群和函数内部的局部变量事实上是存在于同一块堆栈区域的。
比如,类似
int Function(int nParam1, int Param2, int Param3)
{
int nVar1;
char *pszVar2;
long nVar3;
}
结构如图:
|
栈顶 |
|
|
nVar3 |
|
|
pszVar2 |
|
|
nVar1 |
|
|
函数返回地址 |
|
|
copy_nParam1 |
|
|
copy_nParam2 |
|
|
copy_nParam3 |
|
情况2:可能影响到nPathSize的值的范围,应该在nPathSize定义区域的位置。nPathSize定义位置上下文:
int nSomeData = -1;
int nPathSize = 0;
char szFilePath[256] = { 0 };
分析:正常情况下。声明为类似int这种值传递参数方式时,相应函数调用送入参数是使用拷贝方式将该类型复制在堆栈空间内(这也就是为什么尽量避免使用对象参数的原因),复制过去的值和原来的值是没有关连的,即使在函数内部将这个参数作为局部变量使用,也不应该会影响到外部的值。
可以看到Method有两个参数,第二个参数是一个指针pszFilePath,结合情况2,OK,问题就在这里了。
Method方法传入入口参数时,会将pszFilePath本身的值—也就是指针的值—复制在堆栈内,如果在Method方法内部操作pszFilePath缓冲区,事实上也就是在操作调用Method方法的函数的堆栈空间。如图:
Method方法内部对pszFilePath进行了内存拷贝操作(strcpy/memcpy/…),而超出了pszFilePath分配的内存块大小,导致覆盖了nPathSize的值。如果szFilePath变为pszFilePath改在堆上进行内存动态分配,也有可能影响到邻近的内存块的值。导致出错。
将szFilePath声明的内存块扩大增容,故障消失。
点评:
堆栈/函数入口参数/函数返回地址在内存中存放在连续的空间上,稍有不慎,就有可能导致出错。严重的情况下,如果覆盖掉函数返回地址,则程序将会在函数返回时跳转到到未知地址,此时程序很容易产生莫名的错误乃至崩溃。著名的缓冲区溢出漏洞其实使用的就是这种原理,只不过在溢出的数据中经过精细的构造,可以控制在溢出时覆盖堆栈中的返回地址为攻击者所期望的地址,然后开始执行蠕虫代码。
很简单,不是么。




最近评论