前言
在嵌入式领域,我们编码调试时,经常会出现段错误 (core dumped),根据个人经验,最常见的莫过于“空指针”、“野指针”、内存泄露(一般是堆区泄露)、栈溢出、越界访问等。
笔者在此只阐述栈溢出(栈区被破坏)的情况,因为这种问题在项目中极具有典型性、隐藏性、且不易被跟踪发现。
栈破坏的具体表现为:在该函数(栈帧内)执行时,不会立即崩溃(Segmentation fault)。而是函数执行完后返回时,返回地址等栈保存的信息被破坏。才出现崩溃。
项目需求
该项目是需要”通过GSM模块的GPRS功能来实现上网功能,需要跑少量流量,模拟人为的上网功能。防止被运营商封杀”主要应用于VOIP语言网关产品当中。正是因为有这样的需求,才有了现有BUG的出现。
技术上来说,主要是通过配置web前端的URL地址,后台随机产生前端配置的URL,通过下发到GSM模块,GSM模块再发给运营商。这样实现了一个数据流量的交互。下图为web配置。
前端定义的URL最大长度为128字节。
我们再来看后端发送代码。
从上述可以看出at_cmd_buffer就是我们需要发送给GSM模块的缓存buffer。再来看看其定义的大小(64字节)
缓存区大小是64字节,而URL最大是128字节,外加AT指令的长度肯定超过了128字节
程序崩溃
GDB调试,函数调用栈已经被完全破坏了。
打印显示at_cmd_buffer缓存区已经出现错误。
原因分析
程序员在找BUG时,最好是能够深层次去理解其中的机理,这样有助于经验的积累,也是一笔宝贵的财富。既然是栈溢出,那么我就得来从函数调用栈来分析。
为了更好的理解,我们必须有程序堆、栈、段等概念。
函数调用都有自己的一个栈帧。
安全编程
很多人在编程的时候随手拈来调用一些系统库函数(C库),尤其在字符串操作时候,边界问题是一直需要我们注意的。以下有两种不同的写法。
int sprintf( char *buffer, const char *format,… ); <不安全>
int snprintf(char *str, size_t size, const char *format, …) <安全>