假设我们在C ++中有一个SDK,它接受一些二进制数据(如图片)并做一些事情。是不是可以使这个SDK“防崩溃”?由于用户传递的无效输入(如异常短的垃圾数据),我主要意味着操作系统在内存访问冲突时强制终止。
我没有使用C ++的经验,但是当我用Google搜索时,我发现了几个听起来像解决方案的方法(使用向量而不是数组,配置编译器以便执行自动边界检查等)。
当我向开发人员提出这个问题时,他说仍然不可能。不是我不相信他,但如果是这样,那么像Java这样的语言处理这个问题呢?我以为JVM每次都会执行边界检查。如果是这样,为什么不能手动在C ++中做同样的事情呢?
更新
通过“防止崩溃”我并不是说应用程序不会终止。我的意思是它不应该突然终止没有发生的事情的信息(我的意思是它会转储核心等,但是不可能显示像“参数x无效”等消息吗?)
答案 0 :(得分:6)
您可以在C ++中检查数组的边界,std::vector::at
自动执行此操作。
这不会让你的应用程序崩溃证明,你仍然可以故意用脚射击自己,但是C ++中的任何内容都不会让你扣动扳机。
答案 1 :(得分:4)
没有。即使假设您的代码没有错误。首先,我看了很多自动提交的崩溃报告,我可以向您保证,硬件的质量远远低于大多数开发人员所期望的。位翻转在商用机器上太常见并导致随机AV。而且,即使您准备处理访问冲突,也有一些例外情况,操作系统别无选择,只能终止进程,例如无法提交stack guard page。
答案 2 :(得分:4)
由于用户传递的无效输入(如异常短的垃圾数据),我主要意味着OS在内存访问冲突时强制终止。
通常会发生这种情况。如果您访问某些无效内存,则OS会中止您的程序。
然而问题是什么是无效内存...你可以自由地填充垃圾堆和堆栈中的所有内存,这从OS的角度来看是有效的,从你创建垃圾的角度看它是无效的
基本上 - 您需要仔细检查输入数据并继续此操作。没有操作系统会为你做这件事。
如果仔细检查输入数据,则可能会正确管理数据。
答案 3 :(得分:3)
我的意思是强行终止 由OS访问内存 违规,由于传递的无效输入 由用户
不确定“用户”是谁。
您可以编写因最终用户输入无效而不会崩溃的程序。在某些系统上,由于使用了太多内存(或者因为某些其他程序使用了太多内存),您可能会被强制终止。正如Remus所说,没有任何语言可以完全保护您免受硬件故障的影响。但这些事情取决于用户提供的数据字节以外的因素。
你在C ++中不能轻易做到的是证明你的程序不会因输入无效而崩溃,或者更糟糕的方式出错,造成严重的安全漏洞。所以有时[*]你认为你的代码对任何输入都是安全的,但事实证明并非如此。你的开发者可能就是这个意思。
如果您的代码是一个函数,例如指向图像数据的指针,那么没有什么可以阻止调用者传递一些无效的指针值:
char *image_data = malloc(1);
free(image_data);
image_processing_function(image_data);
因此该函数本身不能“防崩溃”,它要求程序的其余部分不做任何事情以使其崩溃。你的开发者也可能是这个意思,所以也许你应该让他澄清一下。
Java通过使得无法创建无效引用来处理此特定问题 - 您无法在Java中手动释放内存,因此特别是在执行此操作后您无法保留对它的引用。它以其他方式处理许多其他特定问题,因此C ++中“未定义行为”并且可能导致崩溃的情况将在Java中执行不同的操作(可能会抛出异常)。
[*]让我们面对现实:在实践中,在大型软件项目中,“经常”。
答案 4 :(得分:1)
我认为这是C ++代码不是托管代码的情况。
管理Java,C#代码,即它们由解释器有效执行,该解释器能够执行绑定检查并检测崩溃情况。
对于C ++的情况,您需要自己执行绑定和其他检查。但是,您可以使用异常处理,这可以防止在您无法控制的事件期间崩溃。
最重要的是,C ++代码本身并不是防撞证明,但良好的设计和开发可以使它们成为现实。
答案 5 :(得分:1)
通常,您无法使C ++ API 防崩溃,但有一些技术可用于使其更强大。对于您的特定示例,我的头顶(并非详尽无遗):
答案 6 :(得分:0)
如果“崩溃证明”仅表示您希望确保您有足够的信息来调查崩溃后解决方案可以很简单。大多数情况下,调试信息在崩溃期间丢失是由于在一个线程中运行的代码导致非法内存操作导致的损坏和/或堆栈数据丢失。如果你几乎没有地方调用你不信任的库或SDK,你可以直接保存堆栈跟踪,然后在全局变量指向的某个内存位置调用该库,该变量将包含在部分或完全内存转储中应用程序崩溃时由系统生成的。在Windows上由CrtDbg API提供的功能。在Linux上,您可以使用backtrace API - 只需在show_stackframe()上搜索doc。如果丢失了堆栈信息,则可以指示调试器在加载转储文件后将内存中的该位置用作堆栈顶部。好吧,它毕竟不是很简单,但是如果你被内存转储困扰而不知道发生了什么,它可能会有所帮助。 嵌入式应用中经常使用的另一个技巧是循环内存缓冲区,用于详细记录。记录到缓冲区是非常便宜的,因为它永远不会保存,但是你可以通过在崩溃后查看内存转储中缓冲区的内容来了解崩溃前几毫秒发生的事情。
答案 7 :(得分:-1)
实际上,使用边界检查会使您的应用程序更容易崩溃!
这是一个很好的设计,因为这意味着如果你的程序正在运行,那就更有可能正常工作/,而不是错误地工作。
也就是说,严格来说,给定的应用程序不能成为“防撞”,直到停止问题得到解决。祝你好运!