c-更有效地避免字符缓冲区溢出


我写了一个简单的输入/输出程序每当我运行它并输入并超过我得到的字符限制时*** stack smashing detected ***: terminated Aborted (core dumped)

我搜索了一下,发现这是一个gcc的安全问题,我听说它可能会导致seg故障,所以我尝试用-fno-stack-protector关闭它,如果我超过字符限制,它就会正常运行

但是,如果我想写程序,如果输入长度未知,有更安全的方法吗?将char中的值增加到一个大得离谱的值更有效吗?

代码:

#include <stdio.h>
int main()
{
char in[1];
printf("in: ");
scanf("%s", &in);
printf("nout: %sn", in);
}

p.s-im是C的新手,>2天大,所以一个简单的解释将不胜感激

char in[1];只能容纳空字符串(单个空终止字节(,这是不可能与scanf一起安全使用的。

还要注意,在字符串文字中显式声明null终止字节是多余的,因为它们是隐式null终止的。

但是如果我想在输入长度未知的情况下编写程序,有更安全的方法吗?将char中的值增加到一个大得离谱的值更有效吗?

这里的反问题是:

  • 你认为什么是低效的
  • 你把什么定义为大得离谱

在我看来,你有两个选择:

  1. 使用动态分配的内存读取任意大小的字符串
  2. 设置一个实际的输入长度上限

#1的一个例子可以在库函数(如POSIXgetlinegetdelim(中看到。它的重新实现可以像malloc(realloc(、getchar和一个循环一样简单。

#2的使用在很大程度上取决于程序的上下文,以及它应该做什么。也许你只需阅读一行,一个小的缓冲区就足够了。也许您期望得到更大的数据块,并且需要更多的内存。只有你自己才能决定。

在任何情况下,您都可以通过在未定义行为发生之前防止溢出来避免未定义行为。如果已经发生了,那就已经太晚了。

使用%s:时使用字段宽度说明符

char buf[512];
if (1 != scanf("%511s", buf))
/* read error */;

或者使用类似fgets的sane函数,它允许您传递缓冲区大小作为参数。

检测到堆栈粉碎
我搜索了一下,发现这是一个gcc安全

这确实是gcc通过插入所谓的";堆叠金丝雀";以发现堆栈损坏/溢出。检测到更多错误是一件好事。

我听说它可能会导致seg故障

不,应用程序中的错误会导致seg错误。如果编译器提供了在操作系统之前检测它们的方法,那是一件好事。程序中存在休眠但严重的错误是一件坏事。然而,操作系统也可能检测到错误并说";seg故障";。

所以我尝试用-fno堆栈保护器关闭它,如果我超过字符限制,它就会正常运行

基本上你知道自己是一个没有经验的司机,害怕撞到其他车。为了解决这个问题,你可以闭着眼睛开车,这样你就看不到那些你可能撞到的车了。这并不意味着它们消失了。

char in[1];只能容纳1个字节的数据,如果您读取超出该数组的边界,则会调用未定义的行为,这将表现为堆栈粉碎或seg错误。因为你在试图写那些不属于你的记忆。这就是bug,这就是问题所在。正确的解决方案是分配足够的内存。

(您还有一个错误scanf("%s", &in);->scanf("%s", in);。不需要&,因为in是一个数组,当您将其传递给函数时,它会自动"衰减"为指向其第一个元素的指针。(

一种合理的方法是分配128个字节左右,然后限制输入,使其不能超过128个字节。读取具有受限输入长度的字符串的合适函数是fgets。因此,您可以切换到fgets,也可以接受您的初学者试用程序不需要达到生产质量,现在只使用scanf。(您可以安全地使用scanf,如另一个答案所示,但IMO只是比使用fgets更麻烦。(

此外,我强烈建议C初学者不要担心他们是否分配了10个字节或100个字节。用电脑学习编程,然后就没关系了。优化内存消耗是一个高级主题,您将在稍后学习。

最新更新