当我试图C编译代码的使用 gets() 功能与海湾合作委员会,我得到这个警告:

(.文+0x34):警告:该`到'功能是危险的,不应使用。

我记得这个有什么用栈保护和安全,但我不知道到底为什么。

我怎么删除这一预警和为什么会有这样的警告有关使用 gets()?

如果 gets() 是很危险的,那么为什么我们不能移除它吗?

有帮助吗?

解决方案

为了安全地使用gets,你必须知道你会究竟有多少字符来读,这样就可以使你的缓冲区足够大。你只知道,如果你确切地知道你会看什么数据。

除了使用gets的,要使用 fgets ,该有签名

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

fgets,如果读取整条生产线,将离开'\n'字符串中。你必须处理这个)

它保持的语言到1999 ISO C标准的正式的一部分,但 它被正式通过了2011年标准中删除。大多数C实现仍然支持它,但至少GCC发出用于任何代码的警告使用它。

其他提示

为什么 gets() 危险

第一次互联网虫(的 莫里斯互联网虫)逃脱大约30年前(1988-11-02),并使用它 gets() 和一个缓冲区溢出作为其方法的传播系统,以系统。基本的问题是,能不知道有多大的缓冲区,所以它继续读书直到找到一个新行或遇到EOF,并可能会溢出边界的缓冲。

你应该忘记你曾经听说 gets() 存在。

C11标准ISO/IEC9899:2011年淘汰 gets() 作为一个标准功能,这是一件好事™(这是正式的标记为'过时"和"废弃的'ISO/IEC9899:1999/Cor。3:2007年技术更正》3C99,然后在C11)。可悲的是,它将保留在图书馆为多年(意为'十年')为理由向后兼容性。如果是我的话,执行 gets() 会成为:

char *gets(char *buffer)
{
    assert(buffer != 0);
    abort();
    return 0;
}

鉴于你的码将会崩溃,无论如何,迟早,最好是头上的麻烦,关闭宜早不宜迟。我会准备增加一个错误信息:

fputs("obsolete and dangerous function gets() called\n", stderr);

现代版本的Linux汇编系统产生的警告如果你链接 gets() —并且也对其他一些功能还有安全问题(mktemp(), …).

替代品 gets()

fgets()

正如其他人所说的,在规范替代 gets()fgets() 指定 stdin 作为文件流。

char buffer[BUFSIZ];

while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
    ...process line of data...
}

什么没有其他人还提到的是, gets() 不包括新行但是 fgets() 不。所以,你可能需要使用的包装 fgets() 删除newline:

char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
    if (fgets(buffer, buflen, fp) != 0)
    {
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len-1] == '\n')
            buffer[len-1] = '\0';
        return buffer;
    }
    return 0;
}

或者,更好:

char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
    if (fgets(buffer, buflen, fp) != 0)
    {
        buffer[strcspn(buffer, "\n")] = '\0';
        return buffer;
    }
    return 0;
}

此外,作为 caf 指出在意见和 paxdiablo 显示在他的回答, fgets() 你可能会有数据超过上一条线。我的包装码叶,数据读取下来的时间;你可以容易地修改它要狼吞虎咽行的其余部分的数据,如果你喜欢:

        if (len > 0 && buffer[len-1] == '\n')
            buffer[len-1] = '\0';
        else
        {
             int ch;
             while ((ch = getc(fp)) != EOF && ch != '\n')
                 ;
        }

剩余的问题是如何报告的三个不同的结果国—EOF或错误,行阅读,并不截断,以及部分行阅读,但数据是截断。

这个问题不会出现 gets() 因为它不知道你的缓冲区结束,并欢快地践踏结束后,造成严重破坏关于你的美丽往往存的布局,常常搞乱了返回堆(a 堆溢出)如果缓冲区上分配堆,或践踏在控制信息,如果在缓冲区是动态分配,或者复制的数据比其他的宝贵的全球性的(或模块)的变量,如果在缓冲区是静态的分配。没有这些是一个很好的想法—他们体现了这句话未定义的行为`.


还有 TR24731-1 (技术报告,C标准委员会)提供更安全的替代品的各种职能,包括 gets():

§6.5.4.1的 gets_s 功能

概要

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

运行时间限制

s 不是一个空的指针。 n 不等于零,也不能大于 RSIZE_MAX.一个新的线性格,最终文件,或是读错误应发生在阅读 n-1 字符 stdin.25)

3如果有一个运行时间约束的违反, s[0] 被设置为零的字符,并且符 读和丢弃 stdin 直到一个新的字符读出,或者结束的文件或 读错误发生。

描述

4 gets_s 功能读取至多一个低于该数字指定的通过 n 从流指出通过 stdin, ,进入阵指出,由 s.没有额外的 字符读之后,一个新的线性格(它被丢弃的)或之后结束的文件。丢弃的新的线性格不计入数字阅读。一个 空字写入之后立即最后一个字符读入阵列。

5如果最终的文件所遇到的和不符已经读到的阵,或者如果一读 错误发生在操作过程中,然后 s[0] 被设置为零的字符,并且其他的 元素 s 采取未指明的价值观。

建议的做法

6 fgets 功能允许适当地编写的程序来安全的进程输入线太 长期存在的结果阵列。一般来说,这要求呼叫者的 fgets 支付 注意到存在或不存在的新行符的结果阵列。考虑 使用 fgets (及任何需要处理基于新的线人物),而不是的 gets_s.

25)gets_s 功能,与不同的 gets, 使它运行时违反约束的一线的输入到 溢出的缓冲储存。不像 fgets, gets_s 保持一对一的关系 输入线和成功的话 gets_s.程序使用 gets 期望这种关系。

Visual Studio编译器实施一个近似的TR24731-1标准,但也有差异之间的签名实施的微软和那些在TR。

C11标准、ISO/IEC9899-2011,包括TR24731附件K作为一个可选择部分的图书馆。不幸的是,很少实施在类Unix系统。


getline() —POSIX

POSIX2008年还提供了一个安全的替代 gets()getline().它分配的空间进行动态的,所以你最终需要为免费。它消除的限制线的长度,因此。它还回报数据的长度,是阅读,或 -1 (而不 EOF!), 这意味着null bytes在输入时可以处理可靠。还有一个'自己选择的单字符'的变化称为 getdelim();这可能是有用的,如果你正在处理的输出 find -print0 在那里结束的文件名称标有ASCII NUL '\0' 字符,例如。

由于gets没有做任何检查,而从获得的标准输入的字节,并把他们的地方。一个简单的例子:

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

gets(array1);

现在,首先你被允许输入你想有多少个字符,gets不会在意它。其次在其中你把他们(在这种情况下array1)数组的大小,字节将覆盖任何他们在内存中找到,因为gets将它们写。在前面的示例中,这意味着,如果输入"abcdefghijklmnopqrts"也许,不可预测的,它也将覆盖array2或任何

功能是不安全的,因为它假设是一致的输入。 从来没有使用它!

你不应该使用 gets 由于它有没有办法阻止一个缓冲区的溢出。如果用户类型中的数据多于可能适合你的缓冲器,你将最有可能最终损坏或变得更糟。

事实上,ISO实际上已经采取的步骤 除去 gets 从C的标准(如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 在于,它可以防止缓冲区溢出,但是它还通知叫什么发生了,并清除了多余的字符这样,他们这样做不会影响你的下一个输入操作。

随意使用它为你愿意,我在此释放它下"做你真他妈的希望"许可证:-)

与fgets

要来自标准输入读取:

char string[512];

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

可以不不破坏API除去API函数。如果你想,许多应用程序将不再编译或运行在所有。

这是一个参考给出的理由:

  

读取溢出的线   指向的数组由s结果在   未定义的行为。 )使用与fgets的(   推荐使用。

在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功能允许适当编写的程序以安全地处理输入线太   长结果阵列中存储。通常,这需要fgets工资的呼叫者   注意结果阵列中的换行字符的存在或不存在。考虑   使用fgets(基于新行字符的任何所需的处理一起),而不是   gets_s

我想的诚挚邀请,扩展到任何C库的维护者那里谁仍然包括在自己的音乐库“以防万一有人仍取决于它” gets:请相当于

更换您的实现
char *gets(char *str)
{
    strcpy(str, "Never use gets!");
    return str;
}

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

gets()是危险的,因为它能够使用户通过键入太多到提示程序崩溃。它不能检测可用内存的结束,所以如果你分配的内存太小的目的的量,它可以导致赛格故障和崩溃。有时它似乎不太可能,用户将键入的字母1000到提示意味着一个人的名字,但是作为程序员,我们需要使我们的节目防弹。 (它也可以是一个安全风险,如果用户可以通过发送数据太多崩溃的系统程序)。

fgets()允许指定多少个字符被取出标准输入缓冲器的,所以他们不溢出该变量。

的C获取功能是危险的,一直是非常昂贵的错误。东尼·霍尔选拔出来的特别提及在他的谈话“空引用:数十亿美元的错误”:

HTTP://www.infoq。 COM /演示/空引用-的亿美元-错误-托尼霍尔

在整整一个小时是值得关注,但对他的评论30分钟查看与特定得到周围39分钟批评。

希望这磨砺你的整个谈话的胃口,其中提请注意我们如何需要语言更正式的正确性证明以及如何语言设计者应该在他们的语言被指责为错误,而不是程序员。这似乎是整个可疑的原因不好语言的设计者推怪程序员“程序员自由”的幌子。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top