News for hackers. Stuff that matters.
Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @12:16PM (#11497932)
I can see both sides of this. But looking at this argument:

> There are two ways to evaluate the the C specification's rightness and properness. [...] The second is to ask what is most useful. And there again the C committee have clearly failed.

Just two days on HN we saw this article: https://news.ycombinator.com/item?id=11468603 In which it says:

> no matter the kind of software, performance is almost always worse than our customers would like it to be.

That is why all of this is happening. There is a market demand for performance. Compilers that increasingly exploit UB in C is just a manifestation of that market demand.

Re:“Fiercely resist any further broadening of the scope of t... by jerf on Thursday April 14, 2016 @12:31PM (#11498097)
You don't really understand the UB problem if you model this in your head as some sort of simple set of rules that everybody really should have known all along, and it's not a big deal to fix the UB in your code. You have to make sure you're modeling it as A: a set of rules so arcane and byzantine that virtually nobody understands them, and to a first approximation, no non-trivial well-defined C program has ever been written and B: that this was a de facto change in C's nature... in a very real way, the language changed out from underneath people, without them expecting it.

You can't just shrug this away with "Well, if you want performance...", because people in fact don't want abstract "performance"... they want the language they were truly writing in to perform well, not for what is de facto a different dialect of the language to suddenly appear and replace the language they were using.

Re:“Fiercely resist any further broadening of the scope of t... by pcwalton on Thursday April 14, 2016 @12:59PM (#11498397)
> You can't just shrug this away with "Well, if you want performance...", because people in fact don't want abstract "performance"... they want the language they were truly writing in to perform well, not for what is de facto a different dialect of the language to suddenly appear and replace the language they were using.

Is wanting a for loop setting an array to zero to optimize into memset optimizing the language they were truly writing in? I think it is. But that optimization frequently depends on undefined behavior.

UB exploitation usually exists because people filed bugs on compilers complaining that they didn't optimize some case they expected to optimize.

Re:“Fiercely resist any further broadening of the scope of t... by nkurz on Thursday April 14, 2016 @01:46PM (#11498883)
Is wanting a for loop setting an array to zero to optimize into memset optimizing the language they were truly writing in? I think it is. But that optimization frequently depends on undefined behavior.

Yes, this is a good optimization, since it efficiently does what the programmer intended. The bad optimization is removing the security-essential loop altogether when the compiler notices that result appears unused, and sensitive information is left susceptible to later attack.

UB exploitation usually exists because people filed bugs on compilers complaining that they didn't optimize some case they expected to optimize.

I doubt that anyone has ever filed a bug saying "I explicitly wrote a loop to zero memory, but the compiler failed to optimize it out." If you know of one, please point to it. I think you are throwing out the baby (intentional C) with the bathwater (autogenerated C++).

Re:“Fiercely resist any further broadening of the scope of t... by pcwalton on Thursday April 14, 2016 @01:50PM (#11498931)
> I doubt that anyone has ever filed a bug saying "I explicitly wrote a loop to zero memory, but the compiler failed to optimize it out."

That optimization is a natural consequence of SROA and DCE. If you claim you don't want those optimizations, I don't know what to tell you. Those optimizations are some of the most basic, critical optimizations any modern C/C++ compiler does and throwing them away can easily result in at least a 2x performance loss.

Re:“Fiercely resist any further broadening of the scope of t... by cwzwarich on Thursday April 14, 2016 @03:37PM (#11499713)
The optimization actually has very little to do with SROA. In LLVM there is a special loop idiom recognition pass designed to detect memset / memcpy loops that works after ordinary SSA conversion.
Re:“Fiercely resist any further broadening of the scope of t... by kbenson on Thursday April 14, 2016 @02:05PM (#11499047)
> I doubt that anyone has ever filed a bug saying "I explicitly wrote a loop to zero memory, but the compiler failed to optimize it out."

I suspect it's less that they asked for that than they asked for some generic optimization on loops, and the implementation works great 99% of the time, and 100% of the test cases, but because it was approached from a performance perspective the idea that sometimes we want to change the state of the memory, even though it has no affect on a correctly functioning program, isn't considered. Zeroing memory in this way has nothing to do with correctly running programs, the only use is to defend against your program malfunctioning and exposing the data, or some external actor looking at memory. Neither of those have to do with the normal functionality of the code the compiler is running, but is is important. Unfortunately since it deals with things that have nothing to do with the actual instructions needed to make the program function, it's easy to overlook.

Re:“Fiercely resist any further broadening of the scope of t... by pcwalton on Thursday April 14, 2016 @02:09PM (#11499092)
> because it was approached from a performance perspective the idea that sometimes we want to change the state of the memory, even though it has no affect on a correctly functioning program, isn't considered.

A lot of dead stores happen because of post-inlined, post-constant-propagated (etc.) code. Nobody writes that code by hand; it occurs due to inlining and other aggressive types of optimizations. You need those optimizations for performance; IPO is a huge win.

It's like the old joke: "Who writes that kind of code? Macros do."

Re:“Fiercely resist any further broadening of the scope of t... by kbenson on Thursday April 14, 2016 @02:30PM (#11499249)
I understand, I'm not really taking a stance. The question, really, is should the compiler be responsible for making sure your unused bits and ends of memory that are provably irrelevant to the functioning of the program that is being compiled stay exactly as you left them? I think it's interesting because the situation is a bit untenable on both ends of the spectrum.

If we disallow all those optimizations, we end up with hugely wasteful programs in the current C ecosystem. Enforcing this is useless, the market will route around your best intentions, because no company that's making money will be willing to cede a huge performance gap to their competitors.

If we require code be annotated to bypass optimizations, we run afoul of the fact that it's impossible to know what optimizations might be developed in the future that will affect portions of our code that might be security sensitive.

The problem is insidious and deep. Imagine an architecture that allows specialized instructions to speed up zeroing chunks of memory, but does so by hardware mapping different pages around behind the scenes (to swap in a pre-zeroed page for a filled page). Correctly detecting that the architecture has an efficient method for zeroing large chunks of space could be detected and used by the compiler, but the hardware is working against us because the original memory may still be accessible to an external actor. C is ensuring the memory addresses you requested are zeroed, it's just not ensuring that the data that existed within them is no longer anywhere on the system.

Re:“Fiercely resist any further broadening of the scope of t... by Kristine1975 on Friday April 15, 2016 @01:41AM (#11502439)
The C standard already allows the compiler to remove calls to memset if the memory isn't used afterwards by the program (essentially dead-store elimination). C11 provides memset_s to clear memory for security reasons.
Re:“Fiercely resist any further broadening of the scope of t... by cwzwarich on Thursday April 14, 2016 @03:08PM (#11499507)
You can easily write a for loop that optimizes to memset without relying on undefined behavior. The only potential problem is that the loop bound evaluation could require a memory access, but this is simple to work around.
Re:“Fiercely resist any further broadening of the scope of t... by pcwalton on Thursday April 14, 2016 @03:17PM (#11499567)
Well, not if the compiler can't prove that the stores don't alias the induction variable. TBAA is the easiest and most reliable way to prove that, which is UB exploitation.
Re:“Fiercely resist any further broadening of the scope of t... by cwzwarich on Thursday April 14, 2016 @03:33PM (#11499680)
No, that is not the most reliable way to prove that property. The most reliable way is to ensure that the loop bound and induction variable are local variables that have not had their address taken.
Re:“Fiercely resist any further broadening of the scope of t... by pcwalton on Thursday April 14, 2016 @03:46PM (#11499774)
Well, yes, in the trivial case, but not if the induction variable and/or loop bound come in as references (as in templated code, etc).

There's also the issue of the store changing the array base, as in Chris Lattner's example on "What Every C Programmer Should Know".

Re:“Fiercely resist any further broadening of the scope of t... by cwzwarich on Thursday April 14, 2016 @05:30PM (#11500493)
You can always make the induction variable and loop bound local variables, and the induction variable is almost always a local variable anyways. It was the WebKit coding style for years to use local variables for loop bounds for this reason.

This is the example you're talking about in "What Every C Programmer Should Know":

  float *P;
  void zero_array() {
    int i;
    for (i = 0; i < 10000; ++i)
      P[i] = 0.0f;
  }
If you make the global variable static, then it gets converted to a memset with -fno-strict-aliasing, because the 'P' variable never has its address taken. If you used LTO with sufficient linkage information, the same thing would happen. Similarly, if you pass the array as an argument then the loop gets converted to a bzero call just fine without -fno-strict-aliasing:

  float *P;
  void zero_array_impl(float* a) {
    int i;
    for (i = 0; i < 10000; ++i)
      a[i] = 0.0f;
  }
  void zero_array() {
    zero_array_impl(P);
  }
I don't think this is a very good example for these reasons.
Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @12:41PM (#11498204)
> You don't really understand the UB problem if you model this in your head as some sort of simple set of rules that everybody really should have known all along

What does this have to do with my comment, at all?

> You can't just shrug this away with

Who is shrugging?

You don't understand a complex problem until you understand both sides. The article only presents one side. I am presenting the other. It doesn't mean I think the problem is simple or easy.

Re:“Fiercely resist any further broadening of the scope of t... by physguy1123 on Thursday April 14, 2016 @12:47PM (#11498273)
To add to this, a lot of what compilers decide is undefined isn't bizarre corner case code that rarely happens, but common and useful techniques that the compiler will happily demolish without warning. The 'defined' workarounds, however, are usually difficult, less safe, harder to understand, and often slower. This is without getting into the nightmare that is LTO finding 'undefined' behavior across program boundaries.
Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @12:53PM (#11498338)
> The 'defined' workarounds, however, are usually difficult, less safe, harder to understand, and often slower.

That has not been my experience. Do you have an example of this?

Re:“Fiercely resist any further broadening of the scope of t... by physguy1123 on Thursday April 14, 2016 @01:49PM (#11498916)
Sure - all sorts of stuff with type-punning in unions because much harder to understand when it's transformed into a whole series of casts to/from void/char , with the loss of checking that one gets from a union. Even without unions, aliasing rules can screw you over in surprising ways. Look at the free list example linked in the post for another one.

Slower - the various methods of checking for integer overflow are slower than x = a + b; if (a < x) {/overflow*/}. It can also be easier to just use a slower, or more memory hungry way of doing things than a better but needlessly complicated method.

At my current job we've sacrificed memory (and in this case, performance as a consequence) because the complexity cost of keeping some critical code in the defined behavior realm was too risky.

Re:“Fiercely resist any further broadening of the scope of t... by Kristine1975 on Thursday April 14, 2016 @02:11PM (#11499108)
Type punning in unions is best done using memcpy. Compilers know what it does and optimize it: http://blog.regehr.org/archives/959

As for integer overflow at least gcc and clang have built-in functions that generate optimal code by checking the CPU's overflow flag: http://clang.llvm.org/docs/LanguageExtensions.html#checked-a... and https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins...

Re:“Fiercely resist any further broadening of the scope of t... by colin_mccabe on Friday April 15, 2016 @12:12AM (#11502079)
C99 Techincal Corrigendum 3 clarifies that union-based type punning is valid by amending 6.5.2.3 with this footnote:

If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called “type punning”). This might be a trap representation.

(copied from a comment on blog.regehr.org)

This is actually a really common area of misunderstanding. But ultimately, one of the main uses of unions is to provide multiple ways to access the same data.

Re:“Fiercely resist any further broadening of the scope of t... by physguy1123 on Monday April 18, 2016 @10:51AM (#11520333)
In C, yes, type-punning with union is valid. However, in C++, it is considered undefined behavior and may get removed by the compiler if it sees fit.
Re:“Fiercely resist any further broadening of the scope of t... by colin_mccabe on Monday April 25, 2016 @12:39AM (#11562533)
Thanks for the clarification. Do the new C++ standards explicitly discuss this anywhere?
Re:“Fiercely resist any further broadening of the scope of t... by physguy1123 on Thursday April 28, 2016 @04:50PM (#11591802)
There's this stackoverflow qanswer: http://stackoverflow.com/questions/11373203/accessing-inacti...

where answers try to distill the standard into something legible by humans. Unsurprisingly, they come to different conclusions.

The overall interpretation in some situations, if you look at things just right, you can do type punning in C++. But it's pretty risky since you're a LOC away from the compiler deleting your function.

Or, you can't really do it at all safely. Which I would conclude from the above anyways.

Re:“Fiercely resist any further broadening of the scope of t... by AlexandrB on Thursday April 14, 2016 @01:43PM (#11498853)
There were examples in the linked message board post [1]. Note how there is now a paragraph describing why the code is written with 4 casts instead of comparing pointers naively. Multiply this by hundreds of instances of similar code in a large code base.

[1] http://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=tools/libx...

Re:“Fiercely resist any further broadening of the scope of t... by Kristine1975 on Friday April 15, 2016 @01:42AM (#11502442)
Why not just write a function is_ptr_less that does all the casts.

P.S: In C++ std::less is guaranteed to do the right thing(tm).

Re:“Fiercely resist any further broadening of the scope of t... by ktRolster on Thursday April 14, 2016 @12:35PM (#11498144)
because people in fact don't want abstract "performance"

That's true, if performance were the only consideration, we would all be writing assembly.

Re:“Fiercely resist any further broadening of the scope of t... by jibsen on Thursday April 14, 2016 @04:38PM (#11500151)
To a large degree the language didn't change (some of the undefined behaviours we are discussing were in C89), rather the compiler vendors started to enforce parts of it.
Re:“Fiercely resist any further broadening of the scope of t... by mwcampbell on Thursday April 14, 2016 @12:28PM (#11498060)
But why do we always want more, more, more? If gcc -O2 without whole-program optimization was good enough to encode MP3 faster than real-time on a 366 MHz Pentium laptop in 1999, then surely it's good enough now, with all the increases in processor performance since then. If that level of compiler optimization isn't good enough now, then maybe that means we programmers are doing something wrong. At some point, better optimization in a compiler probably isn't worth what you give up to get it, in terms of reliability and comprehensibility.

Edit: All the responses are right. Feel free to downvote this ill-considered comment into oblivion, now that I can't take it back.

Re:“Fiercely resist any further broadening of the scope of t... by chrisseaton on Thursday April 14, 2016 @12:42PM (#11498218)
> If gcc -O2 without whole-program optimization was good enough to encode MP3 faster than real-time on a 366 MHz Pentium laptop in 1999, then surely it's good enough now

This is patently a ludicrous argument. How can the speed of encoding an MP3 be used as a proxy for countless other applications of computers?

Programs that are slower than they need to be waste real energy and other industrial resources.

Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @12:36PM (#11498169)
People in 1999 weren't running huge neural nets and beating the world champion in Go.

There are millions of computers in data centers around the world. They are working on problems that someone thought it was worthwhile enough to pay for. If C compilers emit code that is slower, that means buying and maintaining a lot more computers, using more energy, producing more greenhouse gas, etc.

Re:“Fiercely resist any further broadening of the scope of t... by dragontamer on Thursday April 14, 2016 @12:41PM (#11498212)
> People in 1999 weren't running huge neural nets and beating the world champion in Go.

Which was mostly GPU code, so it was either CUDA or OpenCL btw. Not actually C.

People do need to realize that the fastest, massively parallel supercomputers are in fact written in CUDA / OpenCL. And the most powerful supercomputers are mostly being used to display 3d-models of a sword-wielder fighting a guy with guns.

Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @12:45PM (#11498246)
Fair enough. But some very significant fraction of the hardware and energy spent on data centers today is on CPUs.
Re:“Fiercely resist any further broadening of the scope of t... by colin_mccabe on Friday April 15, 2016 @12:19AM (#11502116)
I don't think this is an "ill-considered comment" at all.

Daniel J. Bernstein made the same comment in his slides about "the slow death of the optimizing compiler"[ https://cr.yp.to/talks/2015.04.16/slides-djb-20150416-a4.pdf ]. Basically, a lot of programs are I/O bound. Those that aren't are often dominated by a tiny amount of super-hot spots that can be optimized by hand via assembly language. Most code is "freezing cold" -- it's hardly worth optimizing. C compilers are giving up safety and getting very little in return, except shiny new compiler benchmarks.

Re:“Fiercely resist any further broadening of the scope of t... by pcwalton on Thursday April 14, 2016 @12:57PM (#11498373)
Yes, you don't need the performance for listening to MP3s. MP3 decoding is nowhere near the outer limit of CPU performance you rely on every day.

Just to stay within the domain of codecs, YouTube would not be a nice experience on a 366 MHz Pentium laptop with primitive compiler optimizations.

Re:“Fiercely resist any further broadening of the scope of t... by michaelt on Thursday April 14, 2016 @12:23PM (#11498005)

  There is a market demand for performance.
You could fulfil that demand without breaking existing code by making the riskier optimisations configurable and turned off by default.
Re:“Fiercely resist any further broadening of the scope of t... by webkike on Thursday April 14, 2016 @12:33PM (#11498123)
They are usually. Most people don't change the -03 in their build files after a compiler update though.
Re:“Fiercely resist any further broadening of the scope of t... by ktRolster on Thursday April 14, 2016 @12:40PM (#11498193)
-O4 exists for 'risky' optimizations.

-O3 is for optimizations that are mostly not risky.

-O2 is for optimizations that don't involve a speed/size tradeoff

-O1 is for optimizations that don't slow your compiler down too much

Obviously these are fuzzy categories, but -O4 is where "undefined behavior" optimizations belong. Alternately, each individual optimization has its own flag, and can be turned on individually.

Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @12:51PM (#11498313)
I've never heard of -O4 for "risky" optimizations, and I can't find any reference to this. Do you have one?
Re:“Fiercely resist any further broadening of the scope of t... by ktRolster on Thursday April 14, 2016 @12:56PM (#11498365)
If you search for "gcc O4" you'll find some references (it's tricky because if you search for -O4 Google will search for everything without O4).

Currently, there are no optimizations that fit in that category, but as recently as 2003 I remember seeing it in the man pages. I believe there was -O5 and -O6 too, but I don't trust my memory enough to be certain.

Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @01:00PM (#11498412)
The results for that query all seem to say that "gcc -O4" doesn't exist. There is one proposal from Chris Lattner for adding this to Clang, but it's not clear that this ever happened.
Re:“Fiercely resist any further broadening of the scope of t... by ktRolster on Thursday April 14, 2016 @02:30PM (#11499247)
Currently gcc uses -Ofast for 'dangerous' optimizations.

Maybe I remembered the history wrong. There are definitely cases of people using -O4 http://www.larcenists.org/Twobit/KVW/kvwbenchmarks.html .

There are definitely optimization options which are (or have been) available that are not turned on by -O3. For example:

-ffast-math

-funroll-all-loops

-fstrict-aliasing

Are not turned on (for good reasons: unrolling loops often slows a program down).

So there's no reason UB optimizations need to be turned on by default, or with -O3. You could throw them in -Ofast or use -Oub, or use optimization specific flags (they should have optimization specific flags in any case) and that was my main point.

Re:“Fiercely resist any further broadening of the scope of t... by haberman on Thursday April 14, 2016 @04:02PM (#11499884)
-Ofast turns on optimizations that can break standard-conforming programs!

That is a very different thing that turning on optimizations that might break non-standard-conforming programs!

I think the idea of making it controlled by optimization flags is interesting, but you would need to formalize what the more "safe" variant of the language is if people are actually going to depend on it.

Re:“Fiercely resist any further broadening of the scope of t... by ktRolster on Thursday April 14, 2016 @06:15PM (#11500757)
That is a very different thing that turning on optimizations that might break non-standard-conforming programs!

You're talking as though the standard were well-defined. It's not.

Re:“Fiercely resist any further broadening of the scope of t... by dumael on Thursday April 14, 2016 @06:35PM (#11500832)
One option in GCC I'm aware of is -faggressive-loop-optimizations . Quoting the manual "This option tells the loop optimizer to use language constraints to derive bounds for the number of iterations of a loop. This assumes that loop code does not invoke undefined behavior by for example causing signed integer overflows or out-of-bound array accesses. The bounds for the number of iterations of a loop are used to guide loop unrolling and peeling and loop exit test optimizations. This option is enabled by default. "

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

Re:“Fiercely resist any further broadening of the scope of t... by ArkyBeagle on Thursday April 14, 2016 @01:18PM (#11498617)
You have to work pretty hard to get code to a state such that compiler optimizations will save you.

I've had this same argument with cache management specialists. If you have to consider how the cache works for your product to work, you're doing it wrong.

Re:“Fiercely resist any further broadening of the scope of t... by GFK_of_xmaspast on Thursday April 14, 2016 @04:48PM (#11500230)
> You have to work pretty hard to get code to a state such that compiler optimizations will save you

At least in c++, this is absolutely 100% not the case, there's a huge difference between -O0 and -O2.

Re:“Fiercely resist any further broadening of the scope of t... by ArkyBeagle on Thursday April 14, 2016 @07:13PM (#11500989)
I need to check that out for myself.
Re:“Fiercely resist any further broadening of the scope of t... by dumael on Thursday April 14, 2016 @06:37PM (#11500837)
I've dealt with bugs that existed at -O0, -O2 was fine. But the bug was in the compiler itself.
Re:“Fiercely resist any further broadening of the scope of t... by ArkyBeagle on Thursday April 14, 2016 @07:12PM (#11500986)
Absolutely.