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

9ceoxa92  于 2022-12-02  发布在  其他
关注(0)|答案(6)|浏览(137)

我用这个代码:

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

要防止可能的缓冲区溢出,以便可以传递随机长度的字符串,最好的方法是什么?
我知道我可以通过调用以下示例来限制输入字符串:

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

但是我更希望能够处理用户输入的任何内容,或者这不能用scanf安全地完成,我应该用fgets吗?

wfveoks0

wfveoks01#

Kernighan和Pike在他们的书The Practice of Programming(非常值得一阅读)中讨论了这个问题,他们通过使用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);
}

注意,这仍然限制了输入的大小,如'buffer'所提供的。如果你需要更多的空间,那么你必须做内存分配,或使用一个非标准的库函数为你做内存分配。
请注意,POSIX 2008(2013)版本的scanf()系列函数支持字符串输入(%s%c%[)的格式修饰符m(一个赋值分配字符)。它不采用char *参数,而是采用char **参数,并为它读取的值分配必要的空间:

char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
    printf("String is: <<%s>>\n", buffer);
    free(buffer);
}

如果sscanf()函数无法满足所有转换规范,则在函数返回之前,将释放为类似%ms的转换分配的所有内存。

hfwmuf9z

hfwmuf9z2#

如果您使用的是gcc,则可以使用GNU扩展a说明符让scanf()分配内存来保存输入:

int main()
{
  char *str = NULL;

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

**编辑:**正如Jonathan指出的,您应该参考scanf手册页,因为说明符可能不同(%m),并且您可能需要在编译时启用某些定义。

bzzcjhmw

bzzcjhmw3#

大多数情况下,fgetssscanf的组合可以完成这项工作。另一件事是编写自己的解析器,如果输入格式正确的话。还要注意,第二个示例需要做一些修改,以便安全使用:

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

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

上面的代码丢弃了输入流,直到但不包括换行符(\n)。您需要添加一个getchar()来使用它。

s4n0splo

s4n0splo4#

直接使用scanf(3)及其变体会带来很多问题。通常,用户和非交互用例是根据输入行来定义的。很少会看到这样的情况:如果没有找到足够的对象,更多的行就可以解决问题,但这是scanf的默认模式。(如果用户不知道在第一行输入数字,则第二行和第三行可能也没有帮助。)
至少如果你知道你的程序需要多少输入行,你就不会有任何缓冲区溢出...

kwvwclae

kwvwclae5#

限制输入的长度显然更容易,你可以通过使用循环来接受任意长度的输入,一次阅读一位,必要时为字符串重新分配空间......
但这需要做大量的工作,所以大多数C程序员只是将输入截断为某个任意长度。我想你已经知道了这一点,但使用fgets()并不允许你接受任意数量的文本--你仍然需要设置一个限制。

czq61nw1

czq61nw16#

创建一个函数为你的字符串分配所需的内存并不需要太多的工作,这是我以前写的一个小c函数,我总是用它来读入字符串。
它将返回读取的字符串或如果内存错误发生NULL。但要注意,你必须free()你的字符串,并始终检查其返回值。

#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;
}

相关问题