【问题标题】:Program do not crash on heap overflow程序不会在堆溢出时崩溃
【发布时间】:2018-02-10 03:47:25
【问题描述】:

我编写了以下程序:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main(int argc, char *argv[]){
  char *input;
  input = (char*)malloc(16);
  printf("input is : %s\n", input);
}

当我运行它时:

./test `python -c 'print "A"*5000'`

它不会崩溃。而是打印数据。

当我在printf 之后使用free(input) 时,它会崩溃。

为什么会这样?

【问题讨论】:

  • 因为它的未定义行为
  • 有没有一种可靠的方法可以在没有 free() 的情况下创建崩溃
  • 您描述的行为没有意义。您确定这是您正在运行的确切代码吗?
  • 使用内存检查器如 valgrind 来查找此类错误。
  • 在得到答案后不要实质性地改变问题。如果您觉得必须编辑问题,请添加带有适当注释的程序的第二个版本,例如 — “糟糕:我错过了 strcpy() 操作。代码看起来像这样。”但最好在提问之前让问题中的代码正确。漏掉关键的台词是粗心大意的表现。

标签: c malloc heap-corruption


【解决方案1】:

为什么会这样?

缓冲区溢出(在这种情况下为heap overflow)不会立即导致崩溃。在分配的内存范围之外写入会导致undefined behavior - 任何事情都可能发生;即使它可以正常工作。

有没有一种可靠的方法可以在没有 free() 的情况下创建崩溃

如果您甚至不初始化指针 input 并取消引用它(在那里读取或写入),很可能您会得到一个 SEGFAULT,但它仍然是“唯一”未定义的行为。 p>

来自C99 draft standard

可能的未定义行为包括忽略情况 完全无法预测的结果,在翻译过程中表现 或以文件化方式执行程序的特征 环境(无论是否发布诊断消息),以 终止翻译或执行(发出 诊断信息)。


但是要小心

溢出可能导致使用受影响内存区域的任何进程造成数据损坏或意外行为。在没有内存保护的操作系统上,这可以是系统上的任何进程。

【讨论】:

  • 很明显 :-)
【解决方案2】:

显示的代码忽略了它的命令行参数:

int main(int argc, char *argv[]){
  char *input;
  input = (char*)malloc(16);
  printf("input is : %s\n", input);
}

Python 脚本提供什么并不重要。但是,您的 printf() 正在打印未初始化的数据;这会导致未定义的行为。如果printf() 没有崩溃并且在printf() 之后有一个free(input); 调用,那么free() 应该不会崩溃。

如果您错过了复制操作并打算显示类似的内容,那么规则会有所不同:

int main(int argc, char *argv[]){
  char *input;
  input = (char*)malloc(16);
  strcpy(input, argv[1]);
  printf("input is : %s\n", input);
  free(input);
  return 0;
}

现在您没有在使用之前检查argv[1] 是否不是空指针——这可能会导致崩溃。如果您在argv[1] 中传递了 5000 个字符,那么您将超出分配的内存范围。某些事情可能会引发崩溃;它没有定义什么会导致崩溃。 strcpy() 可能会失败;如果副本没有,printf() 可能不会失败(但这不能保证); free() 可能会失败,因为您践踏了界限(但即使这样也不能保证)。这就是“未定义行为”的奇迹;任何事情都可能发生,这是有效的行为。

【讨论】:

  • 我现在添加了 strcpy。
  • 在得到诊断问题的答案后,不要实质性地改变问题。这不公平。这为什么我在回答中包含了您的原始代码的一个版本——我强烈怀疑这个问题会在给出诊断后发生变化。我将您的void 返回类型更改为int,因为这是标准所期望的——只有Microsoft C 允许void main()。假设 C99 或更高版本,缺少返回不是问题;如果最后没有返回,则在main() 的末尾有一个“隐式return 0;”(但只有main() 得到这种特殊处理)。
  • @JonathanLeffler 千里眼,很有用的技能。 :-)
  • @JonathanLeffler 有一个'隐式返回 0;'如果最后没有返回,则在 main() 的末尾(但只有 main() 得到这种特殊处理) 只有 main()?我刚刚在 gcc 上尝试了一个带有 -std=c99 的程序,该程序没有给出返回类型并且它无论如何都返回零。我收到警告但没有错误。我做错了吗?
  • @J...S:随机机会;允许未定义的行为产生您期望的答案,即使您的期望通常是错误的。是的,main() 有一个特定规则:§5.1.2.2.3 程序终止 如果main 函数的返回类型是与int 兼容的类型,则从对main 函数的初始调用返回等效于以main 函数返回的值作为其参数调用exit 函数;11) 到达终止main 函数的} 返回一个0....的值。
猜你喜欢
  • 1970-01-01
  • 2012-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多