此段程序中,当程序调用 transdata(buf)时就会发生溢出。检查这个错误,首先在遇到 strcpy(buffer,str) 时,检查目的参数buffer,并找到在此之前出现的(第2 行),检测出它的长度应是24 个字节;接着,再检查源参数 str,发现它是经第 1 行(char *str)→ 第 9 行(buf)-->第 5 行(char buf[256])的数据流得到的,并且检测出其长度是 256 字节。此时,已经能初步确定可能产生溢出了。也可以报告存在缓冲区溢出的位置(第 3 行)。但是,如果要更精确地定位,则需要利用语法分析器继续从第 5 行定义数组buf[256]开始检查所有路径,这样就可以检测出在调用transdata(buf)时确实会发生溢出,最后报告造成溢出的路径(第 3、2、5、6、7、8 行)。
3.1.2 3 个参数的字符串函数
这类函数包括 memcpy、strncpy、_mbsncpy、strncat、wcsncat等。其特点是有 3 个形式参数,例如memcpy(buf,"M", count),当 count 说明的字节数大于 buf 的缓冲区长度时,发生溢出。处理此类函数同样采用数据流跟踪的方法。在前面这个例子中,就是要检查并比较 count 的大小是否超过了buf 的缓冲区的大小。
3.1.3 格式化控制的字符串处理函数
这类函数有两种不同的情况:一种包括 printf、fprintf。其特点是函数不能确定数据参数在什么地方结束,因此缓冲区溢出情况一般发生在说明的参数的个数与格式化字符串不匹配时。此类问题要分析格式化字符串与参数是否匹配。
例如下面的一段程序:
(1)int data=1234567890;
(2) printf("data=%d%n\n",data, &data); /* 显示data 的值, 并把显示字符的长度写到变量data 中*/
(3) printf("data=%d\n",data);此程序正常结果是: data=1234567890
data=10
若第2 行写成printf("%d%n\n",data),执行时就会把显示内容的长度写到变量data 存储那个数值所在的指向的内存里[2]。当然,此地址不能被访问。但如果精心设计这个输入值,就会造成缓冲区溢出攻击。分析时,当遇到 printf 时,先用词法分析器分析并记录两个双引号中含有“%”且非“%%”的个数,然 后分析其参数的个数是否与之匹配,就能发现此类问题。
另一类函数包括sprintf、swprintf,它通过格式化字符串进行输出,当字符串缓冲区小于格式化串所说明的长度时,会发生缓冲区溢出。此类问题要检查格式化字符串的动态长度, 并与实际区长度进行比较。
3.1.4 向缓冲区中读入字符串函数
其中一类函数包括 scanf、fscanf、sscanf 等。当说明的缓冲区小于实际读入的字符串长度时,发生缓冲区溢出。分析处理方法:跟踪说明缓冲区的参数在程序中的出现,检查其缓冲区长度,并提示用户使用带有限制输入字符长度的格式化字符串。如程序段:
char buffer[20]; scanf("%s",& buffer);
检查时,先分析出&buffer 所指向的缓冲区的大小,并发现%s 未受限制,说明可能产生溢出。接着采用提示用户使用scanf("%20s",& buffer)替换的处理方法[3]。
另一类函数包括 fgets、fgetc、gets、getc。如果限制读入数据大小的参数值超出目标缓冲区长度就会发生溢出。处理类函数采用数据流跟踪方法检查这两个数值。如 fgets (char *sint n,FILE *stream),此函数的功能是从输入流 stream 中读入字符,并存到 s 串中。这里,要分析 s 与 n 在程序中的定义,检查 s 的长度是不是小于n 的值。需要说明的是,强烈建议不使用 gets、getc,而是用fgets、fgetc 替代。
3.2 关于内存泄漏问题的解决途径
内存泄漏的原因是动态分配了内存,但没有释放,造成分配的内存不能再被使用。一般的情况是堆内存的泄漏,另外还包含系统资源的泄漏,比如核心态HANDLE、GDI Object、SOCKET、Interface 等[4]。
由于泄漏发生在程序运行时,因此要检测出内存泄漏问题不太容易。静态安全检查可以采用控制流跟踪的方法,通过分析所有可能的路径,以达到发现内存泄漏的目的,适用于new/delete、alloc/free、malloc/free、GlobalAlloc/GlobalFree 等函数。根据内存泄漏发生方式的不同,可以分以下几种情况分析。
3.2.1 忘记释放内存造成的内存泄漏
动态分配内存后,没有调用 delete 或free 等释放。这种内存泄漏只要分析所有路径是否存在只使用new、malloc 分配内存,但没有用 delete 或free 释放内存的情况即可。
3.2.2 delete 或free 的调用方法不正确造成的内存泄漏
此类问题比较常见,造成的后果也比较严重。处理此类问题采用语法分析的方法进行路径分析。
例如下面一段程序:
void function(int size)
{
char* p= new char[size]; if( size>=512 ){
printf“( Error!”); return;
}
//using the string pointed by p; delete p;
}
显然程序可能没到出口处就结束了,这样就造成了内存泄漏。
检查这类问题可以先采用 3.2.1 中的方法分析所有路径, 然后再检查是否存在没有使用 delete 或 free 释放内存就结束程序的情况。
3.2.3 隐式内存泄漏