xorl %eax, %eax

CVE-2009-1385: Linux kernel E1000 Integer Underflow

with one comment

This vulnerability was reported by e1000’s ChangeLog on April 2007 as:

* fix panic on changing MTU under stress

And it was fixed in the Linux kernel by Neil Horman on 2 June 2009. Here is the vulnerable routine as seen in 2.6.29 release of the Linux kernel:

Intel PRO/1000 Linux driver
Copyright(c) 1999 – 2006 Intel Corporation.

/**
* e1000_clean_rx_irq – Send received data up the network stack; legacy
* @adapter: board private structure
**/
static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
struct e1000_rx_ring *rx_ring,
int *work_done, int work_to_do)
{

u32 length;

status = rx_desc->status;

if (unlikely(!(status & E1000_RXD_STAT_EOP))) {
/* All receives must fit into a single buffer */
E1000_DBG(“%s: Receive packet consumed multiple”
” buffers\n”, netdev->name);
/* recycle */
buffer_info->skb = skb;
goto next_desc;
}

/* adjust length to remove Ethernet CRC, this must be
* done after the TBI_ACCEPT workaround above */
length -= 4;

/* probably a little skewed due to removing CRC */
total_rx_bytes += length;

if (length < copybreak) { ... /* else just continue with the old one */ } /* end copybreak code */ skb_put(skb, length); ... return cleaned; } [/sourcecode] This code can be found at drivers/net/e1000/e1000_main.c and the above function is used to check the received data and if they are valid, send them to the kernel's network stack. The first if condition shown in the above snippet is responsible for finding any instances of E1000_RXD_STAT_EOP bit in each rx buffer. This constant represents the End-Of-Packet but since Linux kernel supports multiple rx buffers an attacker is able to send data in multiple rx buffers, assuming that this is done with two rx buffers as Neil Horman said, the first will be discarded since it doesn't contain the End-Of-Packet bit but the second one will be accepted since it will be the last one and thus contains the EOP bit. If the latter buffer is less than, or equal to 4 bytes long, it will result in an integer underflow since the subtraction to remove the Ethernet CRC will wrap around. The next range check will fail since length will be definitely larger than copybreak, and this will result in a skb_put() call to add "length" bytes of data into skbuff's buffer. To fix this, they applied the following patch: [sourcecode language="c"] length = le16_to_cpu(rx_desc->length);

– if (unlikely(!(status & E1000_RXD_STAT_EOP))) {
+ /* !EOP means multiple descriptors were used to store a single
+ * packet, also make sure the frame isn’t just CRC only */
+ if (unlikely(!(status & E1000_RXD_STAT_EOP) || (length <= 4))) { /* All receives must fit into a single buffer */ [/sourcecode] The simply added a check for length less than 5 to avoid the underflow.

Written by xorl

June 10, 2009 at 12:05

Posted in bugs, linux

One Response

Subscribe to comments with RSS.

  1. Just another proof that innocent ChangeLog entries may hide serious vulnerabilities. This one looks quite exploitable :-)

    Cheers
    ./hk

    huku

    June 21, 2009 at 02:00


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s