New Linux Kernel Flaw Allows Null Pointer Exploits
Trailrunner7 writes "A new flaw in the latest release of the Linux kernel gives attackers the ability to exploit NULL pointer dereferences and bypass the protections of SELinux, AppArmor and the Linux Security Module. Brad Spengler discovered the vulnerability and found a reliable way to exploit it, giving him complete control of the remote machine. This is somewhat similar to the magic that Mark Dowd performed last year to exploit Adobe Flash. Threatpost.com reports: 'The vulnerability is in the 2.6.30 release of the Linux kernel, and in a message to the Daily Dave mailing list Spengler said that he was able to exploit the flaw, which at first glance seemed unexploitable. He said that he was able to defeat the protection against exploiting NULL pointer dereferences on systems running SELinux and those running typical Linux implementations.'"
If this had been Windows, the article would have been tagged defectivebydesign.
It's important to note that there is almost never any "preferred" or "special" release of Linux to use. And obviously this flaw doesn't affect people that don't use any security modules.
This is not good news, but it's important news. The kernel's not likely to have a "fixed" re-release for this version, although there probably will be patches for it as well. And when in doubt, just don't upgrade. Not very many machines can take advantage of all of the cool bleeding-edge features that come with each release, anyways. Lots of older versions get "adopted" by someone who will continue to maintain that single kernel release.
What's the value of information that you don't know?
Unless they're going to add a proper warning for the condition to gcc 'today' it won't, really.
Sure there are enough developers to go over the kernel to make sure such errors haven't been missed elsewhere, but all it takes is one to miss it and it's still there. Then there's all the other software compiled by gcc..
I'm not entirely sure how it can lead to an exploit (short of remapping page zero, which requires root privileges so doesn't really count) but since it has it's going to need a proper fix.
I think the compiler is correct. If tun is null, then tun->sk is undefined and the compiler can do what even optimization it want.
So when the compiler see tun->sk it can assume that tun is not null, and do the optimization, because IF tun is null, then the program is invoked undefined behavier, which the compiler don't have to preserve/handle. (How do you keep the semantic of an undefined program??)
They were writing nonsense. GCC makes use of the fact that in the C language any pointer that was dereferenced can't be NULL (this is made explicit in the standard). People use C as a high-level assembly where these assumptions don't hold. This is why code that doesn't assume this breaks. This issue came up a few months ago on the GCC lists, where an embedded developer pointed out that he regularly maps memory to the address 0x0, thereby running into issues with this assumption in the optimizers. The GCC developers introduced a command-line flag which tells the computer to not make that assumption, therefore allowing the compiler to be used even in environments where NULL pointers can be valid.
Now, the exploit uses this feature of the compiler (or the C language, if you will) to get the kernel into an unspecified state (which is then exploited) -- the NULL pointer check will be "correctly" optimized away. But in order to do this it first has to make sure that the pointer dereference preceding the NULL pointer check doesn't trap. This needs some mucking around with SELinux, namely one has to map memory to 0x0.
This is a beautiful exploit, which nicely demonstrates how complex interplay between parts can show unforeseen consequences. Linux fixes this by using the aforementioned new compiler option to not have the NULL pointer check optimized away.
No, dereferencing a NULL pointer results in undefined behaviour. The GCC compiler is generating code which follows the C standard in this case.
if (tun == null) then the behaviour becomes undefined as soon as the first line is passed.
if (tun != null) then the check is not needed.
Since "assuming tun is a non-NULL pointer" falls under the remit of "undefined" the compiler is acting reasonably in doing that.
"Undefined behaviour" includes anything that the program can possibly do, such as setting your printer on fire and emailing death threats to your grandmother. (Although it would be a malicious compiler which generates code that did that deliberately, it would still follow the standard!)
Therefore the compiler is allowed to assume that the C program guarantees that tun is not NULL at that point. Expecting any specific behaviour, such as the program halting, is outside the C standard. The compiler could optimize out the read to sk if sk is never used, for example, and that would be an entirely reasonable optimization.
The point is that GCC silently optimizes it away so the programmer has no idea that it's not even running the code they put in (however incorrect that code is). It's like saying "if there is an error in my code just remove that code and keep the rest without telling me".
This issue is a bit more complicated than you people are making it out to be.
For the most part, programmers DO WANT this kind of optimization, which is why they use an optimizing compiler. Things like dead-code elimination, constant propogation, and whole program optimizations are important to programmers.
If you don't want this stuff done, you don't reach for an optimizing compiler and then enable those optimizations. Its their purpose. If (something we know at compile time) should *always* be eliminated in a decent optimizing compiler.
Now, should GCC make assumptions in this specific case about the state of the pointer? Probably not. This isnt actualy a case of "something we know at compile time" so its a bug in the optimizer.
"His name was James Damore."
To me, the "if (!tun)" check should/must be before the de-reference; otherwise, it is meaningless! However, the compiler should print a warning in this case, not just optimize it away.
Over-the-top Response Guy! Giving "Over-the-Top Responses" since 1970.
Of course NULL is part of the C language, you blathering idiot, and it always has been. The level of ignorance here astounds me. Don't post about things you don't understand.
Quoting from C89: (not C99, C89, the one that's older than dirt.)
NULL wasn't even "added" in C89: NULL appears in the oldest, cruftiest UNIX code you can imagine. (That link is the original cat command from 1979.)
Sure it does - GCC knows at compile time that if the if() condition were true, we're already in the "undefined behavior" realm and all bets are off. So it gets rid of it. The code is broken: it's not the compiler's job to compile for the maximum defensiveness of the resulting machine code, otherwise we'd all be using bounds-checking compilers. If the compiler realizes that a certain runtime value will lead to undefined results (because the programmer chose to do so), it is free to break the execution as much as it wants in that case for code that runs afterwards. Essentially, undefined behavior is a contract signed by the programmer that says "I certify that this will never happen", which is why the compiler chose to perform this optimization.
Even though the real bug is clearly in the code, moving on to the realm of what's desirable from a compiler, I think it's clear that this behavior can make some problems worse (to the compiler, problems are binary - if there's a problem all bets are off - but not to us). This is fine in the name of optimization, but I think in this particular instance either a) kernel developers should opt to turn this optimization off, or b) (better) make GCC warn when this kind of optimization happens, because it's quite likely a bug.
In effect, the code is a form of broken defensive programming (you check after the fact whether you've screwed up). It's wrong, but we still wouldn't want the compiler to silently remove the check. So I think the ideal solution (besides fixing the code) is to add a warning to the compiler. NULL pointer dereferences are a bug in the vast majority of cases, and checking for a NULL pointer after dereferencing it (in such a way that the compiler recognizes it and is about to remove the check) is at best redundant and more likely a bug.
There's still the issue of the page 0 fuckery. If someone can make page 0 accesses not crash the kernel then that's also a bug - there are good reason why we want NULL and neal-NULL pointer accesses to always crash.
I think the compiler is correct. If tun is null, then tun->sk is undefined and the compiler can do what even optimization it want.
So when the compiler see tun->sk it can assume that tun is not null, and do the optimization, because IF tun is null, then the program is invoked undefined behavier, which the compiler don't have to preserve/handle. (How do you keep the semantic of an undefined program??)
The compiler is a complete asshole for deliberately optimizing a too late NULL check away instead of screaming "possibly dereferencing NULL" or something.
Arguably the compiler is wrong because it's (obviously) not actually impossible for address 0 to refer to valid memory however against convention and best practices that may be. The very existence of this problem proves that the compiler can NOT assume that tun is not null.
In this case, it is tun->sk, not &(tun->sk) which is being loaded, however the pointer arithmetic which generates the address happens first.
Well, there is no provision for this. The C standard says (6.5.2.3/4) that the value of tun->sk is that of the sk member of the object to which tun points - but since tun does not point to any object (it is a NULL pointer, remember?), then sk is a member of no object.
Whatever "pointer arithmetic" is going on behind the scenes, it is the implementation issue and outside of the C standard. Actually, the C standard does not even mention the this term anywhere near this paragraph. Your reasoning is correct from the compiler hacker point of view, but is outside of the spec's scope.
Submissions and patches to the kernel are independently tested and verified at least twice before being signed off and committed, usually by upstream developers (more experienced). This is the normal process. The only thing different in this case is that a vulnerability was exposed, hence it is in the news.
C|N>K
You, sir, have no clue what you're talking about. This has nothing to do with best practices, the code is just wrong. That is, unless you consider "you won't dereference a NULL pointer" a "best practice". The rest of us consider it a fundamental law of the universe.