Threat Level: green Handler on Duty: Jan Kopriva

SANS ISC: Internet Security | DShield SANS ISC InfoSec Forums

Participate: Learn more about our honeypot network

Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!
A new fascinating Linux kernel vulnerability

Source code for a exploit of a Linux kernel vulnerability has been posted by Brad Spengler (Brad is the author of grsecurity). I have to tell you right now – this was one of the most fascinating bugs I've read about lately.

Why is it so fascinating? Because a source code audit of the vulnerable code would never find this vulnerability (well, actually, it is possible but I assure you that almost everyone would miss it). However, when you add some other variables into the game, the whole landscape changes.

While technical details about this are a bit complex, generally what's happening can be easily explained. The vulnerable code is located in the net/tun implementation. Basically, what happens here is that the developer initialized a variable (sk in the code snippet below) to a certain value that can be NULL. The developer correctly checked the value of this new variable couple of lines later and, if it is 0 (NULL), he just returns back an error. The code looks like this:

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

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

This code looks perfectly ok, right? Well, it is, until the compiler takes this into its hands. While optimizing the code, the compiler will see that the variable has already been assigned and will actually remove the if block (the check if tun is NULL) completely from the resulting compiled code. In other words, the compiler will introduce the vulnerability to the binary code, which didn't exist in the source code. This will cause the kernel to try to read/write data from 0x00000000, which the attacker can map to userland – and this finally pwns the box. There are some other highly technical details here so you can check your favorite mailing list for details, or see a video with this exploit on YouTube at Brad was able to even bypass SELinux protections with this and LSM.

The fix for this is relatively easy, the check has to be done before assigning the value to the sk structure.
Fascinating research that again shows how security depends on every layer, and how even very expensive source code audit can result in missed vulnerabilities.


I will be teaching next: Web App Penetration Testing and Ethical Hacking - SANS Munich February 2022


401 Posts
ISC Handler
Jul 17th 2009
the video says this affects 2.6.30 and up, what about 2.6.29?
More info (and exploit) here:

5 Posts
In the snippet you provide, if tun is NULL, the initialization sk = tun->sk would cause a segfault (at least in userspace, maybe different in kernel), in which case the if (!tun) would never be reached if tun were NULL and therefore the optimizer could legitimately remove the block.

Perhaps the initialization was something more like sk = &tun->sk (which would initialize sk with an invalid pointer, but would not segfault)?

Or, if NULL dereference is not invalid in the kernel, perhaps the problem here is that the optimizer needs different settings for kernel code (this is arguably a compiler bug)?

3 Posts
Alex, this was copied directly from the kernel code so it worked like that for ages until identified by someone. Guess it could be a problem with the compiler -- I was more amazed to find out that someone actually managed to identify this vulnerability and exploit it.

401 Posts
ISC Handler
As Alex mentioned this looks more like a standard coding bug, initializing a variable before any test for NULL.
1 Posts
One extra line of context (assignment to tun) would make the situation clearer, see the patch at:
The problem is in drivers/net/tun.c un_chr_poll()
and appears to have been an oversight resulting
from a change in the way a tun_struct is managed.
These guys explain how kernel NULL pointers can be exploited:
Mr Spuratic

3 Posts
I think there are two aspects to this.

Whenever the 'tun' pointer was initialised, it should have been tested for NULL before using it (to avoid a NULL pointer dereference). That's the real origin of this bug and a common enough mistake. But it's very interesting that the compiler would optimize out the (late) test for NULL, which probably allowed this to go un-noticed for considerable time.

Anyway, this maybe wouldn't have happened in a C++ kernel...
Steven C.

171 Posts
@ joeblow

From the source of the exploit (referring to the commit that introduced the flaw):
Though it was committed before the release of the 2.6.29 kernel, it
did not (thankfully) make it into the 2.6.29 kernel. It first
appeared in 2.6.30.
"Brad was able to even bypass SELinux protections with this and LSM."
Yes, you tend to be able to do that when you're CPL0.

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

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

This code is faulty. It doesn't matter if you turn on optimization or not.

Sign Up for Free or Log In to start participating in the conversation!