4 ms·
Not really. https://web.archive.org/web/20210508125532/https://twitter.com/m13253/status/1371615680068526081 https://web.archive.org/web/20210508125532/https:/
by gavinhoward 3y ago
Not really.
https://web.archive.org/web/20210508125532/https://twitter.com/m13253/status/1371615680068526081 https://web.archive.org/web/20210508125532/https://twitter.c...
https://godbolt.org/z/1q1bjn https://godbolt.org/z/1q1bjn
https://news.ycombinator.com/item?id=26498233 https://news.ycombinator.com/item?id=26498233
Nasal demons was an analogy, not something to take literally.
- UncleMeat 3y agoOh come on, this is exactly what I'm talking about. We've seen these before. The "format your hard drive" one is instead "completely fuck up your control flow and jump between functions" and it is purposefully constructed. People could have just focused on that - it was already intense enough. And you can happily get fucked up control flow with good old fashioned modular arithmetic errors. C and C++ are both nightmare languages and the connection between UB and the as-if rule are a mess. But incorrect programs can still blast off into incredible harm without the optimizer's help. And the emphasis on UB because it is so fun to construct weird scenarios has led to a world where people make strange and ill defined demands for how the compiler should react in these situations.
- gavinhoward 3y agoIt may be purposefully constructed for the example, but it is also easily constructed. This is only possible because the compiler deletes code. Yes, you can get messed up control flow from modular arithmetic code, but the compiler is not going to delete that code if it happens to overflow. And just because it's purposefully constructed doesn't mean it won't happen in real code, which is much more complicated and liable to triggering such problems.
- UncleMeat 3y agoThe compiler deletes code all the time in well formed programs. You typically want this. I do agree that the "but I put the check there" argument is real and that it absolutely breaks the principle of least surprise that you could look at your source and see a check that is important yet doesn't actually end up in the binary. But basically nobody has provided a rigorous conception of what a compiler shouldn't do w.r.t. undefined behavior. "Just don't delete my code" doesn't work since that's a big portion of what makes compiler optimization valuable. "Don't ever optimize based on UB assumptions" doesn't work, since that means that literally any function without a body available to the current translation unit can havoc all memory (or worse, even another function running concurrently).
- gavinhoward 3y ago> "Don't ever optimize based on UB assumptions" doesn't work Why not? Why is that somehow worse than code that breaks everywhere, all the time? Correctness should always come first. Sometimes performance is part of correctness (real-time), but it is not most of the time. When performance is not a part of correctness, why would optimizing for performance even be a possibility? And having done real-time work, I can tell you that the output of the compiler is scrutinized heavily in that domain. If the compiler is optimizing according to UB, changes would be made. But to answer your question, this is what I would do: If a programmer did not opt-in to the UB, don't optimize according to the assumption of lack of that particular UB. Programmers do not opt-in to signed overflow as UB, so it should never be used as an optimizing assumption. Programmers do opt-in to UB for data races by sharing memory across threads or signal handlers, so compilers can optimize against that. Programmers do not opt-in to non-terminating loops as UB, so compilers should not optimize to that. Programmers do opt-in to strict aliasing by using pointers to different types, or by using the `restrict` keyword, so the compiler can optimize on that. You may complain that if I use signed types, I'm opting-in to that UB. I disagree, but even if you are right, I'm explicitly opting-out by using unsigned types. And yet, you'll still complain about that; you started this discussion by complaining about developers using unsigned types because it would be slower, even though you acknowledged that the bugs wouldn't be as bad. In other words, you want performance at any cost, so much so that you'll complain when people use mitigations against compiler optimizations that can raze code like Rome razed Carthage. So you and I will never see eye-to-eye because you firmly believe that performance is everything, and I firmly believe that correctness is everything. I usually try to be diplomatic on HN (and often fail), but this is one time I can confidently say: you are dead wrong. By the way, I have public code that uses unsigned types as much as possible, and it still holds its own against an implementation that uses a heavily optimized library. So you are also wrong that unsigned types make it noticeably slower.
- UncleMeat 3y ago> Why not? Why is that somehow worse than code that breaks everywhere, all the time? Imagine this code int x = 0; f(); return x; In a world where you can't optimize based on UB assumptions, x can change in the body of f. So instead of returning the constant zero, you need to allocate space on the stack, store to it, and then read from that location after f returns. Arbitrary pointer arithmetic means that any function can havoc all memory. Even worse, this can happen in another thread. You don't need to share memory across threads to opt in to data races. You can access whatever you want with pointer arithmetic if you want to be correct in the presence of UB. Or this code void do(foo* f) { if (f == nullptr) return; f->do(); } f could be deleted. This could be UB. You need runtime tracking of deleted memory locations (not even objects, since we can set up arbitrary aliases in our UB-defensive world). You can use ASan for this, but even that isn't guaranteed to detect the UaF so you are going to need even more overhead. Even worse, you can't do this if you've got multiple threads. You don't even have to have shared f across threads, you just need to have shared something else and let the other thread do some pointer arithmetic. Any runtime check that f is not deleted can be raced. > So you and I will never see eye-to-eye because you firmly believe that performance is everything I do not believe this. I am one of the people at my company fighting to spend performance to buy safety. I just don't believe that the principle of least surprise can be implemented in the standard in the way some people say and I don't believe that UB is totally unique from other forms of incorrectness in a way that should cause us to behave very differently about it. > I firmly believe that correctness is everything. Do you run your production binaries with all of the sanitizers enabled?