难道不能制作一个 C++ 吗?应用程序“防撞”?
假设我们有一个 C++ 语言的 SDK,它接受一些二进制数据(如图片)并执行某些操作。难道这个SDK就不能做到“防崩溃”吗?我所说的崩溃主要是指由于用户传递的无效输入(例如异常短的垃圾数据)而导致内存访问冲突时操作系统强制终止。
我没有 C++ 经验,但是当我用 google 搜索时,我发现了几种听起来像是解决方案的方法(使用向量而不是数组,配置编译器以便执行自动边界检查等)。
当我向开发人员提出这个问题时,他说这仍然不可能。并不是我不相信他,但如果是这样,像 Java 这样的语言是如何处理这个问题的呢?我认为 JVM 每次都会执行边界检查。如果是这样,为什么不能在 C++ 中手动做同样的事情呢?
更新
我所说的“防崩溃”并不是说应用程序不会终止。我的意思是它不应该在没有发生什么情况的信息的情况下突然终止(我的意思是它会转储核心等,但是是否不可能显示“参数 x 无效”等消息?)
Let's say we have an SDK in C++ that accepts some binary data (like a picture) and does something. Is it not possible to make this SDK "crash-proof"? By crash I primarily mean forceful termination by the OS upon memory access violation, due to invalid input passed by the user (like an abnormally short junk data).
I have no experience with C++, but when I googled, I found several means that sounded like a solution (use a vector instead of an array, configure the compiler so that automatic bounds check is performed, etc.).
When I presented this to the developer, he said it is still not possible.. Not that I don't believe him, but if so, how is language like Java handling this? I thought the JVM performs everytime a bounds check. If so, why can't one do the same thing in C++ manually?
UPDATE
By "Crash proof" I don't mean that the application does not terminate. I mean it should not abruptly terminate without information of what happened (I mean it will dump core etc., but is it not possible to display a message like "Argument x was not valid" etc.?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您可以检查 C++ 中数组的边界,
std::vector ::at
自动执行此操作。这并不能让你的应用程序不会崩溃,你仍然可以故意搬起石头砸自己的脚,但 C++ 中没有任何东西强迫你扣动扳机。
You can check the bounds of an array in C++,
std::vector::at
does this automatically.This doesn't make your app crash proof, you are still allowed to deliberately shoot yourself in the foot but nothing in C++ forces you to pull the trigger.
不。即使假设您的代码没有错误。首先,我查看了许多自动提交的崩溃报告,我可以向您保证,现有硬件的质量远远低于大多数开发人员的期望。位翻转在商用机器上非常常见,并会导致随机反病毒。而且,即使您准备好处理访问冲突,在某些例外情况下,操作系统别无选择,只能终止进程,例如未能提交 堆栈保护页面。
No. Even assuming your code is bug free. For one, I have looked at many a crash reports automatically submitted and I can assure you that the quality of the hardware out there is much bellow what most developers expect. Bit flips are all too common on commodity machines and cause random AVs. And, even if you are prepared to handle access violations, there are certain exceptions that the OS has no choice but to terminate the process, for example failure to commit a stack guard page.
这是通常会发生的情况。如果您访问一些无效内存,操作系统通常会中止您的程序。
然而,问题是什么是无效内存......您可以自由地用垃圾填充堆和堆栈中的所有内存,这从操作系统的角度来看是有效的,但从您的角度来看,当您创建垃圾时,它是无效的。
基本上 - 您需要仔细检查输入数据并以此为基础。没有操作系统会为你做这件事。
如果您仔细检查输入数据,您可能会很好地管理数据。
This is what usually happens. If you access some invalid memory usually OS aborts your program.
However the question what is invalid memory... You may freely fill with garbage all the memory in heap and stack and this is valid from OS point of view, it would not be valid from your point of view as you created garbage.
Basically - you need to check the input data carefully and relay on this. No OS would do this for you.
If you check your input data carefully you would likely to manage the data ok.
不确定“用户”是谁。
您可以编写不会因最终用户输入无效而崩溃的程序。在某些系统上,您可能会因使用过多内存(或因为某些其他程序使用过多内存)而被强制终止。正如 Remus 所说,没有一种语言可以完全保护您免受硬件故障的影响。但这些事情取决于用户提供的数据字节以外的因素。
在 C++ 中,您无法轻松做到的是证明您的程序不会因无效输入而崩溃,或者以更糟糕的方式出错,从而造成严重的安全缺陷。因此有时[*]您认为您的代码对于任何输入都是安全的,但事实证明并非如此。您的开发人员可能是这个意思。
如果您的代码是一个采用指向图像数据的指针的函数,那么没有什么可以阻止调用者向您传递一些无效的指针值:
因此该函数本身不能“防崩溃”,它要求程序的其余部分不会执行任何操作来使其崩溃。您的开发人员也可能是这个意思,所以也许您应该请他澄清一下。
Java 通过使其不可能创建无效引用来处理这个特定问题 - 您无法在 Java 中手动释放内存,因此特别是在执行此操作后您无法保留对它的引用。它以其他方式处理许多其他特定问题,因此在 C++ 中属于“未定义行为”并且很可能导致崩溃的情况在 Java 中会执行不同的操作(可能会引发异常)。
[*] 让我们面对现实:在实践中,在大型软件项目中,“经常”。
Not sure who "the user" is.
You can write programs that won't crash due to invalid end-user input. On some systems, you can be forcefully terminated due to using too much memory (or because some other program is using too much memory). And as Remus says, there is no language which can fully protect you against hardware failures. But those things depend on factors other than the bytes of data provided by the user.
What you can't easily do in C++ is prove that your program won't crash due to invalid input, or go wrong in even worse ways, creating serious security flaws. So sometimes[*] you think that your code is safe against any input, but it turns out not to be. Your developer might mean this.
If your code is a function that takes for example a pointer to the image data, then there's nothing to stop the caller passing you some invalid pointer value:
So the function on its own can't be "crash-proof", it requires that the rest of the program doesn't do anything to make it crash. Your developer also might mean this, so perhaps you should ask him to clarify.
Java deals with this specific issue by making it impossible to create an invalid reference - you don't get to manually free memory in Java, so in particular you can't retain a reference to it after doing so. It deals with a lot of other specific issues in other ways, so that the situations which are "undefined behavior" in C++, and might well cause a crash, will do something different in Java (probably throw an exception).
[*] let's face it: in practice, in large software projects, "often".
我认为这是C++代码不是托管代码的情况。
Java、C# 代码是受管理的,也就是说,它们由能够执行边界检查并检测崩溃情况的解释器有效执行。
对于 C++ 的情况,您需要自己执行绑定和其他检查。但是,您可以使用异常处理,这将防止在您无法控制的事件期间发生崩溃。
最重要的是,C++ 代码本身并不能防崩溃,但良好的设计和开发可以使它们如此。
I think this is a case of C++ codes not being managed codes.
Java, C# codes are managed, that is they are effectively executed by an Interpreter which is able to perform bound checking and detect crash conditions.
With the case of C++, you need to perform bound and other checking yourself. However, you have the luxury of using Exception Handling, which will prevent crash during events beyond your control.
The bottom line is, C++ codes themselves are not crash proof, but a good design and development can make them to be so.
一般来说,您无法使 C++ API 防崩溃,但可以使用一些技术来使其更加健壮。对于您的特定示例,我的脑海中浮现出(绝不是详尽无遗):
In general, you can't make a C++ API crash-proof, but there are techniques that can be used to make it more robust. Off the top of my head (and by no means exhaustive) for your particular example:
如果“崩溃证明”仅意味着您希望确保在发生崩溃后有足够的信息来调查崩溃,那么解决方案可能很简单。大多数情况下,在崩溃期间丢失调试信息是由于线程之一中运行的代码进行非法内存操作而导致堆栈数据损坏和/或丢失。如果您在几个地方调用您不信任的库或 SDK,则可以在调用该库之前在全局变量指向的某个内存位置保存堆栈跟踪,该全局变量将包含在部分或完整内存转储中当您的应用程序崩溃时由系统生成。在 Windows 上,此类功能由 CrtDbg API 提供。在 Linux 上,您可以使用 backtrace API - 只需在 show_stackframe() 上搜索文档。如果您丢失了堆栈信息,则可以指示调试器在加载转储文件后使用内存中的该位置作为堆栈顶部。好吧,这毕竟不是很简单,但如果你被内存转储困扰而不知道发生了什么,它可能会有所帮助。
嵌入式应用程序中经常使用的另一个技巧是循环内存缓冲区以进行详细记录。记录到缓冲区的成本非常低,因为它永远不会被保存,但是您可以通过在崩溃后查看内存转储中的缓冲区内容来了解崩溃前几毫秒发生的情况。
If "crash proof" only mean that you want to ensure that you have enough information to investigate crash after it occurred solution can be simple. Most cases when debugging information is lost during crash resulted from corruption and/or loss of stack data due to illegal memory operation by code running in one of threads. If you have few places where you call library or SDK that you don't trust you can simply save the stack trace right before making call into that library at some memory location pointed to by global variable that will be included into partial or full memory dump generated by system when your application crashes. On windows such functionality provided by CrtDbg API.On Linux you can use backtrace API - just search doc on show_stackframe(). If you loose your stack information you can then instruct your debugger to use that location in memory as top of the stack after you loaded your dump file. Well it is not very simple after all, but if you haunted by memory dumps without any clue what happened it may help.
Another trick often used in embedded applications is cycled memory buffer for detailed logging. Logging to the buffer is very cheap since it is never saved, but you can get idea on what happen milliseconds before crash by looking at content of the buffer in your memory dump after the crash.
实际上,使用边界检查会使您的应用程序更有可能崩溃!
这是一个很好的设计,因为这意味着如果您的程序正在运行,那么它更有可能正常运行,而不是错误运行。
也就是说,严格来说,在解决停止问题之前,无法使给定的应用程序“防崩溃”。祝你好运!
Actually, using bounds checking makes your application more likely to crash!
This is good design because it means that if your program is working, it's that much more likely to be working /correctly/, rather than working incorrectly.
That said, a given application can't be made "crash proof", strictly speaking, until the Halting Problem has been solved. Good luck!