6 ms·
A better title would be security flaws caused by relying on undefined behavior.
by qw3rty01 7y ago
A better title would be security flaws caused by relying on undefined behavior.
- ummonk 7y agoGiven that practically anything under the sun is undefined behavior for C and C++, that isn’t saying much.
- papermachete 7y agoWhat gives you this imimpression?
- bhk 7y agoThe C standard includes an appendix that lists ~200 examples of undefined behavior. This list does not claim to be exhaustive. Often, what constitutes undefined behavior is non-obvious (and not well justified). For example, when adding two signed integers results in an overflow, it is undefined behavior even if your program never uses the result. Due to C's definition of "undefined" behavior, it means that all of the guarantees we rely on to ensure security go out the window whenever the programmer steps on one of these land mines.
- caspper69 7y agoNot all UB falls into this category. A lot of UB, such as your signed integer addition example, is dependent upon the behavior of the underlying hardware. Certain archs may throw an exception on signed integer overflow, or exhibit otherwise inconsistent behavior, for example. The standard is the standard, of course, but not all implementations inherit the UB of the standard.
- bhk 7y agoWhether or not something is "undefined behavior" has nothing to do with the hardware. The C specification says what is specified, what is "unspecified", what is "implementation defined", and what is "undefined". If something is "undefined" according to C, you can't rely on what the hardware does, because the hardware might not even get a chance to do anything. The compiler may completely elide sections of your program -- and they do in practice (for example, bounds checks). Actually, hardware always does something reasonable for add instructions (throw exception or overflow or saturate). It's additions in C that can have unreasonable results.
- Gibbon1 7y ago> A lot of UB, such as your signed integer addition example, is dependent upon the behavior of the underlying hardware. That problem is you can no longer depend on that because the compiler writers have decided they can do anything during AST optimization when there is undefined behavior.
- qw3rty01 7y agoIt says everything. Even if undefined behavior works one way on all platforms today, it could work differently tomorrow in a way that introduces bugs. It's nonsensical to blame the compiler for conforming to the standard in a way that breaks code using undefined behavior.
- papermachete 7y agoWhat about I-know-what-I'm-doing const_casts in critical software like operating systems and kernels? Do you think developers should distribute binaries they know work, or let people maybe cause bugs with compiler options?
- qw3rty01 7y agoThere is no guarantee it would work if it relies on undefined behavior--that's what undefined means, and also the reason the optimizer acted the way it did. In that case, adding flags that defines what happens when particular undefined behavior hits would probably be the way to go, or rewrite it in a way that doesn't rely on undefined behavior.
- perl4ever 7y agoThe concept of "undefined" is incoherent. At the same time as people insist the compiler can do anything under the circumstances, everyone accepts that there is some limit to what it is reasonable to expect it to do. It's all just quibbling over where exactly the limits are. But as long as there are limits, the definition of undefined was never valid. It seems to me that the problem is that trying to define undefined behavior is an inherent contradiction. Setting aside the question of what exactly "undefined behavior" means, why does a language spec have to include it? If there is behavior that cannot be defined, why not just omit it from the standard?
- zrm 7y ago> Setting aside the question of what exactly "undefined behavior" means, why does a language spec have to include it? If there is behavior that cannot be defined, why not just omit it from the standard? The original reason was that there were things they didn't want to define. For example, signed integer overflow works differently on different hardware architectures. If they defined one behavior in the standard then compilers for architectures that didn't do it that way would have to do something inefficient to make it work the way the standard says it should rather than the way that hardware actually does it. Calling it "undefined behavior" lets the compiler do whatever the hardware does even if that means the program produces different results on different architectures. It also means that if some new architecture comes out that does it slightly differently, nobody can be surprised when compilers use the native overflow behavior for that architecture. The flaw was in giving compilers too much discretion. They were generally expected to implement one of the sane versions of signed integer overflow, and specifically the one corresponding to the relevant hardware architecture, but according to the spec they can literally do whatever they want. So we get this: https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html https://kristerw.blogspot.com/2016/02/how-undefined-signed-o... x + c < x -> false Which means you can't use that to check whether signed overflow occurred even when you know the underlying hardware behavior, because if it did occur you've already invoked UB and the compiler is allowed to do anything, including omit your check, which it does. What would help a bit is if compilers are going to do something like this, they emitted a warning something like "comparison is always false because signed integer overflow is undefined." What would help even more is for the next version of the standard to convert a lot of this undefined behavior into implementation-defined behavior or similar, which still allows for hardware-specific implementations but requires them to be documented and prevents a lot of this unintuitive ex post facto "optimization" that causes more trouble than it's worth.