Slashdot Mirror


Overeager Compilers Can Open Security Holes In Your Code

jfruh writes: "Creators of compilers are in an arms race to improve performance. But according to a presentation at this week's annual USENIX conference, those performance boosts can undermine your code's security. For instance, a compiler might find a subroutine that checks a huge bound of memory beyond what's allocated to the program, decide it's an error, and eliminate it from the compiled machine code — even though it's a necessary defense against buffer overflow attacks."

29 of 199 comments (clear)

  1. old news from decades ago by iggymanz · · Score: 4, Insightful

    well known for decades that optimizing compilers can produce bugs, security holes, code that doesn't work at all, etc.

    1. Re:old news from decades ago by NoNonAlphaCharsHere · · Score: 5, Insightful

      That's why I always use a pessimizing compiler.

    2. Re:old news from decades ago by KiloByte · · Score: 5, Insightful

      Or rather, that optimizing compilers can expose bugs in buggy code that weren't revealed by naive translation.

      --
      The creatures outside looked from Alt-Right to Antifa; but already it was impossible to say which was which.
    3. Re:old news from decades ago by Marillion · · Score: 4, Insightful

      Right. The other part of the issue is why didn't anyone write a test to verify that the buffer overflow detection code actually detects when you overflow buffers?

      --
      This is a boring sig
    4. Re:old news from decades ago by AuMatar · · Score: 3, Insightful

      Because it worked in debug mode (which generally has optimizations off)?
      Because it was tested on a compiler without this bug? The people writing the memory library is usually not the people writing the app that uses it.
      Similarly, it was tested on the same compiler, but with different compiler flags?
      Because that optimization didn't exist in the version of the compiler it was tested on?
      Because the test app had some code that made the compiler decide not to apply the optimzation?
      Life is messy. Testing doesn't catch everything.

      --
      I still have more fans than freaks. WTF is wrong with you people?
    5. Re:old news from decades ago by blackwizard · · Score: 2

      I'm going with old news from decades ago.

    6. Re:old news from decades ago by itzly · · Score: 3, Interesting

      That's an example of a programmer not understanding the rules of a conforming C/C++ compiler. It should be fixed in the source, not in the compiler.

    7. Re:old news from decades ago by tepples · · Score: 2

      Perhaps the problem is that standard C isn't expressive enough to express some operations that some programs require, especially with respect to detection of integer arithmetic overflows.

    8. Re:old news from decades ago by K.+S.+Kyosuke · · Score: 3, Interesting

      I'd personally rather work in languages that are safe by default with optional (but available) extra performance/lower safety where explicitly instructed (the way Common Lisp does it, for example), rather than the other way around. I've come to the impression that most codebases would have fewer overrides in the former case rather than the latter. If you think the latter is preferable, what about all those bugs and security vulnerabilities we got "thanks" to that approach? Was it actually worth it?

      --
      Ezekiel 23:20
    9. Re:old news from decades ago by russotto · · Score: 2

      Perhaps the problem is that standard C isn't expressive enough to express some operations that some programs require, especially with respect to detection of integer arithmetic overflows.

      Indeed; the compiler's even allowed to assume signed integer overflow doesn't happen, which is where you get into trouble. Yet we have this perfectly good mechanism for detecting integer overflow (condition codes) and no way to reach them from high level languages (C isn't unique in this respect)

    10. Re:old news from decades ago by lgw · · Score: 2

      If you're testing only in debug mode, you're doing it wrong. Do your customers run debug? No? Then all your tests must run retail or you fail.

      Is this a compiler bug? Doubtful. Chances are it's code that isn't standard that was getting away with its non-standard behavior until the compiler started enforcing this bit.

      Compiler flags? Again, test the binaries your users will run.

      Testing can catch a lot, if not half-assed.

      --
      Socialism: a lie told by totalitarians and believed by fools.
    11. Re:old news from decades ago by gweihir · · Score: 2

      That would be, you know, sane? And competent? Cannot have that in somebody that does software...

      There are also nice compiler directives that are used to switch off optimization for some functions.

      --
      Most ACs are not even worth the keystrokes to insult them. Be generically insulted by this and ignored otherwise.
  2. Unsable Code, again by Anonymous Coward · · Score: 5, Informative

    This is just as poorly written up as last time. These are truly bugs in the programs using undefined parts of the language. It's silly to blame the compiler.

    1. Re:Unsable Code, again by david_thornley · · Score: 2

      Actually, that makes sense.

      Suppose we want to check for buffer overflows in C++, so we allocate a stretch of memory beyond the buffer, zero it out, and check it to see if it's zeros later. Depending on how the buffer is used, it may be that a buffer overflow would require undefined behavior, such as accessing memory beyond the limits of a data structure. An optimizing compiler might figure that out. In event of undefined behavior, anything the compiler does is conforming, and in event of no undefined behavior the stretch of memory will remain zeros, so in either case the check for zeros is unnecessary for a program, and the compiler can remove it by the "as-if" rule.

      --
      "When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
    2. Re:Unsable Code, again by lgw · · Score: 2

      Compilers remove vastly more code than you seem to think, when optimizing. They always have - this isn't new. You wouldn't want all that spam as warnings, for sure (if you build with warnings as error, as one ought to do, you'd never build anything).

      --
      Socialism: a lie told by totalitarians and believed by fools.
  3. UNISEX conference by Mdk754 · · Score: 5, Funny
    Wow, you know you're ready to go home when it's Friday afternoon and you read:

    But according to a presentation at this week's annual UNISEX conference

  4. Complete nonsense.... by Anonymous Coward · · Score: 3, Insightful

    Any code removal by the compiler can be prevented by correctly
    coding the code with volatile (in C) or its equivalent.

    1. Re:Complete nonsense.... by Anonymous Coward · · Score: 2, Informative

      Except not, so now we have explicit_bzero()

    2. Re:Complete nonsense.... by Threni · · Score: 2

      ---
      I can't offhand think of many situations where I could say with any degree of certainty that if something read or wrote to memory externally that it wouldn't matter, and it would rarely be the best use of my time to try an establish it... so really... mark everything volatile all the time.

      Clearly THAT isn't right.
      ---

      Yeah, that would be a poor design. You use volatile when you need to. That's the rule. You just need to work out when you need to.

      > If you do not expect the memory to be written to or read from, you don't mark it
      > volatile.

      > So now you are saying, well, if you REALLY don't expect the memory to written
      > to or read from externally, then you should mark it volatile.

      I'm not sure you typed that in right, or maybe you don't understand what volatile means or when to use it.

    3. Re:Complete nonsense.... by ultranova · · Score: 2

      If I don't mark it volatile then the sanity check gets optimized away, and the software is vulnerable.

      Except it's just as vulnerable either way. Your sanity check never executes even if x and y are volatile, since the buffer overrun in somestuff() already transferred control to pwned() when somestuff() tried to return. All your sanity check does is give you a false sense of security.

      Yes, yes, the real problem is in somestuff() where the buffer got overrun in the first place, but that's someone else's code or whatever.

      Yes, and that problem is not solvable in C, no matter how many sanity checks you litter your code with. As soon as you execute any code from an untrusted source, you don't have any security. Any error puts the system into an undefined state, at which point all bets are off.

      --

      Forget magic. Any technology distinguishable from divine power is insufficiently advanced.

  5. Bad summary is bad by werepants · · Score: 4, Informative

    This is not really about the existence of bad compiler optimization - it is about a tool called Stack that can be used to detect this, which is known as "unstable" code, and has been used to find lots of vulnerabilities already.

  6. Old news by Anonymous Coward · · Score: 4, Informative

    I know that at least GCC will get rid of overflow checks if they rely on checking the value after overflow (without any warning), because C defines that overflow on signed integers is undefined. This is even documented. If anything is declared by the language specification as being undefined, expect trouble.

  7. Misleading summary, slashdot by Anonymous Coward · · Score: 2, Insightful

    The kinds of checks that compilers eliminate are ones which are incorrectly implemented (depend on undefined behavior) or happen too late (after the undefined behavior already was triggered). The actual article is reasonable— it's about a tool to help detect errors in programs that suffer here. The compilers are not problematic.

  8. Floating point algorithms too by Anonymous Coward · · Score: 2, Informative

    Compilers can also "optimize" away Kahan summation algorithm. See page 6 of How Futile are Mindless Assessments of Roundoff in Floating-Point Computation

  9. Functionally correct, but insecure by Smerta · · Score: 5, Insightful

    The classic example of a compiler interfering with intention, opening security holes, is failure to wipe memory.

    On a typical embedded system - if there is such a thing (no virtual memory, no paging, no L3 cache, no "secure memory" or vault or whatnot) - you might declare some local (stack-based) storage for plaintext, keys, etc. Then you do your business in the routine, and you return.

    The problem is that even though the stack frame has been "destroyed" upon return, the contents of the stack frame are still in memory, they're just not easily accessible. But any college freshman studying computer architecture knows how to get to this memory.

    So the routine is modified to wipe the local variables (e.g. array of uint8_t holding a key or whatever...) The problem is that the compiler is smart, and sees that no one reads back from the array after the wiping, so it decides that the observable behavior won't be affected if the wiping operation is elided.

    My making these local variables volatile, the compiler will not optimize away the wiping operations.

    The point is simply that there are plenty of ways code can be completely "correct" from a functional perspective, but nonetheless terribly insecure. And often the same source code, compiled with different optimization options, has different vulnerabilities.

  10. Re:Simple. by Desler · · Score: 3, Insightful

    C became popular because it was vastly more portable and performant than its predecessors. It still is today. None of those "better" languages that came before it or after it can beat that. And yes, extreme portability does matter when you have 100s of millions if not billions of devices that can't run anything but assembly or C. It's why the people saying that OpenSSL should be written in Java or C# are morons. Care to tell me how that's going to run on a, for example, Linksys WRT54G with only 8 or 16 MB of RAM, 2 to 4 MB of Flash storage and a 125 to 240 mhz MIPS CPU? Yeah, it's not.

  11. Re:Simple. by Desler · · Score: 2

    The problem is that most programmers have never had to get their hands dirty doing embedded work. They live in a bubble that ignores all the memory/storage/processing-power constrained devices all around them. OpenSSL, for example, as used in something like DD-WRT would be unusable if it was written in anything but C or possibly C++.

  12. Re:Simple. by Desler · · Score: 3, Insightful

    Well I'd be pretty pissed as well if my pet language was relegated to the graveyard of obscurity by a language that was usable for real work. Dennis Ritchie was a pragmatist who got shit done not some guy wanking over the greatness and purity of the language he created. People to this day are still jealous of that.

  13. Re:Simple. by lgw · · Score: 2

    More than that, string and vector are just as fast as the vulnerable C alternatives as long as you pre-allocate the buffers to the expected size. It's not rocket science, but the number of times I've heard "but C++ is too slow, we have to use C" makes me cry. With bounds checking in both (hand-written in C, of course), the speed is the same. Without buffer checking in either, the speed is the same.

    --
    Socialism: a lie told by totalitarians and believed by fools.