Anatomy of a kernel vulnerability
[Posted December 15, 2004 by corbet]
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)