Hacker News new | ask | show | jobs
by nikic 905 days ago
> People can (and do) point at the C spec for fault of this and it is true that if the C spec was more strict then these compilers would not have the free pass to do these crazy miscompilations. However there is nothing stopping these compilers from just not doing that, there is nothing stopping them from just defining their own sane behavior for what the C spec defines as undefined behavior. It is no longer the case where we have a dozen or so C compilers that people need to target with their programs, the spec is not the bottom line anymore.

Some compilers (like gcc and clang) do give you the option to make most undefined behaviors well-defined, using flags like -fwrapv, -fno-delete-null-pointer-checks, -fno-strict-aliasing, etc.

The key thing to understand is that if you use these flags, you opt-in to a non-standard C dialect. Going forward, you are no longer writing C code. Your code is now non-portable. Your code can no longer be compiled by a compiler that does not support these special C dialects.

Using these C dialects is a perfectly sensible thing to do, as long as you understand that this is what you are doing.

4 comments

> Your code can no longer be compiled by a compiler that does not support these special C dialects.

What compiler might that be?

It's true that MSVC doesn't have an equivalent of -fno-strict-aliasing, but that's because it just doesn't apply optimizations that assume strict aliasing in the first place. Admittedly the picture around signed overflow is more complicated, but it's essentially the same story.

> Using these C dialects is a perfectly sensible thing to do, as long as you understand that this is what you are doing.

I suppose that they technically are C dialects, but it seem more than a bit absurd to put it like that -- at least to me. By that standard the Linux kernel isn't written in C. And Firefox isn't written in C++. Postgres also wouldn't count as according-to-hoyle C under your definition. Even though Postgres compiles when -fwrapv and -fno-strict-aliasing are removed, and still passes all tests.

The implication of what you're saying seems to be that all of these open source projects each independently decided to "go there own way". I find it far more natural to explain the situation as one of GCC diverging when it decided to make -fstrict-aliasing the default around 15 years ago.

> It's true that MSVC doesn't have an equivalent of -fno-strict-aliasing, but that's because it just doesn't apply optimizations that assume strict aliasing in the first place. Admittedly the picture around signed overflow is more complicated, but it's essentially the same story.

Of course it's fine if the compiler doesn't implement this as an option, but rather as the default behavior -- as long as this is actually a documented guarantee, rather than "we just haven't implemented those optimizations yet, we might start exploiting this UB at any point in the future". I'm not familiar with what guarantees MSVC documents in this area.

> By that standard the Linux kernel isn't written in C.

I think that in a very real sense, it isn't. The kernel is written in standard C plus a few hundred GCC extensions, of which additional compiler options are but a small part. It took very extensive effort to make the kernel compile with clang (which already was mostly gcc-compatible, but nowhere near enough for the kernel's level of extension use).

> Even though Postgres compiles when -fwrapv and -fno-strict-aliasing are removed, and still passes all tests.

I think there is a lot of difference between "requires signed integer overflow to be well-defined because we explicitly rely on it" and "disable optimization based on signed integer overflow as a hardening measure, in case we got this wrong somewhere".

> The implication of what you're saying seems to be that all of these open source projects each independently decided to "go there own way".

You kind of make it sound like use of these options is common. To the best of my knowledge, this is not the case, and these projects are rare exceptions, not the rule. So, yes, they decided to go their own way (which is perfectly fine.)

>The key thing to understand is that if you use these flags, you opt-in to a non-standard C dialect.

This is not true. The C Standard explicitly states that a conforming implementation is welcome to provide well defined semantics to behavior that the standard states is undefined.

The whole point of undefined behavior is that the C Standard imposes no requirement on implementations about the semantics of that program, so if a specific implementation adds some kind of checks or provides some sort of deterministic behavior to something that is otherwise undefined, the program itself is still C code and adheres to the C Standard.

>Your code can no longer be compiled by a compiler that does not support these special C dialects.

Undefined behavior is a semantic property of a program, not a syntactic property, so any other conforming C compiler will have no problem compiling it.

For example Linux uses -fno-delete-null-pointer-checks
> The key thing to understand is that if you use these flags, you opt-in to a non-standard C dialect.

Isn't all C with undefined behavior non-standard. Or is there a standard for undefined behavior (obviously not, I would think)?

I don't understand why those flags wouldn't be turned on by default. Or do they affect more than just undefined behavior?

They affect performance, forcing the dev to insure they aren't doing unsafe/ill-defined things which is almost impossible on huge code bases in c/c++
They are unhappy being forced to write code that is free of undefined behavior?

It reminds me of the joke: Alice claims she is very fast at mental math. Bob asks "what's 4821 times 5997?" Alice replies "ten thousand." Bob says, "What, no, that's wrong, very wrong." Alice says, "But it was fast!"

Are you telling me C / C++ developers are like Alice? When given the choice between fast undefined behavior and slower but more likely to be correct undefined behavior, developers will choose the faster option that is more likely to be incorrect?

You seem to be assuming that on a large code base that you have top 1% programmers. I suppose that is the case for some places that are paying $500k average pay, but most businesses don't pay that and have average developers and a few top 5% if big enough. That's why tools that check for issues like linters and sanitizers help, especially if you make them part of "the process" for checkin