Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

461–470 of 503 posts

Re: Undefined behavior in C is a reading error

#461
post #121

Earlier quoted context omitted.

While technically correct, “undefined behavior” in terms of C and C++ refer to what the standard calls out explicitly as undefined, and not a simple “it’s not referenced, therefore it’s undefined.” For example, signed(?) integer overflow is explicitly undefined by the standard, but as @formally_proven said, just because write(2) isn’t mentioned doesn’t mean usage of it is undefined.

Actually, that's exactly what it means: > If a "shall" or "shall not" requirement that appears outside of a constraint or runtime-constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words "undefined behavior" or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe "…

> but the C standard says nothing about what that function does

I'm pretty sure I'm just bad at searching through the standards document, but does the Standard actually define the precise semantics of function calls? 6.2.2 is about the function calls and the result thereof, but doesn't seem to be quite as precise about the semantics as I might expect.

Re: Undefined behavior in C is a reading error

#462

Earlier quoted context omitted.

But you're not using the offset to compute the index in bar(). void foo(int offset, int *arr) { for(int i = offset; i If you call this with offset = 100, the arr[i] in the loop will write to arr[100], arr[101], ..., arr[115]. void bar(unsigned int offset, int *arr) { if(offset+16 If you call this with offset = 100, the arr[i] in the loop will write to arr[0], arr[1], ..., arr[15].

Fixed it, but same kind of result https://gcc.godbolt.org/z/hx1zjE5xW

Nice. If you like, could you explain again what the perceived difference is to adding

    if (offset >= INT_MAX - 16) fail();
in foo()?

(I mean, besides the fact that the size of the buffer pointed to by arr is highly unlikely to agree exactly with either INT_MAX or UNIT_MAX.)

Re: Undefined behavior in C is a reading error

#463

Earlier quoted context omitted.

I mean, that works ? It's not great by any means, but it at least eliminates the ability to make the assumptions underlying more aggressive optimizations, which seems like it'd address one of the bigger concerns around said optimizations.

Perhaps I should have phrased it as "all implementation-defined behaviour is whatever the hardware happens to do when executing whatever code the compiler happens to generate". The point of implementation-defined behaviour is that the implementation should be required to actually define the behaviour. Whereas undefined behaviour doesn't impose any requirements; the implementation can do whatever seems reasonable on a…

> Perhaps I should have phrased it as "all implementation-defined behaviour is whatever the hardware happens to do when executing whatever code the compiler happens to generate".

Even with this definition, the important part is that compilers would no longer be able to ignore control flow paths that invoke undefined behavior. Signed integer overflow/null pointer dereference/etc. may be documented to produce arbitrary results, and that documentation may be so vague as to be useless, but those overflow/null pointer checks are staying put.

Re: Undefined behavior in C is a reading error

#464
post #429

Earlier quoted context omitted.

> Sure, the C standard doesn't define what write(2)'s effects will be, but that does not mean that calling it is UB according to the standard. Yes, it does. I already explained exactly why it needs to be UB, but let me quote where the standard says so: C99 6.9 External definitions: > Semantics: > An external definition is an external declaration that is also a definition of a function (other than an inline definition…

> let me quote where the standard says so: Wouldn't this hinge on what precisely "entire program" means? A definition for write(2) may not appear in the source code you wrote, but if "entire program" includes e.g., libraries dynamically linked in then it's quite feasible for the end result to be fully defined. For example, 5.2.2 Paragraph 2 starts with (emphasis added): > In the set of translation units and libraries…

Sure, but in the situation we were talking about, the user never wrote a definition for write(), and the user did not specify any library to include that provided a definition of write(). From the standard's perspective, that means there is no definition for it in the entire program.

Keep in mind that the standard's perspective is somewhat different from how things work in practice. We know that on Unix-like systems, there is also the concept of libraries, somewhat different from how the standard describes it, and write() will be provided by the "c" library. But consider the following strictly conforming program:

  #include 
  void write(void) {
    puts("Hello, world!");
  }
  int main(void) {
    write();
  }
A confirming C implementation is not allowed to reject this for a duplicate definition of write(): the name "write" is reserved for use by the programmer, it is not reserved to the implementation. This program must be considered not to violate the "there shall be exactly one external definition for the identifier", so the only way to consider this valid is to say that the implementation does not implicitly provide an external definition of the write() function as far as the C standard is concerned.

Yet at the same time, from the perspective of the implementation, the c library is considered to provide a definition of the write() function, but it is a definition that is only used if the program does not override it with another definition that should be used instead. This concept of multiple definitions for the same name, with rules specifying which of the multiple definitions gets picked, is very useful but is also beyond the scope of the C standard. When we say that a function is defined, we need to be clear on whether we use "define" in the ISO C sense or in some other sense. As your comment shows, things get very confusing if we are not careful with that.

Re: Undefined behavior in C is a reading error

#465
post #197

Earlier quoted context omitted.

C was created during a time where instructions were executed linearly with no vectorization, memory was a flat space with no CPU caches, and there wasn’t a branch predictor that may or may not execute the correct program branch in advance. The list goes on but the rest is beyond my scope. C was designed for a now obsolete computer architecture model and over the years this old model has essentially become an abstract…

The Chisnall article is a tutorial in incorrect Computer Architecture. PDP11s had caches by the 1970s and always had memory management. IPL was invented in the 1960s and has nothing to do with C. Branch predictors were invented in the 1960s too and one of the first machines C was ported to was the IBM370 which had super sophisticated IPL. Etc.

I was unaware. Thank you for the correction! Do you have any further reading on this topic?

Re: Undefined behavior in C is a reading error

#466
post #104
post #40

Earlier quoted context omitted.

Linking a failed discussion from another (possibly) failed discussion seems kind of pointless.

Just like this thread.

I assumed your labelling it a dupe and giving a link meant there was a more interesting discussion on that page. Nope. So I left my comment to warn others not to make the same mistake I did. You didn't seem to understand, so someone tried explaining, then you left another obtuse, mean-spirited comment.

Re: Undefined behavior in C is a reading error

#467

Earlier quoted context omitted.

Fixed it, but same kind of result https://gcc.godbolt.org/z/hx1zjE5xW

Nice. If you like, could you explain again what the perceived difference is to adding if (offset >= INT_MAX - 16) fail(); in foo()? (I mean, besides the fact that the size of the buffer pointed to by arr is highly unlikely to agree exactly with either INT_MAX or UNIT_MAX.)

I wanted to show that we don't need UB justified deletes to get good code generation. There was no need to break all that working code when we could have just told people that size_t counters worked better in loops on x86-64 than ints. A lot of C optimization could work that way - relying on cooperation between the compiler and programmers. Java can't do that because Java programmers rely on complex abstractions that need a lot of compiler work to run fast.

Re: Undefined behavior in C is a reading error

#468

Earlier quoted context omitted.

> Hardware doesn't define C semantics. C defines C semantics. C does not define C sematics for undefined behaviour ; that's what makes it undefined behaviour. > an uninitialized y is an actual uninitialized register or memory location. On typical hardware, yes.

>> an uninitialized y is an actual uninitialized register or memory location. > On typical hardware, yes. Maybe you can help me identify the register or memory location for y in your example: https://gcc.godbolt.org/z/3b4z56Y87

Link is broken; says:

> Without Javascript the regular website is not functional. To go to the noscript version Compiler Explorer click here

"click here" goes to https://gcc.godbolt.org/noscript/z/3b4z56Y87, which has:

  int choose(int cond1, int cond2)
    {
    int y;
    int x1 = cond1 ? 2 : y; /* So int x1 = 2; */
    int x2 = cond2 ? 3 : y; /* So int x2 = 3; */
    return x1 + x2;
    }
but no actual assembly.

Based on context, I'll speculate that you have found a compiler bug, that the compiler writers will refuse to fix it, and that the varible y has been optimised out because it's unused after constant propagation, regardless of whether said compiler bug causes the constants being propagated to be incorrect.

Re: Undefined behavior in C is a reading error

#469

Earlier quoted context omitted.

Perhaps I should have phrased it as "all implementation-defined behaviour is whatever the hardware happens to do when executing whatever code the compiler happens to generate". The point of implementation-defined behaviour is that the implementation should be required to actually define the behaviour. Whereas undefined behaviour doesn't impose any requirements; the implementation can do whatever seems reasonable on a…

> Perhaps I should have phrased it as "all implementation-defined behaviour is whatever the hardware happens to do when executing whatever code the compiler happens to generate". Even with this definition, the important part is that compilers would no longer be able to ignore control flow paths that invoke undefined behavior. Signed integer overflow/null pointer dereference/etc. may be documented to produce arbitrary…

Err, that's not a definition, that's a example of pathologically useless 'documentation' that a perverse implementation might provide if it were allowed to 'define' implementation-defined behaviour by deferring to the hardware. Deferring to the hardware is what undefined behaviour is, the point of implementation-defined behaviour is to be less vague than that.

> may be documented to produce arbitrary results, and that documentation may be so vague as to be useless, but those overflow/null pointer checks are staying put. [emphasis added]

Yes, exactly; that is what undefined behaviour is. That is what "the standard imposes no requirements" means.

Re: Undefined behavior in C is a reading error

#470

Earlier quoted context omitted.

>> an uninitialized y is an actual uninitialized register or memory location. > On typical hardware, yes. Maybe you can help me identify the register or memory location for y in your example: https://gcc.godbolt.org/z/3b4z56Y87

Link is broken; says: > Without Javascript the regular website is not functional. To go to the noscript version Compiler Explorer click here "click here" goes to https://gcc.godbolt.org/noscript/z/3b4z56Y87 , which has: int choose(int cond1, int cond2) { int y; int x1 = cond1 ? 2 : y; /* So int x1 = 2; */ int x2 = cond2 ? 3 : y; /* So int x2 = 3; */ return x1 + x2; } but no actual assembly. Based on context, I'll spe…

So on the one hand you're (rightly) saying that this function has no semantics under the rules of the C language, but on the other hand you're saying that the semantics that C compilers assign to it is a bug, and on the third hand you are saying that the hardware should assign semantics to it even though this code is not written in assembly language, so it's not clear what the hardware should assign semantics to? Got it.
Post reply on HN