今天,编写的程序仍然利用这些调用,因为从来没有人教开发人员避免使用它们。某些人从各处获得某个提示,但即使是优秀的开发人员也会被这弄糟。他们也许在危险函数的自变量上使用自己总结编写的检查,或者错误地推论出使用潜在危险的函数在某些特殊情况下是“安全”的。
第 一位公共敌人是 gets()。永远不要使用 gets()。该函数从标准输入读入用户输入的一行文本,它在遇到 EOF 字符或换行字符之前,不会停止读入文本。也就是:gets() 根本不执行边界检查。因此,使用 gets() 总是有可能使任何缓冲区溢出。作为一个替代方法,可以使用方法 fgets()。它可以做与 gets() 所做的同样的事情,但它接受用来限制读入字符数目的大小参数,因此,提供了一种防止缓冲区溢出的方法。例如,不要使用以下代码:
void main() { char buf[1024]; gets(buf); } |
而使用以下代码:
#define BUFSIZE 1024 void main() { char buf[BUFSIZE]; fgets(buf, BUFSIZE, stdin); } |
C 编程中的主要陷阱
C 语言中一些标准函数很有可能使您陷入困境。但不是所有函数使用都不好。通常,利用这些函数之一需要任意输入传递给该函数。这个列表包括:
- strcpy()
- strcat()
- sprintf()
- scanf()
- sscanf()
- fscanf()
- vfscanf()
- vsprintf
- vscanf()
- vsscanf()
- streadd()
- strecpy()
- strtrns()
坏消息是我们推荐,如果有任何可能,避免使用这些函数。好消息是,在大多数情况下,都有合理的替代方法。我们将仔细检查它们中的每一个,所以可以看到什么构成了它们的误用,以及如何避免它。
strcpy()函数将源字符串复制到缓冲区。没有指定要复制字符的具体数目。复制字符的数目直接取决于源字符串中的数目。如果源字符串碰巧来自用户输入,且没有专门限制其大小,则有可能会陷入大的麻烦中!
如果知道目的地缓冲区的大小,则可以添加明确的检查:
if(strlen(src) >= dst_size) { /* Do something appropriate, such as throw an error. */ } else { strcpy(dst, src); |
完成同样目的的更容易方式是使用 strncpy() 库例程:
strncpy(dst, src, dst_size-1); dst[dst_size-1] = '\0'; /* Always do this to be safe! */ |
如果 src 比 dst 大,则该函数不会抛出一个错误;当达到最大尺寸时,它只是停止复制字符。注意上面调用 strncpy() 中的 -1。如果 src 比 dst 长,则那给我们留有空间,将一个空字符放在 dst 数组的末尾。
当然,可能使用 strcpy() 不会带来任何潜在的安全性问题,正如在以下示例中所见:
strcpy(buf, "Hello!"); |
即使这个操作造成 buf 的溢出,但它只是对几个字符这样而已。由于我们静态地知道那些字符是什么,并且很明显,由于没有危害,所以这里无须担心 ― 当然,除非可以用其它方式覆盖字符串“Hello”所在的静态存储器。
确保 strcpy() 不会溢出的另一种方式是,在需要它时就分配空间,确保通过在源字符串上调用 strlen() 来分配足够的空间。例如:
dst = (char *)malloc(strlen(src)); strcpy(dst, src); |
strcat()函数非常类似于 strcpy(),除了它可以将一个字符串合并到缓冲区末尾。它也有一个类似的、更安全的替代方法 strncat()。如果可能,使用 strncat() 而不要使用 strcat()。
函数 sprintf()和 vsprintf()是用来格式化文本和将其存入缓冲区的通用函数。它们可以用直接的方式模仿 s