Live data from Hacker News

The Problem with Friendly C

blog.regehr.org

11–20 of 174 posts

Re: The Problem with Friendly C

#11
post #7
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

If the standard writers had wanted to, they could have specified that a shift by an amount as wide or wider than the type had an "implementation-defined result", which is used elsewhere (for example, right-shifting a negative value gives an implementation-defined result).

Yes. And that would make sense.

They might not insist on exactly this, but they already leave this sort of possibility open by suggesting that undefined behaviour could result in the program behaving "in a documented manner characteristic of the environment".

If the standard writers' hands were tied by having to cater for every system they'd ever heard of, even the stupid ones where shifting by 32 (or whatever) results in rand() (or whatever) - implementers' hands are not! They have only one system to think about!

If that system happens to be one of the stupid ones - well, anybody working on it will have exactly the same problem.

But if it isn't one of the stupid ones - why do they insist that their users should behave as if it is?

Re: The Problem with Friendly C

#12

I think the most important point of Friendly C is not to define the behaviour for what would otherwise be undefined, but to define a behaviour; from this point of view, it would be unnecessary to argue over the examples he mentions like memcpy() vs memmove() and integer shifting --- it only suffices that every implementation define the behaviour, and what that behaviour precisely is can differ between them. A lot dif…

You have to look deeper for the OOB array access question. For example, imagine I have code like this:

  if (a > 1)
    b++;
  array[b] = 0;
  c = a + 10;
Under your suggested semantics, can the compiler use the value loaded from `a` at the point of the if() statement to calculate `a + 10`? Or does it have to emit a reload of `a` after the array access, in case `array[b]` was an OOB access that overwrote `a`?

Re: The Problem with Friendly C

#13
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

What modern interpretation? As long as C has been standardized, undefined behaviour has meant nasal demons.

There's an argument to be had that some undefined behaviour should rather be unspecified or implementation-defined, but compilers making use of it for aggressive optimization? That's by design.

Quoting another article by John Regehr:

My view is that exploiting undefined behavior can sometimes be OK if a good debugging tool is available to find the undefined behavior.

Thus -fsanitize=undefined.

Re: The Problem with Friendly C

#14
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

Apparently a (uint32_t) shifted by 32 is license to become completely insane.

  int main(int argc, char **argv) {
    uint32_t x = (uint32_t)0x12345678

Re: The Problem with Friendly C

#15
Target a virtual architecture (LLVM IR). Run the original code in an emulator for the target system (QEMU); give warnings where the two differ, with a switch for the virtual architecture to mimic the behavior of whatever architecture you need.

You get friendly C, a new way to think about warnings, and your old code can still be compatible with the new virtual architecture.

Next up, world hunger.

Re: The Problem with Friendly C

#16

Target a virtual architecture (LLVM IR). Run the original code in an emulator for the target system (QEMU); give warnings where the two differ, with a switch for the virtual architecture to mimic the behavior of whatever architecture you need. You get friendly C, a new way to think about warnings, and your old code can still be compatible with the new virtual architecture. Next up, world hunger.

This solution relies on some undecidable problems as being decidable

Re: The Problem with Friendly C

#17
post #13
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

What modern interpretation? As long as C has been standardized, undefined behaviour has meant nasal demons. There's an argument to be had that some undefined behaviour should rather be unspecified or implementation-defined, but compilers making use of it for aggressive optimization? That's by design. Quoting another article by John Regehr: My view is that exploiting undefined behavior can sometimes be OK if a good de…

Undefined behaviour has meant undefined behaviour. That doesn't mean nasal demons. That means this:

    Possible undefined behavior ranges from ignoring the situation 
    completely with unpredictable results, to behaving during translation
    or program execution in a documented manner characteristic of the
    environment (with or without the issuance of a diagnostic message),
    to terminating a translation or execution (with the issuance of
    a diagnostic message).
Permit me to suggest that when there is a choice between making things behave in a documented manner characteristic of the environment, or something else - don't go for the something else.

Re: The Problem with Friendly C

#18

Target a virtual architecture (LLVM IR). Run the original code in an emulator for the target system (QEMU); give warnings where the two differ, with a switch for the virtual architecture to mimic the behavior of whatever architecture you need. You get friendly C, a new way to think about warnings, and your old code can still be compatible with the new virtual architecture. Next up, world hunger.

You can't simply run code for all possible inputs.

Re: The Problem with Friendly C

#19

I think the most important point of Friendly C is not to define the behaviour for what would otherwise be undefined, but to define a behaviour; from this point of view, it would be unnecessary to argue over the examples he mentions like memcpy() vs memmove() and integer shifting --- it only suffices that every implementation define the behaviour, and what that behaviour precisely is can differ between them. A lot dif…

    I don't have anything to say about races, but OOB array
    accesses should simply do what you'd expect the hardware
    to do: attempt to access memory at the location the array
    indexing equation gives. It may segfault, or it may
    access some other contents in memory. That's what any C
    programmer would probably expect.
The problem is that defining OOB array access this way means throwing out pretty much every semantic guarantee from the rest of the language. There is really no friendly way to define writing an arbitrary value to an arbitrary location. By way of example, pretty much every (non-embedded) C implementation since 1972 supports recursion by writing an address of code to execute to memory. If you have semantics allowing a programmer to write to that location, then you can start executing literally any piece of code you want to. On some platforms like x86 you can execute starting in the middle of an instruction.

For example, "return" would stop meaning "execution resumes at the callsite of this function" and instead mean "execution resumes at the callsite of this function or maybe somewhere else if someone wrote to arr[-5]". The article makes a big point about the controversy of giving memcpy() the semantics of memmove(); how would you like the semantics "memmove() unless a concurrent thread is running, in which case anything might be written to dest and src might be overwritten."

Re: The Problem with Friendly C

#20
post #14
post #3

If ((uint32_t)x But why can't it just produce something different on each system? Allow me to call it as I see it: the modern interpretation of undefined behaviour is bullshit. What compilers do today should be the recourse of absolute last resort, and the sort of thing that makes its authors feel bad. But it seems to be treated as a matter of course. I don't know what to say. Mandatory reading: http://robertoconcert…

Apparently a (uint32_t) shifted by 32 is license to become completely insane. int main(int argc, char **argv) { uint32_t x = (uint32_t)0x12345678

I am almost LOLing at the segmentation fault. Where the fuck does a segmentation fault come from? It's shifting a value. Utterly mystifying.
Post reply on HN