当我尝试用GCC编译使用gets()函数的C代码时,我得到这个警告:
(.text+0x34):警告:' gets'函数是危险的,不应该使用。
我记得这与堆栈保护和安全性有关,但我不确定确切的原因。
我如何删除这个警告,为什么使用gets()会有这样的警告?
如果gets()是如此危险,那么为什么我们不能删除它?
当我尝试用GCC编译使用gets()函数的C代码时,我得到这个警告:
(.text+0x34):警告:' gets'函数是危险的,不应该使用。
我记得这与堆栈保护和安全性有关,但我不确定确切的原因。
我如何删除这个警告,为什么使用gets()会有这样的警告?
如果gets()是如此危险,那么为什么我们不能删除它?
当前回答
我想向仍然在库中包含get的C库维护者发出诚挚的邀请,“以防有人仍然依赖它”:请将您的实现替换为
char *gets(char *str)
{
strcpy(str, "Never use gets!");
return str;
}
这将有助于确保没有人仍然依赖它。谢谢你!
其他回答
在C11(ISO/IEC 9899:201x)中,gets()已被删除。(在ISO/IEC 9899:1999/ co .3:2007(E)中已弃用)
除了fgets(), C11引入了一个新的安全替代gets_s():
C11 K.3.5.4.1 gets_s函数 #定义__STDC_WANT_LIB_EXT1__ # include < stdio . h > (Char *s, rsize_t n);
但是,在推荐实践部分中,fgets()仍然是首选。
fgets函数也允许正确编写的程序安全地处理输入行 在结果数组中存储。一般来说,这要求打电话的人得到报酬 注意结果数组中是否存在换行字符。考虑 使用fget(以及任何基于换行符的必要处理)而不是 gets_s。
为什么gets()是危险的
第一个互联网蠕虫(Morris internet worm)大约在30年前(1988-11-02)逃脱,它使用gets()和缓冲区溢出作为从一个系统传播到另一个系统的方法之一。基本的问题是,函数不知道缓冲区有多大,所以它继续读取,直到找到换行符或遇到EOF,并可能溢出给定的缓冲区边界。
你应该忘记你曾经听说过gets()的存在。
C11标准ISO/IEC 9899:2011取消了gets()作为标准函数,这是a Good Thing™(它在ISO/IEC 9899:1999/Cor中被正式标记为“过时”和“已弃用”。3:2007 - C99的技术勘误3,然后在C11中删除)。遗憾的是,由于向后兼容的原因,它将在库中保留很多年(意思是“几十年”)。如果由我来决定,gets()的实现将变成:
char *gets(char *buffer)
{
assert(buffer != 0);
abort();
return 0;
}
考虑到您的代码迟早会崩溃,最好是尽早解决问题。我会准备添加一个错误消息:
fputs("obsolete and dangerous function gets() called\n", stderr);
如果链接gets(),现代版本的Linux编译系统会生成警告——对于其他一些也存在安全问题的函数(mktemp(),…)也是如此。
gets()的替代方案
fgets ()
正如其他人所说,gets()的规范替代方法是指定stdin作为文件流的fgets()。
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
...process line of data...
}
没有人提到的是gets()不包括换行符,但fgets()包含。因此,你可能需要使用fgets()的包装器来删除换行符:
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或错误,行读取而未截断,部分行读取但数据被截断。
This problem doesn't arise with gets() because it doesn't know where your buffer ends and merrily tramples beyond the end, wreaking havoc on your beautifully tended memory layout, often messing up the return stack (a Stack Overflow) if the buffer is allocated on the stack, or trampling over the control information if the buffer is dynamically allocated, or copying data over other precious global (or module) variables if the buffer is statically allocated. None of these is a good idea — they epitomize the phrase 'undefined behaviour`.
还有TR 24731-1(来自C标准委员会的技术报告),它为各种函数提供了更安全的替代方案,包括gets():
§6.5.4.1 gets_s函数
# # #剧情简介
#定义__STDC_WANT_LIB_EXT1__ # include < stdio . h > (Char *s, rsize_t n);
Runtime-constraints S不能是空指针。n既不等于零也不大于RSIZE_MAX。从stdin读取n-1个字符时,将出现换行符、文件结束符或读取错误。
3如果存在违反运行时约束的情况,s[0]将被设置为空字符,并且从stdin中读取并丢弃字符,直到读取换行字符,或文件结束或发生读取错误。
描述
gets_s函数从stdin指向的流读入s指向的数组的字符数最多比n少1个。在换行字符(被丢弃)之后或文件结束之后不读入额外的字符。丢弃的换行字符不计入读取的字符数。空字符在最后一个字符读入数组后立即写入。
5如果遇到文件结束符并且没有字符被读入数组,或者在操作过程中发生了读错误,那么s[0]将被设置为空字符,s的其他元素取不指定的值。
推荐的做法
fgets函数允许正确编写的程序安全地处理太长而不能存储在结果数组中的输入行。通常,这要求fget的调用者注意结果数组中是否存在换行字符。考虑使用fget(以及任何基于换行符的必要处理)而不是gets_s。
25) gets_s函数与gets函数不同,它使输入行溢出缓冲区来存储它违反了运行时约束。与fgets不同,gets_s在输入行和成功调用gets_s之间维护一对一的关系。使用gets的程序期望这样的关系。
Microsoft Visual Studio编译器实现了近似于TR 24731-1标准的签名,但是Microsoft实现的签名与TR中实现的签名之间存在差异。
C11标准,ISO/IEC 9899-2011,在附录K中包含TR24731作为库的可选部分。不幸的是,它很少在类unix系统上实现。
poxy(琼斯
POSIX 2008 also provides a safe alternative to gets() called getline(). It allocates space for the line dynamically, so you end up needing to free it. It removes the limitation on line length, therefore. It also returns the length of the data that was read, or -1 (and not EOF!), which means that null bytes in the input can be handled reliably. There is also a 'choose your own single-character delimiter' variation called getdelim(); this can be useful if you are dealing with the output from find -print0 where the ends of the file names are marked with an ASCII NUL '\0' character, for example.
简单地说,gets() (can)是危险的,因为用户可能会输入比变量有足够空间存储的内容更大的内容。第一个答案是关于fgets()以及为什么它更安全。
为了安全地使用get,您必须确切地知道将要读取多少字符,以便使缓冲区足够大。只有当您确切地知道要读取哪些数据时,您才会知道这一点。
您希望使用具有签名的fgets,而不是使用gets
char* fgets(char *string, int length, FILE * stream);
(fgets,如果它读取了整行,将在字符串中留下'\n';你得自己处理。)
直到1999年的ISO C标准,gets仍然是语言的官方部分,但在2011年的标准中被正式删除。大多数C实现仍然支持它,但至少gcc会对任何使用它的代码发出警告。
我最近在comp.lang.c的一篇USENET文章中读到,gets()将从标准中删除。哦吼
你会很高兴知道 委员会刚刚投票(一致通过) 将gets()从 还有草案。