Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

421–430 of 503 posts

Re: Undefined behavior in C is a reading error

#421
post #113

Earlier quoted context omitted.

> If, instead, we force compilers to think about the fact that offset + 16 could have some implementation-defined meaning like wrapping, then all bets are off & we have to throw a bunch of optimization opportunities out the window. Uh huh. If `i` is declared as `unsigned int` instead of `int`, then overflow is defined and the compiler can't apply those optimizations. And yet the world doesn't end and the sun will sti…

> And yet the world doesn't end and the sun will still rise tomorrow... No, you just get much slower, non-vectorized code because the compiler is forced to forgo an optimization if you use unsigned int as the loop bound (EDIT: tom_mellior's reply illustrates this extremely well: https://gcc.godbolt.org/z/cje6naYP4 ) Which is precisely the point: forcing a bunch of existing code with int loop bounds, which currently e…

And switch the i to a size_t and get vector code without the possibility of writing to random memory because your int overflows and GCC wants to pretend it cannot.

This is a poorly written loop. C design model is that if it is not critical, we don't care, and if it is, the programmer should fix it so optimization can work. https://gcc.godbolt.org/z/ErMP4cn6s

Re: Undefined behavior in C is a reading error

#422
post #371

Earlier quoted context omitted.

> Dereferencing a pointer further in the code shouldn't be a valid justification for optimizing out previous tests of it being null. Not further. Anywhere. If the implementation wishes to rewrite pointer dereferences from `use(*p)` to: if(!p) abort(); use(*p); it may do so (undefined behaviour!), but if it chooses not to do so, it may not later pretend that it did, and remove a explict `if(!p)` that the programmer wr…

What is the meaning of use(*p) in case p is null? what should the compiler emit?

> What is the meaning of use(*p) in case p is null?

It dereferences a null pointer, invoking undefined behaviour, then calls the function `use` with the resulting value.

> what should the compiler emit?

Probably something to the effect of:

  ld r0 [sp+.p]  # if p is not already in a register
  ld r0 [r0]  # *p
  jsr use
but it would be fine to emit something like:

  ld r0 [sp+.p]  # if p is not already in a register
  jz r0 .panic
  ld r0 [r0]  # *p
  jsr use
because the jz can only be taken when undefined behaviour happens.

Re: Undefined behavior in C is a reading error

#423
post #197
post #59

Earlier quoted context omitted.

> Vanilla C no longer gives them the tools to do that on a modern processor Can you elaborate on this point?

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.

Re: Undefined behavior in C is a reading error

#424
post #174

Earlier quoted context omitted.

I recall a bug-report discussion that I sadly have never been able to find. It contains a pretty bad side-effect of this. 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 poi…

Actually, there's a even worse version: struct foo { ...; bar_t bar[NBAR]; }; struct foo* p = ...; bar_t* q = &p->bar[0]; // add rq, rp, #foo_bar_offs // other declarations if(!p) return NOPE; // optimized out // use p and q Not even any dereferencing, just pointer arithmetic.

Yes, that one is especially nasty as &p->bar[0] is a constant expression completely solvable at compile time. It's equivalent to the offsetof() macro of stddef.h

Re: Undefined behavior in C is a reading error

#425
post #273
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…

Thanks for the link. Excellent article. Those are all great points and I like having them summarized in one place, because I am indeed a little behind in my modern architecture theory. However it has always been acknowledged that C was by definition a sort of simplified computing model. For example, when I first learned see the 8086 architecture was popular but it was competing with many others and it was already dra…

That article is nonsensical. Seymour Cray and colleagues and Tomasula and colleagues invented ILP, branch prediction, etc. in the 1960s before C was thought of. The PDP11 is much more similar to modern x86s than to the weird architectures of the 1970s.

Re: Undefined behavior in C is a reading error

#426
post #367

Earlier quoted context omitted.

Only moving away to other languages will do it. C culture and to certain extent Objective-C and C++ ones are tainted by microptimizaitons while typing, where the compilers are the worst examples. Unfortunely UNIX and C go together, so those that want to keep UNIX like platforms around bettter fix C somehow.

That sucks. I can imagine next generation operating systems improving on Unix technically (standing on the shoulders of giants, yo), but establishing standards a la POSIX will be extremely difficult. It's hard to fight against the interest of platform vendors to lock their users in.

Microsoft fought for several years against C, trying to migrate everyone to C++ as the future of Windows systems programming.

Yet Azure Sphere, despite its security message, uses C only SDK.

Meanwhile Visual Studio now supports C11 and C17.

Market pressure, their customers weren't willing to buy into it, and the new Microsoft also wants all those POSIX FOSS packages written in C running on Windows.

Re: Undefined behavior in C is a reading error

#427
post #409

Earlier quoted context omitted.

The relevant discussion about UB is restricted to constructa that the standard describes. For example, writing past the end of object is UB - the construct is described in the standard, but is given no semantics by the standard. The standard does not describe pattern matching, so using pattern matching is also undefined behavior, but there is nothing to be talked about here.

The comment I replied to did talk about something not described by the standard though, namely syscalls. If you want to argue that we should not be talking about syscalls here, your issue should be with the original comment that brought them up ( https://news.ycombinator.com/item?id=27222325 ), not with my reply, I think. However, that comment looks perfectly fine to me. Also, depending on how the syscalls are made,…

Syscalls are not any more UB than any other function call, though. Whether talking about write(2) or my_foo(), the call has the semantics given by the function signature visible in the current translation unit. 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.

If the function has not been declared by the time it is first used, even then calling it is not UB - it is defined to be a compilation error (in versions earlier than C99 it was actually valid, but UB if the call did not match the actual function definition).

Re: Undefined behavior in C is a reading error

#428

Earlier quoted context omitted.

These are all issues a compiler could insert checks for, saving you the time to do it manually, while trading performance for security or correctness. C doesn't trade performance away, so if you'd like to pay the price of these checks, it's on you, the programmer to add them in. C programmers have to put in extra effort to make the executables safer and output correct results. Other languages trade off performance fo…

C is designed to allow programmers to insert or remove checks as performance tuning. Compiler UB "optimizations" that remove that ability from the programmer make the language unusable.

Not knowing what is UB in C is equivalent to not knowing a core part of the C language. In that situation, yes, C would seem unusable for people who have superficial knowledge of it.

Re: Undefined behavior in C is a reading error

#429
post #409

Earlier quoted context omitted.

The comment I replied to did talk about something not described by the standard though, namely syscalls. If you want to argue that we should not be talking about syscalls here, your issue should be with the original comment that brought them up ( https://news.ycombinator.com/item?id=27222325 ), not with my reply, I think. However, that comment looks perfectly fine to me. Also, depending on how the syscalls are made,…

Syscalls are not any more UB than any other function call, though. Whether talking about write(2) or my_foo(), the call has the semantics given by the function signature visible in the current translation unit. 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. If the function has not been declared by the time it is first used,…

> 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) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

If your program provides a declaration of write() and uses it without also providing a definition, the program does not have "exactly one external definition for the identifier", it has zero definitions for the identifier. This violates a "shall" that appears outside of a constraint, for which we turn to:

C99 4 Conformance:

> If a "shall" or "shall not" requirement that appears outside of a constraint is violated, the behavior is undefined.

Re: Undefined behavior in C is a reading error

#430

This was posted by a user with a name closely matching the domain (perhaps the original author?) 16 hours prior, and flagged: https://news.ycombinator.com/item?id=27215697 When I stumbled across it last night, I couldn't understand why that would be. The content seemed good enough for readers on here, and this one's placement on the 2nd page of HN seems to confirm that. What's going on?

Good question. I'd love to know. Is there any way to find out?
Post reply on HN