LWN.net Logo

Another null pointer exploit

By Jonathan Corbet
November 4, 2009
Back in mid-October, Earl Chew reported a null pointer crash in the kernel pipe code. Initial response to his report was somewhat slow, partly because the kernel he was running was based on 2.6.21. Earl took the time to dig through the code and identify the problem, though; it turns out to be an old vulnerability which is still present in current kernels.

What it comes down to is that there is a race condition in the pipe code. Prior to 2.6.32-rc6, the code which opens a pipe (for write-only access, in this case) looks like:

    static int
    pipe_write_open(struct inode *inode, struct file *filp)
    {
	mutex_lock(&inode->i_mutex);
	inode->i_pipe->writers++;
	mutex_unlock(&inode->i_mutex);

	return 0;
    }

The problem is that if the final close of this pipe slips in at the wrong time, inode->i_pipe may have been set to null. So this is yet another null pointer vulnerability; the rest is just a matter of writing the exploit. That exploit must face the challenge that the window of opportunity is quite short, but computers are very good at continually trying things until something works.

The fix makes the code much more careful about checking the current status of the pipe and refusing new opens if the final close has already happened. Distributors are shipping updates.

This particular bug is attracting attention because it is in the core kernel and (relatively) straightforward to trigger. But it is far from unique. A quick look at commits since 2.6.31 turns up no fewer than 34 which explicitly fix null pointer dereference bugs. Quite a few more fix things that could be null pointer bugs, and there's no telling how many more were fixed without an explicit mention in the commit title. Null pointer bugs are common, and are likely to remain so for quite some time.

What is surprising about this bug is that some distributions are still vulnerable to it. We have had the ability to keep null pointer bugs from being exploitable for some time, but certain distributions - generally of the "enterprise" variety - disable that protection by default. Sites running such distributions might want to be sure that they have the vm.mmap_min_addr knob set to a reasonable value; either that or expect to be vulnerable to more null pointer exploits in the future.


(Log in to post comments)

The story restarts again and again

Posted Nov 5, 2009 0:58 UTC (Thu) by haypo (guest, #42675) [Link]

All exploits for the last famous local kernel vulnerabilities (vmsplice(),
tun_chr_pool(), perf_counter) use NULL pointer dereference. vmsplice() bug
was found in february 2008. And the story restarts again and again. Why
not setting /proc/sys/vm/mmap_min_addr *default value* to non zero value
in Debian Stable
or RedHat? Ubuntu, Debian Sid and Fedora all use non zero value. I read
that mmap_min_addr causes issue with Qemu, dosemu and WINE (only old
versions of Qemu and dosemu, and it was still possible to use them as
root). But I think that most people prefer to be protected against
root exploits than being able to use Qemu, dosemu, ... without any manual
configuration.

About the new pipe bug/vulnerability, the funniest is that Brad Spengler
is still able to disable SELinux on RedHat 5.4! See this screenshot:
http://grsecurity.net/~spender/rhel54.png But disabling SELinux should be
a game for the author of grsecurity ;-)

The story restarts again and again

Posted Nov 5, 2009 2:59 UTC (Thu) by spender (subscriber, #23067) [Link]

vmsplice on 64bit kernels wasn't a NULL ptr deref (see http://lwn.net/Articles/271874/). perf_counter wasn't either (I released 2 versions of it -- one that used a NULL deref and one that didn't)

From reading slashdot comments I was surprised how many people were posted with mmap_min_addr set to 0, and seeming surprised or not understanding why it had been turned off. It was also worrisome to see people just check the value and determine that that meant they were protected -- which is not necessarily the case, particularly for RHEL. I don't know what kind of standards are in place for this, but it seems like a user-visible warning should be given (like the ones I see on Debian) if a user is installing something that will force mmap_min_addr off. Some people reported that it was turned off, but all their apps worked fine -- could the request to disable the feature be moved to the application for when it's actually used?

Anyway, I wouldn't try to over-analyze the threatscape of the kernel based purely on exploits I release publicly.

FYI, the exploit was released tonight with a video and an OpenBSD history lesson.
http://grsecurity.net/~spender/enlightenment.tgz

-Brad

The story restarts again and again

Posted Nov 5, 2009 3:01 UTC (Thu) by spender (subscriber, #23067) [Link]

All their apps worked fine with it turned back on, I meant to say.

The story restarts again and again

Posted Nov 5, 2009 19:20 UTC (Thu) by nybble41 (subscriber, #55106) [Link]

All of this is just patching the symptoms. The kernel should be secure against NULL pointer exploits regardless of the value of mmap_min_addr.

This is a case of security being badly compromised for the sake of performance. If the hardware doesn't support limiting supervisor access to the NULL page while allowing user accesses, and the overhead of updating the page tables when moving between user and supervisor mode is too high, then the page(s) should be permanently unmapped with user-space accesses emulated in the page-fault handler. That would allow (much slower) access to these infrequently-used pages without kernel security compromises.

The story restarts again and again

Posted Nov 5, 2009 13:42 UTC (Thu) by BenHutchings (subscriber, #37955) [Link]

There's a conflict between stability and security here. Debian stable probably will be updated to raise the default mmap_min_addr, but we need to clearly announce the change so that users that need DOS/Win16 compatibility know that they need to override it.

The story restarts again and again

Posted Nov 5, 2009 13:58 UTC (Thu) by paravoid (subscriber, #32869) [Link]

This has already happened. Have a look at DSA 1915-1, dated 23/10/2009.

Another null pointer exploit

Posted Nov 5, 2009 10:16 UTC (Thu) by eteo (guest, #36711) [Link]

Updates to correct this vulnerability were released earlier this week for Red Hat Enterprise Linux 3, 4, 5, and Red Hat Enterprise MRG. See https://www.redhat.com/security/data/cve/CVE-2009-3547.html. We have also written a knowledgebase article on how to mitigate against future NULL pointer dereference vulnerabilities on Red Hat Enterprise Linux. You can find out more about this at: http://kbase.redhat.com/faq/docs/DOC-20536. Thanks.

How is this more than a DoS?

Posted Nov 5, 2009 11:26 UTC (Thu) by quotemstr (subscriber, #45331) [Link]

I don't understand how this problem is anything more than a possible DoS. The Wunderbar exploit worked because the kernel jumped to an address it obtained from the 0 page.

This case is different. If i_pipe is NULL, the kernel just increments a word at offsetof(struct pipe_inode_info, writers) in memory, a location scarcely above memory location 0. That increment can't touch any kernel memory.

Now if page 0 isn't mapped, the kernel will try to update that memory location and panic. But if page 0 is mapped, nothing will happen.

What am I missing?

How is this more than a DoS?

Posted Nov 5, 2009 13:20 UTC (Thu) by spender (subscriber, #23067) [Link]

What you're missing is that the pipe_open function shown above isn't the area of code that directly makes the vulnerability exploitable for arbitrary code execution. Your analysis of that particular function is correct, but the pipe_open function is only needed to get a file descriptor that pipe_read, pipe_write, or others can then be used on. It's these functions that make the vulnerability exploitable.

Take a look at the exploit linked to above; I've commented it sufficiently that you should be able to see exactly how the vulnerability is exploited for privilege escalation.

For 2.6.10+ kernels, the attacker by correctly filling out the pipe_inode_info struct can pass through some checks and cause the kernel to make use of an attacker-supplied pointer to an array of function pointers -- the values of which are also supplied by the attacker (to the code that compromises the kernel). For 2.4 and 2.6.9 and below, the array of function pointers doesn't exist, but an attacker-controlled pointer that determines the location in the kernel to be written to by the pipe will allow for overwriting of a function pointer in the kernel and then subsequently arbitrary code execution.

-Brad

Copyright © 2009, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds