为什么获取功能如此危险以至于不应该使用它?

时间:2009-11-07 18:55:08

标签: c fgets buffer-overflow gets

当我尝试编译使用GCC的gets()函数的C代码时,

我明白了

警告

  

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

我记得这与堆栈保护和安全性有关,但我不确定为什么?

如何删除此警告?为什么有关于使用gets()的警告?

如果gets()非常危险,那么我们为什么不能删除它?

11 个答案:

答案 0 :(得分:156)

为了安全地使用gets,您必须确切地知道要读取的字符数,以便您可以使缓冲区足够大。只有在您确切知道要阅读的数据时,您才会知道。

您希望使用具有签名的fgets,而不是使用gets

char* fgets(char *string, int length, FILE * stream);

fgets,如果它读取整行,则会将'\n'留在字符串中;您将不得不处理它。)

它仍然是1999年ISO C标准的语言的官方部分,但是 它被2011年标准正式删除。大多数C实现仍然支持它,但至少gcc会对使用它的任何代码发出警告。

答案 1 :(得分:133)

答案 2 :(得分:21)

因为gets在从 stdin 获取字节并将它们放在某处时没有进行任何检查。一个简单的例子:

char array1[] = "12345";
char array2[] = "67890";

gets(array1);

现在,首先您可以输入您想要的字符数,gets不会关心它。其次,你放置它们的数组大小的字节数(在本例中为array1)将覆盖它们在内存中找到的任何内容,因为gets将写入它们。在前面的示例中,这意味着如果您输入"abcdefghijklmnopqrts"可能,不可预测,它也会覆盖array2或其他任何内容。

该功能不安全,因为它假定输入一致。 永远不要使用它!

答案 3 :(得分:16)

您不应该使用gets,因为它无法阻止缓冲区溢出。如果用户输入的数据多于缓冲区中的数据,则很可能最终导致损坏或更糟。

事实上,ISO实际上已经从C标准中删除了删除 gets的步骤(从C11开始,虽然它在C99中被弃用),考虑到它们向后评价的高度兼容性,应该表明该功能有多糟糕。

正确的做法是将fgets函数与stdin文件句柄一起使用,因为您可以限制从用户读取的字符。

但这也存在问题,例如:

  • 用户输入的额外字符将在下次拍摄时使用。
  • 没有快速通知用户输入太多数据。

为此,在职业生涯的某个阶段几乎每个C编码员都会在fgets周围编写一个更有用的包装器。这是我的:

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

有一些测试代码:

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        printf ("No input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long\n");
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

它提供与fgets相同的保护,因为它可以防止缓冲区溢出,但它也会通知调用者发生了什么,并清除多余的字符,以免它们影响您的下一个输入操作。

随意按照您的意愿使用它,我特此在“做你真该想做的”牌照下发布: - )

答案 4 :(得分:11)

fgets

从stdin读取:

char string[512];

fgets(string, sizeof(string), stdin); /* no buffer overflows here, you're safe! */

答案 5 :(得分:6)

如果不破坏API,则无法删除API函数。如果愿意,许多应用程序将不再编译或运行。

这是one reference给出的原因:

  

读一条溢出的线   由s导致的数组   未定义的行为。使用fgets()   推荐。

答案 6 :(得分:4)

我最近在USENET post to comp.lang.c中看到gets()已从标准版中删除。的哇噢

  

你会很高兴知道的   委员会刚刚投了一票(一致同意)   事实证明)从中删除gets()   草案也是如此。

答案 7 :(得分:4)

在C11(ISO / IEC 9899:201x)中,gets()已被删除。 (它在ISO / IEC 9899:1999 / Cor.3:2007(E)中被弃用)

除了fgets()之外,C11还引入了一个新的安全替代方案gets_s()

  

C11 K.3.5.4.1 gets_s函数

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
char *gets_s(char *s, rsize_t n);

但是,在推荐练习部分中,仍然首选fgets()

  

fgets功能允许正确编写的程序也安全地处理输入行   long存储在结果数组中。通常,这需要fgets的来电者付费   注意结果数组中是否存在换行符。考虑   使用fgets(以及基于新行字符的任何所需处理)而不是   gets_s

答案 8 :(得分:3)

我想向任何仍在其库中包含gets的C库维护人员发出诚挚的邀请“以防万一仍有人依赖它”:请用等效的替换你的实现

char *gets(char *str)
{
    strcpy(str, "Never use gets!");
    return str;
}

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

答案 9 :(得分:3)

gets()很危险,因为用户可能通过在提示中输入太多内容来使程序崩溃。它无法检测可用内存的结束,因此如果为此目的分配的内存量太小,可能会导致seg故障和崩溃。有时,用户似乎不太可能在一个人的名字中输入1000个字母,但作为程序员,我们需要使我们的程序具有防弹性。 (如果用户可以通过发送过多数据来使系统程序崩溃,则可能存在安全风险。)

fgets()允许您指定从标准输入缓冲区中取出的字符数,因此它们不会超出变量。

答案 10 :(得分:2)

C获取功能是危险的,并且是一个非常代价高昂的错误。托尼·霍尔(Tony Hoare)在他的演讲中特别提到了这一点&#34; Null References:十亿美元的错误&#34;:

http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

整整一个小时值得关注,但是从30分钟开始他的评论观点就会在39分钟左右受到批评。

希望这能引起你对整个演讲的兴趣,这引起人们对如何在语言中需要更正式的正确性证明以及语言设计者应该如何为其语言中的错误而不是程序员的错误所引起的注意。这似乎是糟糕语言的设计者以“程序员自由”为幌子将程序员责备归咎于整个可疑的原因。