LWN.net Logo

Anatomy of a kernel vulnerability

The Linux kernel has seen a great deal of code auditing work. Even so, longstanding security issues turn up regularly. Consider, for example, the __scm_send() vulnerability recently disclosed by Paul Starzetz. This problem, present in the 2.6.9 kernel, is also present in 2.4; it has been there for some years.

This particular vulnerability hits the kernel socket API. Messages sent with the sendmsg() system call can have, embedded within them, control messages which can be used to transfer certain access rights to the recipient of the message. The control message header is defined as:

struct cmsghdr {
	__kernel_size_t	cmsg_len;	/* data byte count, including hdr */
        int		cmsg_level;	/* originating protocol */
        int		cmsg_type;	/* protocol-specific type */
};

These control messages are passed to __scm_send() for checking. One of the first things done with each control message is to look at the length of the message; the 2.6.9 code which performs this check looks like this:

if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
    (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
		    + cmsg->cmsg_len) > msg->msg_controllen)
	goto error;

The programmer who wrote this code probably thought that all the bases were covered; the control message length was verified to be at least the minimum necessary, but not so large as to overflow the space allocated for control messages in the structure read in from kernel space.

The problem is that the cmsg_len field is of type __kernel_size_t, which is an unsigned integer type. If a very large value is stored in cmsg_len, it will cause an overflow in this calculation:

	((char*)cmsg - (char*)msg->msg_control) + cmsg->cmsg_len)

When this overflow occurs, the resulting sum can be a small number, so cmsg_len does not appear to be overly large to this particular test. At a later point, however, that length will be added to a pointer into the list of control messages. Once again, the addition will cause an integer overflow, with the result that the pointer moves backward.

The exploit created by Mr. Starzetz works by creating a message with two embedded control messages. The second one sets cmsg_len to -12. That length gets translated to a very large unsigned number (0xfffffff4 on 32-bit systems); it happens to be just the right value to bump the pointer in __scm_send() backward in the list, where it encounters the same control message structure again. An infinite loops results.

Interestingly, this particular vulnerability seems to have been found by another researcher at about the same time. The fix was merged on December 8; the identification of the bug is credited to Georgi Guninski. It is, in any case, fixed, at least for 2.6.10. Some distributors have already made updated kernels available.


(Log in to post comments)

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