6

假设我们有一个用 C++ 编写的 SDK,它接受一些二进制数据(如图片)并执行一些操作。难道不能让这个SDK“防撞”吗?崩溃主要是指由于用户传递的无效输入(例如异常短的垃圾数据),操作系统在内存访问冲突时强制终止。

我没有使用 C++ 的经验,但是当我用谷歌搜索时,我发现了几种听起来像解决方案的方法(使用向量而不是数组,配置编译器以便执行自动边界检查等)。

当我向开发人员介绍这个时,他说这仍然是不可能的。不是我不相信他,但如果是这样,像 Java 这样的语言是如何处理这个的?我认为 JVM 每次都会执行边界检查。如果是这样,为什么不能手动在 C++ 中做同样的事情?

更新
“防崩溃”并不是说应用程序不会终止。我的意思是它不应该在没有发生情况的信息的情况下突然终止(我的意思是它会转储核心等,但是不可能显示“参数 x 无效”等消息吗?)

4

8 回答 8

6

您可以在 C++ 中检查数组的边界,std::vector::at自动执行此操作。

这不会使您的应用程序防崩溃,您仍然可以故意向自己的脚开枪,但 C++ 中的任何内容都不会强迫您扣动扳机。

于 2010-12-23T06:02:18.770 回答
4

不。即使假设您的代码没有错误。首先,我查看了许多自动提交的崩溃报告,我可以向您保证,硬件质量远低于大多数开发人员的预期。位翻转在商品机器上太常见了,会导致随机 AV。而且,即使您准备好处理访问违规,也存在某些例外情况,操作系统别无选择,只能终止进程,例如未能提交堆栈保护页面

于 2010-12-23T06:02:58.777 回答
4

崩溃主要是指由于用户传递的无效输入(例如异常短的垃圾数据),操作系统在内存访问冲突时强制终止。

这是通常发生的事情。如果您访问一些无效内存,通常操作系统会中止您的程序。

然而,什么是无效内存的问题......您可以自由地用垃圾填充堆和堆栈中的所有内存,这从操作系统的角度来看是有效的,从您的角度来看它是无效的,因为您创建了垃圾。

基本上 - 您需要仔细检查输入数据并对此进行中继。没有操作系统会为您执行此操作。

如果你仔细检查你的输入数据,你可能会管理好数据。

于 2010-12-23T06:04:22.183 回答
3

我主要是指由于用户传递的无效输入,操作系统在内存访问冲突时强制终止

不确定“用户”是谁。

您可以编写不会因最终用户输入无效而崩溃的程序。在某些系统上,您可能会由于使用过多内存(或因为某些其他程序使用过多内存)而被强制终止。正如 Remus 所说,没有一种语言可以完全保护您免受硬件故障的影响。但这些事情取决于用户提供的数据字节以外的因素。

在 C++ 中你不能轻易做到的是证明你的程序不会因为无效输入而崩溃,或者以更糟糕的方式出错,从而造成严重的安全漏洞。所以有时[*] 你认为你的代码对任何输入都是安全的,但事实证明并非如此。您的开发人员可能是这个意思。

如果您的代码是一个函数,例如一个指向图像数据的指针,那么没有什么可以阻止调用者向您传递一些无效的指针值:

char *image_data = malloc(1);
free(image_data);
image_processing_function(image_data);

因此,该功能本身不能“防崩溃”,它要求程序的其余部分不做任何使其崩溃的事情。您的开发人员也可能是这个意思,所以也许您应该请他澄清一下。

Java 通过使创建无效引用变得不可能来处理这个特定问题 - 您无法在 Java 中手动释放内存,因此特别是在这样做之后您不能保留对它的引用。它以其他方式处理许多其他特定问题,因此在 C++ 中是“未定义行为”并且很可能导致崩溃的情况在 Java 中会做一些不同的事情(可能会引发异常)。

[*] 让我们面对现实:在实践中,在大型软件项目中,“经常”。

于 2010-12-23T11:04:10.230 回答
1

我认为这是 C++ 代码不是托管代码的情况。

Java、C# 代码是受管理的,也就是说它们由能够执行边界检查和检测崩溃条件的解释器有效地执行。

对于 C++ 的情况,您需要自己执行绑定和其他检查。但是,您可以使用异常处理,这将防止在您无法控制的事件期间发生崩溃。

底线是,C++ 代码本身不是防崩溃的,但良好的设计和开发可以使它们如此。

于 2010-12-23T06:07:05.353 回答
1

通常,您无法使 C++ API防崩溃,但可以使用一些技术使其更加健壮。对于您的特定示例,我不以为然(而且绝不是详尽无遗):

  • 在可能的情况下检查输入数据
  • 数据处理代码中的缓冲区限制检查
  • 边缘和角落案例测试
  • 模糊测试
  • 将问题输入放入单元测试中以避免回归
于 2010-12-23T07:11:13.780 回答
0

如果“崩溃证明”仅意味着您要确保有足够的信息来调查崩溃发生后的解决方案可以很简单。大多数在崩溃期间丢失调试信息的情况是由于在线程之一中运行的代码的非法内存操作造成的损坏和/或堆栈数据丢失。如果您调用不信任的库或 SDK 的地方很少,您可以简单地保存堆栈跟踪,然后在全局变量指向的某个内存位置调用该库,该全局变量将包含在部分或全部内存转储中当您的应用程序崩溃时由系统生成。在 Windows 上,由 CrtDbg API 提供的此类功能。在 Linux 上,您可以使用回溯 API - 只需在 show_stackframe() 上搜索文档。如果您丢失了堆栈信息,则可以指示调试器在加载转储文件后使用内存中的该位置作为堆栈顶部。好吧,这毕竟不是很简单,但是如果您被内存转储困扰而对发生的事情一无所知,那可能会有所帮助。嵌入式应用程序中经常使用的另一个技巧是用于详细记录的循环内存缓冲区。记录到缓冲区非常便宜,因为它永远不会被保存,但是您可以通过查看崩溃后内存转储中缓冲区的内容来了解​​崩溃前几毫秒发生的情况。

于 2011-01-09T07:19:15.243 回答
-1

实际上,使用边界检查会使您的应用程序更容易崩溃

这是一个很好的设计,因为这意味着如果您的程序正在运行,那么它更有可能 / 正确 / 运行,而不是运行不正确。

也就是说,严格来说,在解决停止问题之前,不能对给定的应用程序进行“防崩溃”。祝你好运!

于 2010-12-23T06:04:37.037 回答