Slashdot Mirror


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.'"

28 of 391 comments (clear)

  1. DRM is defective by design. by BPPG · · Score: 4, Informative

    I think that tag is mostly reserved for DRM related news...

    And I have seen news about linux DRM modules also tagged that.

    --
    What's the value of information that you don't know?
  2. Re:Double standards by Anonymous Coward · · Score: 4, Informative

    Thats because with Windows, no one would be able to marvel at how un-obvious the flaw is. According to The Register, the kernel actually has gaurds in place against just this type of valnerability, but the complier optimized them out during compiling. IMHO this makes this flaw a very good case study, even with security in place, you cannot really trust the compiler. (actually, this flaw apparently only occurs if security is in place... or if you use PulseAudio (in which case, you deserve it!)).

  3. Wait, what? by TheRaven64 · · Score: 4, Interesting
    This code looks right?

    struct sock *sk = tun->sk; // initialize sk with tun->sk
    ...
    if (!tun)
    return POLLERR; // if tun is NULL return error

    So, he's dereferencing tun, and then checking if tun was NULL? Looks like the compiler is performing an incorrect optimisation if it's removing the test, but it's still horribly bad style. This ought to be crashing at the sk = tun->sk line, because the structure is smaller than a page, and page 0 is mapped no-access (I assume Linux does this; it's been standard practice in most operating systems for a couple of decades to protect against NULL-pointer dereferencing). Technically, however, the C standard allows tun->sk to be a valid address, so removing the test is a semantically-invalid optimisation. In practice, it's safe for any structure smaller than a page, because the code should crash before reaching the test.

    So, we have bad code in Linux and bad code in GCC, combining to make this a true GNU/Linux vulnerability.

    --
    I am TheRaven on Soylent News
    1. Re:Wait, what? by TheSunborn · · Score: 4, Insightful

      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??)

    2. Re:Wait, what? by pdh11 · · Score: 5, Interesting

      Technically, however, the C standard allows tun->sk to be a valid address, so removing the test is a semantically-invalid optimisation.

      No. Technically, if tun is null, dereferencing it in the expression tun->sk invokes undefined behaviour -- not implementation-defined behaviour. It is perfectly valid to remove the test, because no strictly conforming code could tell the difference -- the game is already over once you've dereferenced a null pointer. This is a kernel bug (and not even, as Brad Spengler appears to be claiming, a new class of kernel bug); it's not a GCC bug.

      But as other posters have said, it would indeed be a good security feature for GCC to warn when it does this.

      Peter

    3. Re:Wait, what? by johnw · · Score: 4, Informative

      First, NULL is a preprocessor construct, not a language construct; by the time it gets to the compiler the preprocessor has replaced it with a magic constant[1].

      Which must be either "0" or "(void *) 0".

      The standard requires that it be defined as some value that may not be dereferenced, which is typically 0 (but doesn't have to be

      Not true - the standard requires NULL to be defined as one of the two values given above.

      and isn't on some mainframes

      There are indeed some platforms where a null pointer is not an all-bits-zero value, but this is achieved by compiler magic behind the scenes. It is still created by assigning the constant value 0 to a pointer, and can be checked for by comparing a pointer with a constant 0.

    4. Re:Wait, what? by Anonymous Coward · · Score: 5, Informative

      In this case, it is tun->sk, not &(tun->sk) which is being loaded, however the pointer arithmetic which generates the address happens first. If tun is NULL then this is NULL + {the offset of sk}. While dereferencing NULL is explicitly not permitted, pointer arithmetic on NULL is permitted, and dereferencing any non-NULL memory address is permitted.

      Raven, I've seen you make the same comment a few times in this story. Please stop pushing this nonsense.

      The language standard calls * and -> operations "dereferencing". The way it works is that tun->sk dereferences the whole struct, then hands you the sk field from it.

      When you implement this in your compiler you do an address computation first then load only the field because you don't want to load the whole struct when you don't need to, but that's an implementation detail. The compiler is required to act as if the pointer tun were being dereferenced.

      It would be a major missed optimization bug if the compiler didn't eliminate the later if (!tun) operation. This is a case where the input code is simply wrong.

  4. Re:Double standards by infolation · · Score: 5, Funny

    This language is called Pedantry. A pedant pedantically peddles english into pedanticism.

  5. Re:Serious bug in gcc? by Tony+Hoyle · · Score: 4, Informative

    gcc is definitely doing the wrong thing here.

    Given the code:
    a = foo->bar
    if(foo) something()

    gcc is doing precisely the wrong thing - optimising out the if on the theory that the app would have crashed if it was null.

    What it *should* do is throw a warning (even an error, given the clear intent of the code) pointing out that the variable is dereferensed before it is tested.

    This kind of error being missed by gcc is going to affect a *lot* of code - it's really not that uncommon a coding error, and is easy to do.

  6. Re:I always disable those by 140Mandak262Jamuna · · Score: 4, Funny

    They create vulnerabilities by allowing remote code to overload error handlers and thus pwn your system?

    --
    sed -e 's/Chuck Norris/Rajnikant/g' joke > fact
  7. Actually, it's already been fixed by inode_buddha · · Score: 5, Informative

    Actually, it's already been fixed as of 2.6.31-rc3. Interestingly enough, the code by itself was fine until gcc tries to re-assign the pointer value upon compiling. Steven J. Vaughn-Nichols had a decent write-up about it in Computerworld.

    --
    C|N>K
    1. Re:Actually, it's already been fixed by inode_buddha · · Score: 4, Insightful

      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
  8. Re:Serious bug in gcc? by Bananenrepublik · · Score: 5, Insightful

    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.

  9. Re:Double standards by mortonda · · Score: 5, Informative

    This is arguably more of an issue in the compiler than in the kernel,

    Not completely... from the SANS Storm Center, the code was as follows:


    struct sock *sk = tun->sk; // initialize sk with tun->sk

    if (!tun)
            return POLLERR; // if tun is NULL return error

    The error was that the compiler optimized away the if statement, assuming that tun had already been initialized. The check should have been placed before the sock variable referenced it. Not entirely obvious maybe, but then again, it should have been checked before the assignment.

  10. Re:Double standards by alnjmshntr · · Score: 4, Funny

    Right... Because Microsoft are really losing sleep over the negative comments posted on slashdot, so they have assembled a crack team of slashdotters to game the moderation system in their favour.

    You have to be kidding me.

    --
    If I had created the world I wouldn't have messed about with butterflies and daffodils. I would have started with lasers
  11. Re:Serious bug in gcc? by TheRaven64 · · Score: 5, Interesting
    Except that his explanation is wrong. Dereferencing NULL is illegal, but pointer arithmetic on NULL is legal (and, even if it were illegal, would be practically impossible for a compiler to check). This statement is not dereferencing NULL, it is (potentially) performing pointer arithmetic on NULL (adding the offset of the sk field to it) and then dereferencing the result. This may or may not be valid, depending on how NULL is defined (it doesn't have to be 0 in C, although it usually is), what the offset of sk is, and what the memory layout of the target platform is.

    On most modern platforms, NULL is defined as (void*)0 and the entire bottom page of memory is mapped as no-access. On some embedded systems, however, the bottom few hundred bytes are used for I/O and you get the addresses of these by adding a value to 0. On these systems it is perfectly valid (and correct) C to define a structure which has the layout of the attached devices and then cast 0 to a pointer to this structure and use that for I/O.

    --
    I am TheRaven on Soylent News
  12. Re:Double standards by Shinobi · · Score: 4, Interesting

    Sure. My last entire project has been specifically about that. Been working on a piece of software to go onto an embedded device with deterministic behaviour, with the hardware specs being 32kiBiByte RAM, no cache, 8MHz processor.

    Most people I am forced to work with who have a comp sci degree are unable to work under such conditions. On the other hand, EE's and comp.eng graduates tend to be very nice to work with on such projects.

  13. I really don't see how this is a compiler problem? by gbutler69 · · Score: 4, Insightful

    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.
  14. Re:Linus, you Rookie !! by luca · · Score: 4, Informative

    Ok, I know I shouldn't be feeding the troll, but read the article: the kernel source itself is perfectly fine, is the compiler that optimizes the check away.

  15. Re:Double standards by QuoteMstr · · Score: 4, Informative

    No. You are wrong.

    The code is grabbing the value of the sk field of the tun struct, not its address. Did you misread the code, or do you not actually know C? Or are you perhaps just on the sauce?

    You're claiming the code reads struct sock **sk = &tun->sk when in reality, it reads struct sock* sk = tun->sk, which is completely different.

  16. Re:Serious bug in gcc? by QuoteMstr · · Score: 4, Insightful

    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.)

    4.1.5 Common definitions The following types and macros are defined in the standard header . Some are also defined in other headers, as noted in their respective sections.... NULL which expands to an implementation-defined null pointer constant ... A.6.3.13 Library functions * The null pointer constant to which the macro NULL expands ($4.1.5).

    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.)

  17. Re:Serious bug in gcc? by marcansoft · · Score: 5, Insightful

    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.

  18. Re:Double standards by kestasjk · · Score: 4, Insightful

    So you can disassemble compiled code, way to go.. Have fun disassembling a huge binary that's far too large to economically analyze in assembly.

    What's that? You don't fully disassemble and analyze large binaries but only critical paths or small binaries? How unique and sought-after your services must be. I'm sure analysis of compiled kernels is the best way to tackle this bug..

    --
    // MD_Update(&m,buf,j);
  19. code found in Linux 2.6.30 by pikine · · Score: 4, Informative

    Oh, found the code on lxr. It looks like Linux kernels up to 2.6.29.6 are NOT affected, and this is a vulnerability introduced in 2.6.30 due to a fairly significant rewrite of tun.c. Linux 2.6.30 was released in Jun 9, 2009, just a month ago. Funny the tun.c rewrite was not mentioned in the set of changes for 2.6.30.

    I think this example actually shows a forte of Linux as open source. New vulnerability is found very quickly after "new" code is released.

    --
    I once had a signature.
  20. the set of changes for 2.6.30 by pikine · · Score: 4, Informative

    For some reason I didn't link this correctly. The set of changes for 2.6.30 is found http://kernelnewbies.org/Linux_2_6_30.

    --
    I once had a signature.
  21. Re:Linus, you Rookie !! by gnasher719 · · Score: 5, Informative

    Ok, I know I shouldn't be feeding the troll, but read the article: the kernel source itself is perfectly fine, is the compiler that optimizes the check away.

    Absolutely not. The code itself has a severe bug: If tun is a null pointer then it invokes undefined behaviour. Undefined behaviour means anything can happen. Anything can happen means a severe bug, especially in kernel code. The optimizing compiler just turned C source code that was buggy, but not obviously enough for the programmer, into assembler code that would have been obviously buggy to anyone. Most definitely not the fault of the compiler.

  22. Re:Just don't use that version by mvdwege · · Score: 4, Informative

    I'm very sorry, but you are wrong.

    There is no longer an unstable/stable kernel branch difference. Essentially all new kernels are development versions. It is specifically up to the distribution vendors to pick stable kernels out of this continuous release stream.

    Mart

    --
    "I know I will be modded down for this": where's the option '-1, Asking for it'?
  23. Re:Linus, you Rookie !! by lilo_booter · · Score: 4, Informative

    Umm - no - the *code* does the undefined behaviour and *then* checks if the undefined behaviour could happen. But, heck, mistakes happen - it was identified and fixed. Not much of a story really.