vin*_*rak 210 c gets fgets buffer-overflow
当我尝试编译使用gets()GCC函数的C代码时,
我明白了
警告:
(.text + 0x34):警告:`gets'函数很危险,不应该使用.
我记得这与堆栈保护和安全性有关,但我不确定为什么?
有人可以帮我删除这个警告并解释为什么会有这样的使用警告gets()?
如果gets()是如此危险,为什么我们不能删除它?
Tho*_*ens 161
为了gets安全使用,您必须确切地知道要读取的字符数,以便您可以使缓冲区足够大.只有在您确切知道要阅读的数据时,您才会知道.
gets您想要使用fgets具有签名的,而不是使用
char* fgets(char *string, int length, FILE * stream);
Run Code Online (Sandbox Code Playgroud)
(fgets如果它读取整行,则会留'\n'在字符串中;您将不得不处理它.)
它仍然是该语言的官方部分,直到1999年的ISO C标准,但它已被2011标准正式删除.大多数C实现仍然支持它,但至少gcc会对使用它的任何代码发出警告.
Jon*_*ler 145
gets()危险第一个互联网蠕虫(Morris Internet蠕虫)在大约30年前(1988-11-02)逃脱,它使用gets()缓冲区溢出作为其从系统传播到系统的方法之一.基本问题是函数不知道缓冲区有多大,所以它继续读取直到找到换行符或遇到EOF,并且可能溢出它给出的缓冲区的边界.
你应该忘记你曾经听说过gets()存在过的.
C11标准ISO/IEC 9899:2011 gets()作为标准功能被淘汰,即A Good Thing™(在ISO/IEC 9899:1999/Cor.3:2007中正式标记为'过时'和'弃用' - 技术勘误3表示C99,然后在C11中删除).可悲的是,由于向后兼容的原因,它将在图书馆中存在多年(意为"数十年").如果由我决定,实施gets()将成为:
char *gets(char *buffer)
{
assert(buffer != 0);
abort();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
鉴于你的代码无论如何都迟早会崩溃,最好不要迟早解决问题.我准备添加一条错误消息:
fputs("obsolete and dangerous function gets() called\n", stderr);
Run Code Online (Sandbox Code Playgroud)
现代版本的Linux编译系统会在您链接时生成警告gets()- 以及其他一些也存在安全问题的函数(mktemp(),...).
gets()正如其他人所说,规范替代方案gets()是fgets()指定stdin为文件流.
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
...process line of data...
}
Run Code Online (Sandbox Code Playgroud)
还没有人提到的是,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;
}
Run Code Online (Sandbox Code Playgroud)
或更好:
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;
}
Run Code Online (Sandbox Code Playgroud)
此外,如咖啡厅指出了评论和paxdiablo显示了他的答案,与fgets()你可能有一行遗留数据.我的包装器代码留下了下次要读取的数据; 如果您愿意,可以随时修改它以吞噬剩余的数据行:
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
else
{
int ch;
while ((ch = getc(fp)) != EOF && ch != '\n')
;
}
Run Code Online (Sandbox Code Playgroud)
剩下的问题是如何报告三种不同的结果状态 - EOF或错误,行读取和未截断,以及部分行读取但数据被截断.
这个问题不会出现,gets()因为它不知道你的缓冲区在哪里结束并快速地践踏到最后,对你漂亮的内存布局造成严重破坏,如果缓冲区被分配,通常会搞乱返回堆栈(Stack Overflow)如果缓冲区是动态分配的,则堆栈或践踏控制信息,或者如果缓冲区是静态分配的,则通过其他宝贵的全局(或模块)变量复制数据.这些都不是一个好主意 - 它们集中体现了"未定义的行为"这一短语.
还有TR 24731-1(C标准委员会的技术报告),它提供了各种功能的更安全的替代方案,包括gets():
§6.5.4.1
gets_s功能概要
Run Code Online (Sandbox Code Playgroud)#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期望这种关系的程序.
Microsoft Visual Studio编译器实现了TR 24731-1标准的近似,但Microsoft实现的签名与TR中的签名之间存在差异.
C11标准ISO/IEC 9899-2011包括附件K中的TR24731作为库的可选部分.不幸的是,它很少在类Unix系统上实现.
getline() - POSIXPOSIX 2008还提供了一个安全的替代gets()叫getline().它动态地为该行分配空间,因此您最终需要释放它.因此,它消除了线路长度的限制.它还返回读取的数据的长度,或-1(而不是EOF!),这意味着可以可靠地处理输入中的空字节.还有一个名为"选择你自己的单字符分隔符"的变体getdelim(); 例如,如果要处理find -print0文件名末尾用ASCII NUL '\0'字符标记的输出,这可能很有用.
Jac*_*ack 21
因为gets从stdin获取字节并将它们放在某处时不进行任何类型的检查.一个简单的例子:
char array1[] = "12345";
char array2[] = "67890";
gets(array1);
Run Code Online (Sandbox Code Playgroud)
现在,首先你可以输入你想要多少个字符,gets不关心它.其次,超过你放置它们的数组大小的字节(在这种情况下array1)将覆盖它们在内存中找到的任何内容,因为gets它们会写入它们.在前面的示例中,这意味着如果您输入"abcdefghijklmnopqrts"可能,不可预测,它也会覆盖array2或者其他任何内容.
该函数不安全,因为它假定输入一致.永远不要用它!
pax*_*blo 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;
}
Run Code Online (Sandbox Code Playgroud)
一些测试代码:
// 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;
}
Run Code Online (Sandbox Code Playgroud)
它提供了相同的保护fgets,因为它可以防止缓冲区溢出,但它也会通知调用者发生了什么,并清除多余的字符,这样它们就不会影响您的下一个输入操作.
随意按照您的意愿使用它,我特此在"做你真该想要的"许可下发布它:-)
Thi*_*ira 12
从stdin读取:
char string[512];
fgets(string, sizeof(string), stdin); /* no buffer overflows here, you're safe! */
Run Code Online (Sandbox Code Playgroud)
我最近在USENET 的comp.lang.c一篇文章中读到,它gets()正在从标准中删除。呜呼
你会很高兴知道委员会刚刚投票(结果一致)也从草案中删除了 gets() 。
在 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功能Run Code Online (Sandbox Code Playgroud)#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> char *gets_s(char *s, rsize_t n);
但是,在推荐实践部分,fgets()仍然是首选。
该
fgets函数允许正确编写的程序安全地处理太长而无法存储在结果数组中的输入行。通常,这要求调用者fgets注意结果数组中是否存在换行符。考虑使用fgets(以及任何需要的基于换行符的处理)而不是gets_s.
小智 5
gets()很危险,因为用户可能会在提示中输入太多内容而导致程序崩溃。它无法检测到可用内存的末尾,因此如果为此目的分配的内存量太小,可能会导致段错误和崩溃。有时,用户将 1000 个字母键入一个人名的提示中似乎不太可能,但作为程序员,我们需要使我们的程序防弹。(如果用户发送过多数据可能导致系统程序崩溃,也可能存在安全风险)。
fgets() 允许您指定从标准输入缓冲区中取出多少个字符,这样它们就不会溢出变量。
| 归档时间: |
|
| 查看次数: |
137649 次 |
| 最近记录: |