I have a thought. This is dangerous, I know. Here's my thought:
A programming language where optimizations are feedback. In other words, a programming language where optimizations are explicit, and opt-in. With the compiler (or even runtime feedback) suggesting optimizations. Not implementing them behind your back, but merely suggesting them.
Why? In most programming languages, there's an interesting dilemma. Mainly, that a) you don't know what is actually running, b) that you cannot rely on optimizations, and c) sometimes optimizations are in fact pessimizations.
So, I suggest a programming language where optimizations are explicitly opted into and out of (hierarchically, with most specific scope overriding) by annotations (or something similar), either as a requirement or a suggestion. (For something like TCO, for instance, you may wish to require it. Whereas you may wish to only suggest that a loop be unrolled.)
Does it mean more typing? Does it mean that it takes more time to code? Not really - if that's really a concern just enable everything as a suggestion for the entire project. But most of the time you shouldn't do that, or do it sparingly.
But the advantage of this is that everything is explicit. You know what the compiler is actually doing, as opposed to what you hope you are thinking that you are trying to get the compiler to do (or not do!).
You can actually tell the compiler that no, in fact, that variable that's about to be freed must be zeroed first. Or that unrolling that loop is something that should be done regardless of if it looks good on the surface.
(This was all spurred by me trying to figure out if it's in fact possible to securely zero an array in portable C, and coming to the conclusion that you cannot actually do so. The compiler can and will optimize out things. And even things that it doesn't optimize now, it is allowed to optimize out later.)
view the rest of the comments →
[–] NotSurvivingLife [S] 0 points 2 points 2 points (+2|-0) ago
You are wonderfully naive on that front. Compilers are evil, full stop.
(First off, slight confusion. Volatile only works if it is applied to the original array, where I am talking about "here's an array, can you securely memset it in portable C / C++". There is a distinct difference.)
And volatile doesn't work anyways, as the compiler is allowed to make copies of variables behind-the-scenes and not zero them out. The most obvious example of this is stack on some architectures, but there are other examples also.
And no, memset does not work. The compiler is allowed to - and will - optimize out a call to memset (or equivalent) if the array is not read from afterwards. And even if the array is read from afterwords the compiler will often just optimize out the memset and propagate through the value written directly.
And there are any number of optimizations - not just these ones - compilers make that are actively dangerous for security purposes. From the sounds of it you are talking about code that's not designed to be secure, which is all very well and good in and of itself.
The problem with C / C++ is that their memory models are a subset of the memory models of the underlying hardware.
[–] HentaiOjisan 0 points 1 point 1 point (+1|-0) ago
Hmm you picked my interest. And thanks for the explanation, I still have a lot to learn about compilers.
So in what situation would an array, that the compiler feels it's not going to be read, actually be read and needed to be zeroed? I can only think in having that array in a hard-coded address and being read from that address instead of the variable itself. And could you point out an example of a code that it's insecure because the compiler omits some part of the code if optimizations are active?
I'm not being sarcastic or anything like that, I really want to know what confuses the compiler in which situations. Could you link some kind of documentation about it? Damn, I'm kinda sad that I didn't choose computer engineering when I started in the university.
[–] NotSurvivingLife [S] 0 points 1 point 1 point (+1|-0) ago
For this particular bug?
a) when interfacing with external code that's not C-based, or
b) when combined with other bugs.
For example, suppose you have a bug similar to heartbleed, where you forget to check a length somewhere and accidentally read over the end of the array. If you make sure to always zero out important data (or better yet, replace it with a canary value), it's not the end of the world unless you are actively working with sensitive data when the read overrun occurs. But if the compiler optimizes out the memset to zero out important data, things can be leaked long after you've finished working with them.
Another example:
The Linux kernel compiles with certain optimizations disabled. In particular, it uses "-fno-delete-null-pointer-checks", which is exactly what it says on the tin. Why? Because of a couple times when code along the lines of the following:
Had the null check removed (because s was referenced, so the null check is redundant. Right? Except of course in kernel-land the deference will always work, so this is actually "safe" in practice.). This was a bug in the linux kernel, but is a bug that is virtually impossible to check for. And hence the Linux kernel just tells GCC to not optimize such null checks out. Full stop. And loses the potential benefits everywhere, because the potential problems in some cases is too much.
For another example of compilers and formally undefined behaviour, see here.
Again, these are all things that are formally bugs in the code being compiled. The compiler is adhering to the standard. But the problem is that trying to figure out what is and isn't undefined behavior is so complex that these sorts of mistakes are made all the time by the best of us. And these mistakes often have nasty consequences. And hence, it's the standard that's the problem.