Looking at a few recent kernel security holes
Buffer overflows and more
CVE-2015-5156 is, at its core, a buffer overflow in the virtio network driver subsystem. This driver sets the NETIF_F_FRAGLIST flag on its devices, indicating that it can handle packets that have been split into multiple fragments. When it gets an actual packet, it calls skb_to_sgvec() to turn that list of fragments into a scatter/gather I/O list. Unfortunately, the size of the scatterlist array it allocates for the fragment list is insufficient; in some circumstances, there can be more fragments than can fit into the scatter/gather list. The result is that skb_to_sgvec() writes beyond the end of the list, corrupting a random range of memory.
The problem was "fixed" by removing the NETIF_F_FRAGLIST flag. As a minimal fix for stable kernels, this change probably makes sense. But one could argue that fixing it properly would involve either (1) sizing the scatterlist array properly in virtio, or, better, (2) passing the length of the list to skb_to_sgvec() so it cannot be overrun. Without that latter fix, skb_to_sgvec() behaves much like strcpy(), and this type of overrun could easily happen again.
CVE-2015-2925 is a vulnerability that allows a process to escape from a mount namespace if it can create a bind mount within that namespace. In practice, it means that processes can get out of a container and access the entire host-system filesystem. It was reported in April and was the subject of a long series of discussions. The proposed fixes were complex (to say the least) and ran into some opposition. In the end, a simpler fix was merged for 4.3.
This bug came about because nobody thought about the effects that a rename() call outside of a bind mount might have on processes whose current working directory lies within that mount. In short, a process following ".." out of a directory is normally stopped at the root of the filesystem it is in, but, if a directory can be moved out of a bind mount, a process within that directory can move up without ever encountering that root; it will thus never be stopped. Intersections of security domains will often be fraught with this kind of problem. The issue is fixed, but it is hard to believe that there won't be others like it.
CVE-2015-5257 is a null pointer dereference in the USB WhiteHEAT driver. These bugs can be used to cause a kernel oops; in some cases they can be exploited for privilege escalation, though most distributions should be configured to defeat such exploits. The source of the problem here is clear: the driver trusted the hardware to behave as expected. If somebody shows up with a purpose-built USB device, they can trigger the bug.
This particular vulnerability has few people worried. But the vulnerability of the kernel to malicious hardware in general is worrisome indeed. Such hardware is increasingly easy to make, and it can often create conditions that developers have never thought about or tested for. We will almost certainly see more vulnerabilities of this nature.
Initialization failures of various types
CVE-2015-7613
is a failure to properly initialize memory. In particular, the user and
group IDs associated with a System V shared-memory object are not set before
the object is exposed to the wider world, meaning that authentication
checks can be done against random data. At a minimum, this bug can be
exploited to gain access to shared-memory segments that should be
inaccessible. But, as the Red Hat bugzilla
entry notes: "It is almost certain that this vulnerability can be
used to gain arbitrary code execution in the kernel.
"
The good news here is that, in KASan, we
have a tool that can detect use of uninitialized memory in the kernel.
Indeed, it was KASan that flagged this particular problem. The not-so-good
news is that, as Linus Torvalds noted in the
changelog to the fix, this problem had already been found and fixed in
the System V semaphore code (for 3.18). It would have been good to fix all
three types of
System V IPC (message queues are vulnerable too), but, as Linus notes, "we
clearly forgot about msg and shm
". The lessons seem clear: tools
are invaluable, but, as Al Viro once said:
"Bugs are like mushrooms - found one, look around for more.
"
Initialization-related race conditions are fairly common; another example can be seen in CVE-2015-5283. In a modular system, the module for the SCTP network protocol will not be loaded until a user requests an SCTP socket. The initialization code in the SCTP module registers its installed protocols before it is fully initialized; that opens a window within which another process can attempt to open sockets while the module is in a half-baked state. Good things rarely come from such situations.
Almost any kernel module, be it a driver, a network protocol, or something else, must generally initialize a long list of resources and make them available to the rest of the system. It is easy to create a situation where some resources become visible before the module is fully prepared to manage them. An interrupt handler may be registered before the data structures the handler needs are ready. A sysfs file could show up before the driver is ready. Or an SCTP protocol can appear before the module is ready to handle it. These problems manifest themselves as difficult-to-find race conditions; they are hard to test for. So they will probably continue to pop up.
CVE-2015-5697 is an information-leak vulnerability. The MD (RAID) system implements an ioctl() operation called GET_BITMAP_FILE, which returns the name of the external bitmap file associated with a specific device. Should that device not actually have an external bitmap file, though, the ioctl() will copy 4096 bytes of uninitialized kernel memory to user space after having set just the first byte to zero. The remaining 4095 bytes could contain pretty much anything. An attacker could scan this data for specific patterns and possibly obtain kernel addresses or private data.
The fix is straightforward enough: allocate the space for the file name with kzalloc() instead of kmalloc(). But, once again, this is an easy sort of error to make; it is hard to ensure that all data copied to user space is initialized in all paths through the code. There has been a push over the years to use functions like kzalloc() everywhere, but there is resistance to doing so, especially in hot-path code where the developer is certain that the memory will be properly initialized. In any case, the GET_BITMAP_FILE ioctl() is not one of those hot paths, so there is no reason not to be sure in this case.
These examples were all taken from vulnerabilities that were fixed in
distributor updates over the last month or so. Needless to say, it is not
an exhaustive list. But it does show a few of the numerous ways in which
security-related bugs can be introduced into the kernel. Kernel
programming requires great care, an extreme distrust of the environment in
which the code is running, and, whenever possible, good testing tools. The
kernel community has gotten better with all of these over the years, but
there is clearly a lot of ground to be covered still.
Index entries for this article | |
---|---|
Kernel | Security/Vulnerabilities |
Security | Linux kernel |
Posted Oct 22, 2015 4:29 UTC (Thu)
by jamesmorris (subscriber, #82698)
[Link] (4 responses)
Kees and I will be talking about this next week at the kernel summit.
Posted Oct 22, 2015 12:09 UTC (Thu)
by pabs (subscriber, #43278)
[Link] (1 responses)
Posted Oct 23, 2015 0:42 UTC (Fri)
by jamesmorris (subscriber, #82698)
[Link]
Posted Oct 23, 2015 16:13 UTC (Fri)
by Aissen (subscriber, #59976)
[Link]
Posted Oct 30, 2015 1:23 UTC (Fri)
by pabs (subscriber, #43278)
[Link]
Posted Oct 22, 2015 10:02 UTC (Thu)
by jezuch (subscriber, #52988)
[Link]
Great quote, especially when talking in context of fertile ground like the C language ;) That's something I learned along the way. Nowadays when I see something wrong that should be improved, I in fact see a pattern and often fire up a semantic patching utility[1] to find more instances of it.
[1] For Java there's one in NetBeans, and this is the only thing I use NetBeans for. It's a little neglected, though.
Posted Oct 22, 2015 12:49 UTC (Thu)
by spender (guest, #23067)
[Link]
-Brad
Posted Oct 22, 2015 14:48 UTC (Thu)
by error27 (subscriber, #8346)
[Link]
I thought I would look at how the number of CVEs this year compares with previous years. I used the Ubuntu CVE tracker (https://launchpad.net/ubuntu-cve-tracker) and grepped for "kernel" and counted the CVEs for each year.
2005 112
We still have 20% of the year left so it's likely that we'll hit 90 something bugs before the year is over. Maybe more if we get a lot of KASan bugs, I suppose. I was thinking there would be a trend in those numbers but there really isn't... My gut says there is a increased focus on security these days so you would expect the number of CVEs to go up.
Posted Oct 23, 2015 14:09 UTC (Fri)
by SLi (subscriber, #53131)
[Link] (7 responses)
I know what Linus' opinion of C++ is, but perhaps it would be possible to sneak the good C++ features into C one by one and trick Linus into thinking it's not C++?
Posted Oct 24, 2015 11:33 UTC (Sat)
by JdGordy (subscriber, #70103)
[Link]
Posted Oct 27, 2015 22:04 UTC (Tue)
by Seegras (guest, #20463)
[Link] (5 responses)
But actually, there is something like C which does have features that makes the code really more secure. https://www.rust-lang.org/ Probably not useful for kernels, but certainly for everything else low-level.
Posted Oct 28, 2015 17:12 UTC (Wed)
by smckay (guest, #103253)
[Link] (4 responses)
P.S. To anyone who feels like saying that safe kernels don't have to be slow: I would very much appreciate a demonstration. For serious, please point me to the project website or Github so I can read more.
Posted Oct 28, 2015 18:52 UTC (Wed)
by madscientist (subscriber, #16861)
[Link] (3 responses)
I further see no reason why, even though clearly some unsafe parts are required, this would have to be all or even a significant part of the code.
Posted Oct 28, 2015 19:23 UTC (Wed)
by smckay (guest, #103253)
[Link] (2 responses)
Posted Oct 28, 2015 23:58 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link] (1 responses)
Posted Oct 30, 2015 4:50 UTC (Fri)
by mathstuf (subscriber, #69389)
[Link]
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
2006 90
2007 80
2008 85
2009 118
2010 163
2011 161
2012 96
2013 190
2014 154
2015 72
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Of course since no one has done it yet we can't know for sure. But the conceit behind Rust is that the safety checking is a compile-time feature. So you get that safety at no cost to the runtime. Of course, some data structures (refcounted things etc.) have runtime overhead but presumably similar types of refcounting, etc. would be needed in a C program. I see no reason that the Rust implementations must be slower than the C ones.
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes
Looking at a few recent kernel security holes