返回首页 C 高阶教程

那些不安全的库函数

C 和 C++ 不能够自动地做边界检查,边界检查的代价是效率。一般来讲,C 在大多数情况下注重效率。然而,获得效率的代价是,C 程序员必须十分警觉以避免缓冲区溢出问题。

C语言标准库中的许多字符串处理和IO流读取函数是导致缓冲区溢出的罪魁祸首。我们有必要了解这些函数,在编程中多加小心。

一、字符串处理函数

strcpy()

strcpy() 函数将源字符串复制到缓冲区。没有指定要复制字符的具体数目!如果源字符串碰巧来自用户输入,且没有专门限制其大小,则有可能会造成缓冲区溢出!

我们也可以使用strncpy来完成同样的目的:
strncpy (dst, src, dst_size-1);
如果 src 比 dst 大,则该函数不会抛出一个错误;当达到最大尺寸时,它只是停止复制字符。注意上面调用 strncpy() 中的 -1。如果 src 比 dst 长,则那给我们留有空间,将一个空字符放在 dst 数组的末尾。
但是! strncpy()也不完全安全,也有可能把事情搞糟。即使“安全”的调用有时会留下未终止的字符串,或者会发生微妙的相差一位错误。

确保 strcpy() 不会溢出的另一种方式是,在需要它时就分配空间,确保通过在源字符串上调用 strlen() 来分配足够的空间

dst = (char *)malloc(strlen(src));   
strcpy(dst, src); 

strcat()

strcat() 函数非常类似于 strcpy(),除了它可以将一个字符串合并到缓冲区末尾。它也有一个类似的、更安全的替代方法 strncat()。如果可能,使用 strncat() 而不要使用 strcat()。

sprintf()、vsprintf

函数 sprintf() 和 vsprintf() 是用来格式化文本和将其存入缓冲区的通用函数。它们可以用直接的方式模仿 strcpy() 行为。换句话说,使用 sprintf() 和 vsprintf() 与使用 strcpy() 一样,都很容易对程序造成缓冲区溢出。

sprintf() 的许多版本带有使用这种函数的更安全的方法。可以指定格式字符串本身每个自变量的精度。sprintf 采用” * ”来占用一个本来需要一个指定宽度或精度的常数数字的位置,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来。

例如:  
sprintf (usage, "USAGE: %*s\n", BUF_SIZE, argv[0]);

二、字符读取函数

gets()

永远不要使用 gets()。
该函数从标准输入读入用户输入的一行文本,它在遇到 EOF 字符或换行字符之前,不会停止读入文本。也就是:gets() 根本不执行边界检查。因此,使用 gets() 总是有可能使任何缓冲区溢出。

作为一个替代方法,可以使用方法 fgets()。它可以做与 gets() 所做的同样的事情,但它接受用来限制读入字符数目的大小参数,因此,提供了一种防止缓冲区溢出的方法。

getchar()、fgetc()、getc()、read()

如果在循环中使用这些函数,确保检查缓冲区边界

scanf()系列 : sscanf()、fscanf()、vfscanf()、vscanf()、vsscanf()

scanf系列的函数也设计得很差。目的地缓冲区也可能会发生溢出。
同样地,我们用设置宽度也可以解决这个问题。

getenv()

使用系统调用 getenv() 的最大问题是您从来不能假定特殊环境变量是任何特定长度的。

三、使用安全版本的代码库

微软对于有缓冲溢出危险的API使用其开发的安全版本的库来替代。 SafeCRT自Visual Studio 2005起开始支持。当代码中使用了禁用的危险的CRT函数,Visual Studio 2005编译时会报告相应警告信息,以提醒开发人员考虑将其替代为Safe CRT中更为安全。

  1. 有关字符串拷贝的API

    例如:strcpy, wcscpy等

    替代的Safe CRT函数:strcpy_s

  2. 有关字符串合并的API

    例如:strcat, wcscat等

    替代的Safe CRT函数:strcat_s

  3. 有关sprintf的API

    例如:sprintf, swprintf等

    替代的Safe CRT函数:

    _snprintf_s

    _snwprintf_s

其它被禁用的API还有scanf, strtok, gets, itoa等等。 ”n”系列的字符串处理函数,例如strncpy等,也在被禁用之列。

上一篇: 输出 下一篇: 预处理