如何防止scanf导致C中的缓冲区溢出?

时间:2009-10-25 16:56:38

标签: c overflow scanf

我使用此代码:

while ( scanf("%s", buf) == 1 ){

什么是防止可能的缓冲区溢出的最佳方法,以便它可以传递随机长度的字符串?

我知道我可以通过调用例如:

来限制输入字符串
while ( scanf("%20s", buf) == 1 ){

但我更愿意处理用户输入的内容。 或者这不能使用scanf安全地完成,我应该使用fgets?

6 个答案:

答案 0 :(得分:56)

在他们的书The Practice of Programming(非常值得一读)中,Kernighan和Pike讨论了这个问题,他们通过使用snprintf()创建具有正确缓冲区大小的字符串来解决它,以便传递给scanf()系列功能。实际上:

int scanner(const char *data, char *buffer, size_t buflen)
{
    char format[32];
    if (buflen == 0)
        return 0;
    snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
    return sscanf(data, format, buffer);
}

注意,这仍然将输入限制为“缓冲区”提供的大小。如果你需要更多空间,那么你必须进行内存分配,或者使用为你做内存分配的非标准库函数。


请注意,scanf()系列函数的POSIX 2008(2013)版本支持字符串输入的格式修饰符m(赋值分配字符)%s,{{ 1}},%c)。它不是取%[参数,而是采用char *参数,并为它读取的值分配必要的空间:

char **

如果char *buffer = 0; if (sscanf(data, "%ms", &buffer) == 1) { printf("String is: <<%s>>\n", buffer); free(buffer); } 函数无法满足所有转换规范,则在函数返回之前释放为sscanf()分配的所有内存(如转换)。

答案 1 :(得分:29)

如果你正在使用gcc,你可以使用GNU-extension a说明符让scanf()为你保存输入分配内存:

int main()
{
  char *str = NULL;

  scanf ("%as", &str);
  if (str) {
      printf("\"%s\"\n", str);
      free(str);
  }
  return 0;
}

编辑:正如Jonathan指出的那样,您应该查阅scanf手册页,因为说明符可能不同(%m),您可能需要启用某些定义编译。

答案 2 :(得分:9)

大多数情况下,fgetssscanf的组合可以胜任这项工作。如果输入格式正确,另一件事就是编写自己的解析器。另请注意,您的第二个示例需要进行一些修改才能安全使用:

#define LENGTH          42
#define str(x)          # x
#define xstr(x)         str(x)

/* ... */ 
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array); 

上面将输入流丢弃但不包括换行符(\n)。您需要添加getchar()才能使用此功能。还要检查您是否到达了流末尾:

if (!feof(stdin)) { ...

就是这样。

答案 3 :(得分:4)

直接使用scanf(3)及其变体会带来许多问题。通常,用户和非交互式用例是根据输入行定义的。很少见到如果没有找到足够的对象,更多行将解决问题,但这是scanf的默认模式。 (如果用户不知道在第一行输入数字,则第二行和第三行可能无效。)

至少如果你fgets(3)知道你的程序需要多少输入行,你就不会有任何缓冲区溢出......

答案 4 :(得分:1)

限制输入的长度肯定更容易。您可以通过使用循环接受任意长的输入,一次读取一下,根据需要为字符串重新分配空间......

但这是很多工作,所以大多数C程序员只是以任意长度切断输入。我想你已经知道了,但是使用fgets()不会允许你接受任意数量的文本 - 你仍然需要设置一个限制。

答案 5 :(得分:1)

创建一个为字符串分配所需内存的函数并不是那么多工作。 这是我前段时间写的一个小函数,我总是用它来读字符串。

它将返回读取字符串或者如果发生内存错误,则返回NULL。 但请注意,您必须释放()您的字符串,并始终检查它的返回值。

#define BUFFER 32

char *readString()
{
    char *str = malloc(sizeof(char) * BUFFER), *err;
    int pos;
    for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++)
    {
        if(pos % BUFFER == BUFFER - 1)
        {
            if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL)
                free(str);
            str = err;
        }
    }
    if(str != NULL)
        str[pos] = '\0';
    return str;
}