为什么gets函数很危险,不应该使用?

310

当我使用GCC编译使用gets()函数的C代码时,会收到以下警告:

(.text+0x34): 警告:`gets'函数是危险的,不应该使用。

我记得这与堆栈保护和安全有关,但我不确定具体原因。

如何消除此警告?为什么使用gets()会产生这样的警告?

如果gets()如此危险,为什么我们不能将其删除?


3
gets()缓冲区溢出攻击gets()函数是一种不安全的输入方法,因为它无法检测用户输入的大小。攻击者可以利用这个漏洞来向程序中的缓冲区写入超出其分配大小的数据,导致缓冲区溢出攻击。攻击者可以利用这种漏洞执行恶意代码或者修改程序的逻辑。为了避免这种攻击,建议使用更加安全的输入方法,如fgets() - EsmaeelE
7
请注意,scanf("%s", b) 存在与 gets 相同的问题。 - William Pursell
2
作为衡量WG14(负责C标准的ISO工作组)对此问题的严肃程度的一项措施,到目前为止,这是唯一一个正式从C标准中删除的功能。WG14有一个“永远不破坏现有代码”的政策(即使已经根本无法使用),他们打破了这个政策来摆脱gets() - Andrew
13个回答

1
我想向所有仍在其库中包含“只是为了防止有人仍然依赖它”的gets的C库维护者发出诚挚邀请:请将您的实现替换为等效的实现。
char *gets(char *str)
{
    strcpy(str, "Never use gets!");
    return str;
}

这将有助于确保没有人仍然依赖它。谢谢。

即使他们删除了fgets(),仍然有像scanf("%s", arr)或getline(&arr, 100500, stdin)这样的选项可用。当然,这仍然是一个麻烦,因为当你想写一些糟糕的代码时,通常也希望尽快完成并最小化脑力使用。我希望实现只会停留在警告上。 - mrKirushko
@mrKirushko 为什么他们要移除 fgets()?那个函数是安全的,不像 gets() - FFmpegEnthusiast

1

额外信息:

来自Linux Ubuntu的man 3 gets,你会看到以下内容(重点标记):

DESCRIPTION
       Never use this function.

从cppreference.com的wiki页面(https://en.cppreference.com/w/c/io/gets)中可以看到:注意:永远不要使用gets()函数。

注意事项

gets()函数不执行边界检查,因此该函数极易受到缓冲区溢出攻击。它不能安全地使用(除非程序在限制stdin上出现什么的环境中运行)。因此,该函数已被弃用,包括第三个C99标准的勘误表中,而在C11标准中完全删除了。fgets()gets_s()是推荐的替代品。

永远不要使用gets()

如您所见,该函数已被弃用,并在C11或更高版本中完全删除。

请改用fgets()gets_s()

这是我的fgets()演示用法,带有完整的错误检查:

来自read_stdin_fgets_basic_input_from_user.c

#include <errno.h>   // `errno`
#include <stdio.h>   // `printf()`, `fgets()`
#include <stdlib.h>  // `exit()`
#include <string.h>  // `strerror()`

// int main(int argc, char *argv[])  // alternative prototype
int main()
{
    char buf[10];

    // NEVER USE `gets()`! USE `fgets()` BELOW INSTEAD!

    // USE THIS!: `fgets()`: "file get string", which reads until either EOF is
    // reached, OR a newline (`\n`) is found, keeping the newline char in
    // `buf`.
    // For `feof()` and `ferror()`, see:
    // 1. https://en.cppreference.com/w/c/io/feof
    // 1. https://en.cppreference.com/w/c/io/ferror
    printf("Enter up to %zu chars: ", sizeof(buf) - 1); // - 1 to save room
                                                        // for null terminator
    char* retval = fgets(buf, sizeof(buf), stdin);
    if (feof(stdin))
    {
        // Check for `EOF`, which means "End of File was reached".
        // - This doesn't really make sense on `stdin` I think, but it is a good
        //   check to have when reading from a regular file with `fgets
        //   ()`. Keep it here regardless, just in case.
        printf("EOF (End of File) reached.\n");
    }
    if (ferror(stdin))
    {
        printf("Error indicator set. IO error when reading from file "
               "`stdin`.\n");
    }
    if (retval == NULL)
    {
        printf("ERROR in %s(): fgets() failed; errno = %i: %s\n",
            __func__, errno, strerror(errno));

        exit(EXIT_FAILURE);
    }

    size_t num_chars_written = strlen(buf) + 1; // + 1 for null terminator
    if (num_chars_written >= sizeof(buf))
    {
        printf("Warning: user input may have been truncated! All %zu chars "
               "were written into buffer.\n", num_chars_written);
    }
    printf("You entered \"%s\".\n", buf);


    return 0;
}

样本运行及输出:

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 read_stdin_fgets_basic_input_from_user.c -o bin/a && bin/a
Enter up to 9 chars: hello world!
Warning: user input may have been truncated! All 10 chars were written into buffer.
You entered "hello wor".

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c17 read_stdin_fgets_basic_input_from_user.c -o bin/a && bin/a
Enter up to 9 chars: hey
You entered "hey
".

0
简单来说,gets() 可能会存在危险,因为用户可能输入的内容比变量所能存储的空间还要大。第一个回答提到了 fgets() 以及它为什么更加安全。

2
这个答案只是不必要地重复了其他人已经说过的话。 - jpa
1
@jpa 是真的。我只想用尽可能少的词来表达。 - a573263

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接