Compilers have become more powerful (opening up new ways to exploit undefined behavior) and the primary C compilers are free software with corporate sponsors, not programmer customers (or else perhaps Andrew Pinski would not have been so blithe about ignoring his customer Felix-gcc in the GCC bug report cited above). This is the real problem. We have reached a situation where a small number of compilers dominate the…
> The C standard is a product of an era where you would pay for your tools and so would demand a refund from any compiler vendor that would treat undefined behavior in an absurd manner. Since current compilers aren’t out to do anything malicious with UB, but instead simply treat it as “assume this can’t happen and proceed accordingly”, it’s not clear at all to me what you think paid compilers would do here instead: r…
Undefined behavior in C is a reading error
171–180 of 503 posts
Re: Undefined behavior in C is a reading error
#172Earlier quoted context omitted.
"Undefined behavior" is a term of art relevant to C, meaning that the standard no longer has any comment about what happens. Thus the comments about launching nukes, or destroying your machine, etc., being standards complaint, even though obvious real compilers won't actually emit code that does that. Dereferencing a nil pointer in Java is not undefined behavior. It is defined; it throws a NullPointerException, and "…
Point taken about NPEs being defined behavior, but from a practical point of view, a bug is a bug (and bugs is what the parent comment was referring to). Whether the bug was due to a well-defined or undefined behavior seems like an exercise in assigning blame to another entity (the language, the committee, the compiler, etc).
Re: Undefined behavior in C is a reading error
#173Earlier quoted context omitted.
You are mistaken, the C standard is quite clear that it does not make any guarantees regarding the behavior of programs that exhibit undefined behavior, and that signed integer overflow is undefined behavior.
"for (int i=param; i < param + 16; i++) does not have a guaranteed loop count in the presence of undefined behavior" is true, but it's equally true that the C standard is quite clear that undefined behavior can be ignored, so we can validly treat "for (int i=param; i < param + 16; i++)" as if it were guaranteed to loop 16 times in all cases.
It says, "NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, ...".
It doesn't say that the behavior can be ignored. It says that the undefinedness can be ignored. The implementation doesn't have to take notice of the fact that the behavior is undefined.
Let's take a simpler example:
printf("%d\n", INT_MAX + 1);
The behavior is undefined. The standard does not guarantee anything about it. A conforming implementation can reject it at compile time, or it can generate code that crashes, or it can generate code that emits an ADD instruction and print whatever the hardware returns, or it can play roge at compile time. (The traditional joke is that can make demons fly out of your nose. Of course it can't, but an implementation that did so would be physically impossible, not non-conforming.)An implementation might define the behavior, but it's still "undefined behavior" as that term is defined by the ISO C standard.
Re: Undefined behavior in C is a reading error
#174Earlier quoted context omitted.
> But I also think a lot of discussions of this topic caricaturize compiler writers to a ridiculous degree. The inflamed backlash should tell you just how damaging it is to impose silent failure on meticulously written, previously fine programs.
> meticulously written, previously fine programs With relatively few exceptions, if your program hits undefined behavior, then your program was already doing something pretty wrong to begin with. Signed overflow is a poignant example: in how many contexts is INT_MAX + 1 overflowing to INT_MIN actually sane semantics? Unless you're immediately attempting to check the result to see if it overflowed (which is extremely…
It had code like:
int *p;
// lots of code
if (p != NULL)
return 1;
// use p
Then a later refactor wrongly added a single line before the if statement: int *p;
// lots of code
int a = *p;
if (p != NULL)
return 1;
// use p
This meant the null check was optimized away, since de referencing a null pointer is undefined behavior, so the if-statement can be assumed to be always false.
This then lead to actual errors (perhaps even an exploit, I do not recall) arising from the removed null-check.I think in general the "sanity check" cases are the worst. It is hard to determine whether an expression causes undefined behavior if you cannot try and evaluate it. Perhaps a compiler intrinsic that checks (at runtime) whether an expression causes undefined behavior could be useful here. Though I can imagine such an intrinsic being essentially impossible to implement.
Re: Undefined behavior in C is a reading error
#175Re: Undefined behavior in C is a reading error
#176Earlier quoted context omitted.
> all of the tricks which make "malloc" work, What are those, exactly? AFAIK, you can safely track memory addresses by storing them as intptr_t/uintptr_t.
The C standard says very little about how those types work. In particular, you can cast a pointer to one of them and then cast back to a pointer -- but only if you cast the exact same value back, and the intptr values are not guaranteed to be in any way meaningful. In particular, casting a pointer to intptr_t, doing arithmetic on it, and casting back is not guaranteed to do anything useful. It almost certainly will,…
Re: Undefined behavior in C is a reading error
#177Earlier quoted context omitted.
> For example, if signed integer overflow yielded an unspecified result rather than causing undefined behavior, I wonder if any implementations would be adversely affected. I suspect so - makes it harder to reason about loop counts because the compiler can't necessarily guarantee that an incremented loop counter won't become negative and thus the loop needs to iterate more. E.g. something like for (int i=param; i Tha…
Assuming you defined signed integer overflow to follow two’s complement rules (the only reasonable interpretation other than UB), it would still be a guaranteed loop count of 16. (EDIT: i’m a dumbass, this is obvs not true. disregard this paragraph) There’s an interesting thing to note with that example though: even if you did make signed integer overflow defined, that code is still obviously incorrect if param + 16…
for a loop counter you want an index type that will seg fault on overflow. If you think not having that check is worth it the programmer would need to tag it with unsafe.
It's also problematic because it's size is defined as at least 16 bits. But programmers which means you should never use it to store a constant larger than 16 bits. But people do that all the time.
Re: Undefined behavior in C is a reading error
#178First: I do dislike how hard it is to avoid some UB / how impractical some of the rules are. But I also think a lot of discussions of this topic caricaturize compiler writers to a ridiculous degree. Almost describing them to write optimization passes looking for UB so they can over-optimize something, while cackling loudly in glee about all the programs they can break. The set of people doing so overlaps with the set…
The current situation is not good for compiler writers either. But nobody has ever shown that either C programmers want to sacrifice safety for "optimizations", or that these UB optimizations actually improve performance of anything.
What about backwards compatibility in regards to a new compiler version deciding to issue errors on UB now? You don't have any guarantees about what happens with UB right now, so if you upgrade to a new version compiler that generates errors instead of "optimizations" everything would be still as before: no guarantees. And it's frankly a lot better to blow up the compilation with errors than to have the compiler accept the UB code and roll a dice on how the final binary will behave later. You can either fix the code to make it compile again, or use an older "known good" version of the compiler that you previously used as a stopgap measure.
I fail to see any reason whatsoever why compilers are still doing all kinds of stupid stuff with UB instead of doing the right thing and issuing errors when they encounter UB.
I also fail to see why the C language designers still insist on keeping so much of the legacy shit around.
Re: Undefined behavior in C is a reading error
#179It truly boggles my mind that this discussion (linked in TFA) played out the way it did. In my mind it's 100% not okay to (by default) optimize away a check for integer overflow. I've never really written in C (or any unsafe language) before so I had little context for the types of traps C sets for developers. Based on the responses of the person who presumably implemented the optimization, it comes as no surprise th…
> In my mind it's 100% not okay to (by default) optimize away a check for integer overflow. But a + 100 > a is not a check for overflow. If a >= INT_MAX - 100, it is an overflow. The "will this operation overflow" check would be a >= INT_MAX - 100, and GCC would not optimize that away.
int a,b,c;
a=INT_MAX; /\* statement ONE */
b=a+2; /* statement TWO */
c=(b>a); /* statement THREE \*/
Whether or not you think this is technically permissible by a sufficiently self-serving (from the perspective of a person whose ONLY goal is speed optimization) reading of spec is irrelevant. Any reading of the spec should err on the side of protection against real-world consequences.I don't want lawyering and technically correct-ing, I want pragmatism.
Re: Undefined behavior in C is a reading error
#180Earlier quoted context omitted.
for (int i=param; i does not have a guaranteed loop count with the current rules. The loop body will execute 16 times if param <= INT_MAX-16, but if the expression "param + 16" can overflow, the behavior is undefined. (I'm assuming param is of type int.)
That's precisely my point? Because the overflow case is undefined, the compiler can assume it doesn't happen and optimize based on the fixed loop count.