8 ms·
Security flaws caused by compiler optimizations
- bigtrakzapzap 7y agoBasically, every kernel needs an explicit_bzero() system call because it's very difficult to assure data flow properties of de/initialization without something the compiler cannot optimize away.
- rogueresearch 7y agomemset_s() was added to C11 for this.
- jmgao 7y agomemset_s was added to C11 in an optional annex, and my understanding is that there are zero platforms that actually implement it. (Microsoft implemented an early draft of Annex K that doesn't actually include memset_s.)
- valleyer 7y agoIt's present on Mac OS X.
- rurban 7y agoMost libc's added an insecure version of memset_s, doing only the above discussed compiler-barrier, but not a memory-barrier, which is needed for Spectre, broken HW. The default memset should do the compiler-barrier. But unfortunately you cannot talk with libc maintainers about security. Too much arrogance. Thanks to this Redhat article for supporting the user-base on this. You can use my safeclib, which implements the Annex K extensions.
- atq2119 7y agoThe leaking of sensitive data due to DCE'd memset is an interesting one. Generally, compilers are free to temporarily move data around to a lot of places, such as on the stack for register spilling. Is there any programming language at all which allows sensitive data to be annotated in such a way that the compiler will promise not to leak it to memory indefinitely in some sense? (E.g. all places that the data may be written to are cleaned up before reaching some sort of security boundary)
- nokcha 7y ago>Is there any programming language at all which allows sensitive data to be annotated in such a way that the compiler will promise not to leak it to memory indefinitely in some sense? Somewhat related: there's a recent paper that develops a language with syntax for marking data as secret; the compiler then goes even further and avoids timing side-channel leaks: Cauligi, Sunjay, et al. "FaCT: A flexible, constant-time programming language." 2017 IEEE Cybersecurity Development (SecDev). IEEE, 2017. http://www.sysnet.ucsd.edu/~bjohanne/assets/papers/2017secdev.pdf http://www.sysnet.ucsd.edu/~bjohanne/assets/papers/2017secde...
- couchand 7y agoPerhaps Rust, using Pin? I'm not entirely clear on the guarantees made there, perhaps an expert could weigh in on if that's a viable option?
- asveikau 7y agoI remember when CVE-2009-1897 was current, because it is an obvious example where no one would expect the null check to work: struct sock *sk = tun->sk; unsigned int mask = 0; if (!tun) Obviously "sk" is not used until after the null check, but if we read it line by line as we would expect a naive compiler with no optimization to act, the pointer is followed before the null check, and null should produce a crashing program. It would seem that anyone expecting it to work would assume the evaluation of the initial assignment of sk to be lazy at first use, which is a very strange assumption. Still, I remember in 2009 people writing about that snippet as if it is a surprising result and the compiler did something wrong.
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- tom_ 7y agoWhen NULL corresponds to a valid address, you don't want the check stripped out of this sort of code.
- jfim 7y agoOut of curiosity, what are the expected contents of the zero page area? Is access allowed to it just because it's coming from the kernel instead of a userland process?
- Someone 7y agoThat is platform dependent, and doesn’t matter. NULL need not be the ‘all zeroes’ bit pattern (1), and even if it did, the C standard says dereferencing a NULL pointer leads to undefined behavior (https://en.wikipedia.org/wiki/Null_pointer#Null_dereferencing https://en.wikipedia.org/wiki/Null_pointer#Null_dereferencin...) (1) Recent C standards have peddled back a bit on ‘it should be possible to write a confirming C compiler for every CPU ever made’ (for example, IIRC, by fixing a char to be 8 bits), so that might be a thing of the past.
- deleted 7y ago[deleted]
- qw3rty01 7y agoA better title would be security flaws caused by relying on undefined behavior.
- ummonk 7y agoGiven that practically anything under the sun is undefined behavior for C and C++, that isn’t saying much.
- papermachete 7y agoWhat gives you this imimpression?
- bhk 7y agoThe C standard includes an appendix that lists ~200 examples of undefined behavior. This list does not claim to be exhaustive. Often, what constitutes undefined behavior is non-obvious (and not well justified). For example, when adding two signed integers results in an overflow, it is undefined behavior even if your program never uses the result. Due to C's definition of "undefined" behavior, it means that all of the guarantees we rely on to ensure security go out the window whenever the programmer steps on one of these land mines.
- caspper69 7y agoNot all UB falls into this category. A lot of UB, such as your signed integer addition example, is dependent upon the behavior of the underlying hardware. Certain archs may throw an exception on signed integer overflow, or exhibit otherwise inconsistent behavior, for example. The standard is the standard, of course, but not all implementations inherit the UB of the standard.
- bhk 7y agoWhether or not something is "undefined behavior" has nothing to do with the hardware. The C specification says what is specified, what is "unspecified", what is "implementation defined", and what is "undefined". If something is "undefined" according to C, you can't rely on what the hardware does, because the hardware might not even get a chance to do anything. The compiler may completely elide sections of your program -- and they do in practice (for example, bounds checks). Actually, hardware always does something reasonable for add instructions (throw exception or overflow or saturate). It's additions in C that can have unreasonable results.
- comex 7y ago> However, the programmer failed to inform the compiler that the call to ereport(ERROR,...) does not return. This implies that the division will always execute. I don’t think that’s correct. The compiler is allowed to assume that functions marked noreturn do not return, but it’s not allowed to assume that functions not marked noreturn do return. In other words, it’s not undefined behavior for a function to call abort(), enter an infinite loop, etc. instead of returning. It would be very strange if it were! There’s a somewhat related spec clause that lets the compiler assume that certain types of loops will eventually terminate [1], but that doesn’t apply here. Therefore I think the mentioned compiler optimization is illegal. The issue was reported back in 2011; it would be interesting to see whether newer versions of GCC, or Clang, behave the same way. [1] https://stackoverflow.com/questions/16436237/is-while1-undefined-behavior-in-c https://stackoverflow.com/questions/16436237/is-while1-undef...
- dooglius 7y agoThat seems to be the conclusion of the mailing list thread as well, there was an initial bad gcc bug report that didn't understand the problem, seemingly no follow-up with a proper bug report.
- slaymaker1907 7y agoAn infinite loop with no side effects is undefined behavior.
- comex 7y agoI didn't say no side effects. But even an infinite loop with no side effects is well-defined if the controlling expression is a constant expression; see the link in my previous post.
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- mrich 7y agoMy take, after 12 years of industry C++ experience, working on code that needs to be fast: Too much emphasis is placed on gaining another 0.5% performance improvement, instead of slightly slower code that does what was intended. At least offer some safe defaults and make the bleeding edge optional. While we are debugging things like this, people are writing servers in JavaScript and web services in Python :O No need to optimize so heavily, they will waste it anyway :)
- tetha 7y agoI've spend 3 years building a java code base with a class of requests having an average response time of < 1 ms. And the entire application had a 99%q response time of < 10ms. Including GC and everything. Quite honestly, after a more years of experience: Cache smart and batch-query. Network latency, aka lightspeed in fiber or coppper is our enemy. Not a JVM GC'ing in a controllble way. If the CPU cache is your issue, you can either correct me, or you're abusing the network without realizing it.
- nitrogen 7y agoSome systems will have stricter latency requirements than that -- microseconds, always, no exceptions (e.g. studio audio, network packet processing, industrial controls). Others will have maximizing throughput as a goal (e.g. x264). In both cases CPU cache could be a bottleneck and GC would be the enemy.
- Gibbon1 7y agoI've mostly written C for embedded. Which sometimes needs to be fast. I agree with you. It definitely feels like as compiler writers continue to gleefully add more footguns to C/C++. Application programmers vote with their feet by using slower or much slower interpreted languages like Java, C#, JavaScript, Python Ruby, and PHP. Meanwhile system programmers are eyeing golang and rust for infrastructure.
- adrianN 7y agoThat 0.5% improvement can help a lot when it's inside the Javascript engine or the Python interpreter.