xixitalk's snippet

Post Longer Than 140 Characters Tweets

Jul 9, 2016 - Comments

函数栈破坏检查

函数栈破坏检查 平台 ARM linux,编译器GCC

产品部分音频功能是台湾某家公司提供的,开源了部分代码,二次开发实现一个功能发现偶现程序段错误,根据coredump显示最后异常函数是一个类似printf的打印函数,而这个打印函数基本可以肯定是没有问题的,并且出现了多次。程序这样跑飞其中一个可能的原因就是函数栈遭到破坏,函数执行完弹栈返回函数调用的下条指令时候出错了。所以写了一个函数栈检查函数。

funcA()
{
	funcB()
	RETURN_EXP
}

funcB()
{
	funcC();
	funcD();
	funcE();
}

原理:函数调用过程是进函数funcB把调用funcB地方的下一条指令地址RETURN_EXP_ADDR(RETURN_EXP语句的地址)压栈,funcB函数执行完弹栈从栈把RETURN_EXP_ADDR赋值给PC就完成了函数返回,funcB函数在返回之前栈里的返回地址RETURN_EXP_ADDR一直在栈里,而funcB函数里本身可能会再调用多个其他函数,这些函数funcC、funcD和funcE都不应该擦写函数funcB的返回地址RETURN_EXP_ADDR。进funcB开始,就把RETURN_EXP_ADDR地址从栈里找出来,funcC、funcD和funcE执行完了都检查一下栈里的RETURN_EXP_ADDR是否被破坏。

#define STACK_FIND_SIZE 2048 //find from SP to SP+STACK_FIND_SIZE
void funcStackCheck(unsigned long *psp, unsigned long lr)
{
if(psp && (*psp != lr))
{
assert(0);
}
}
/*find lr in stack mem address*/
unsigned long * funcLrFindPoint(unsigned long sp, unsigned long lr)
{
unsigned long * psp = NULL;
int n = 0;
for(n = 0; n < STACK_FIND_SIZE; n++)
{
psp = (unsigned long *)(sp + n);
if(*psp == lr)
{
break;
}
}
return psp;
}
void testFunc(void)
{
unsigned long *psp = NULL;
unsigned long v_lr = 0, v_sp = 0;
v_lr = (unsigned long)__builtin_return_address(0); //R14 arch ARM
v_sp = (unsigned long)__builtin_frame_address(0); //R13 arch ARM
psp = funcLrFindPoint(v_sp, v_lr);
if(NULL == psp)
{
/*Not found lr in stack*/
assert(0);
}
/*do something*/
funcStackCheck(psp, v_lr);
/*do something more*/
funcStackCheck(psp, v_lr);
}

问题很快定位,问题原因是其中一个函数接收消息的时候,recv函数输入可接收数据size比buffer实际size大,大小都是宏值,不容易发觉,这个buffer恰好是局部变量(栈空间),从而recv长消息的时候把栈破坏了。

特别提醒:如果定位的testFunc函数里局部变量过大,可能需要调整STACK_FIND_SIZE大小。定位平台是ARM linux,编译是GCC,其他平台和编译器以此类推。


知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

Tags: bugfix

malloc封装检查越界 我的AStyle配置选项