《基于Linux的C编程与内核导读》连载(19)
时间:2007-06-13 来源:gaowp
4.3.1 安全性问题
由于内存使用的问题,常常会引发一些安全性问题。例如前面提到的溢出、内存泄漏等问题。这在系统的安全问题中虽然只是一小部分,但由于内存操作而引发的安全问题是很常见的。
溢出错误的一种情况是,当使用静态内存时,定义了一个100元的数组,如果要将数据量大于100的一组数据写入这个数组,将发生溢出错误。虽然通常情况下程序将因此而错误终止,但在某些时候还是会因为覆盖了此缓冲区之后的数据而导致错误的发生。要解决溢出的问题,一个最基本的想法是在用户程序中根据缓冲区大小截断所输入的数据。这一设想可以通过下面的例子实现。
程序4.1如下:
#include <stdio.h> #include <string.h>
void upcase(char *inputstring,char *newstring,int N);
int main(void) { char str1[6]; /*定义数组str1,其长度为6*/ int N; N=sizeof(str1); /*求出数组str1的最大长度*/ upcase(“Everybody”,str1,N);/*调用子函数upcase()*/
printf(“str1=%s \n”,str1); return 0; }
void upcase(char *inputstring,char *newstring,int N) { int counter;
strcpy(newstring,inputstring,N-1);/*将字符串拷贝到str1*/ for(counter=0;counter<(N-1);counter++) { if(newstring[counter]>=97&&newstring[counter]<=122) /*如果是小写字母,转换为大写字母*/ newstring[counter]-=32; } newstring[N]=’\0’; } |
结果分析:
在这个程序中,字符串str1的长度故意设置的很小,当实际输入的字符串的长度大于字符串所限制的长度时,超出的部分将被截去。子函数upcase的作用就是截去超出部分,并将字母转换为大写,(注意:由于在主函数中的后续操作中把str1看作是字符串进行操作,所以只拷贝进去5个字符,而str1[5]存放’\0’)。此程序运行的结果为:
EVERY |
可以看出上面的程序非常简单,但是千万不要小看了它,稍微不注意,就会产生各种错误,把内存部分称为程序编程的“雷区”一点都不夸张,下面列出几种大家可能犯的错误:
(1)将子函数中的strncpy函数用strcpy代替。显然这时若输入字符串大于数组长度,就会产生溢出。虽然有时候程序运行并不一定出错,而且也确实将一个大的字符串全部拷贝到小的字符数组里了。但正是这种隐患的存在,才是大多数软件最头痛的。测试的时候一切正常,但用户使用时不知道什么时候就会出错,因此我们在编程时,尤其是涉及内存的时候,一定要十分小心和严谨。
(2)upcase只有两个参数,并不把n当作参数传递给子函数。有人很可能会做出上面的错误选择,他们的理由很简单,在子函数中同样可以用sizeof(newstirng)来求出字符数组的最大长度。这种想法看似可行,但在实际中是错误的,在子函数中求N=sizeof(newstring),可以发现n的值为4,并非字符数组的最大长度6。而且不论主函数中定义str1的长度为多少,子函数中的N=sizeof(newstirng)始终为4(在windows环境下调试发现N始终为2)。因此我们在编写子函数时,一定要把数组长度作为一个参数传递过去,以避免一些意想不到的错误。
(3)主函数中的N=sizeof(str1)语句换成N=strlen(str1)。这也会导致错误,因为在strlen函数是求出从字符串首到字符串结束标准’\0’间的长度。在str1尚未初始化或未存入字符串时,strlen(str1)求出的值是一个随机值,即从str1首地址开始遇到第一个字符’\0’为止,而这个’\0’的位置是无意义的,并且是随机的。
(4)另外必须注意一点,在过去我们学习C语言时,是没有strncpy函数的,它的功能都是由strcpy函数来实现的。当strcpy有两个参数时,其作用相对于linux中的strcpy函数;当strcpy有三个参数时,其作用大体相当于linux中的strncpy函数。为什么后者叫做大体相当呢,那是因为strncpy并不自动在N个字符后加上一个’\0’,而在WINDOWS下strcpy(str1,str2,N)则会将str2中前N个字符拷贝到str1中后,再加上一个’\0’。这一点要特别注意,否则在使用完strncpy函数后不人为的加上一个’\0’,则str1作为字符串输出时,就会有错误出现,因为str1字符串没有结束标志’\0’。