xorl %eax, %eax

Linux kernel RTL8169 Remote Memory Corruption

with 4 comments

This bug was initially reported by Michael Tokarev on 8 January 2009 and finally patched on 8 June 2009. This driver (RTL8169) is provided in the Linux kernel for support of RealTek 8169/8168/8101 ethernet NICs and its code can be found under drivers/net/r8169.c. Here is the buggy code as seen in 2.6.29 release of the Linux kernel.

static void rtl_hw_start_8169(struct net_device *dev)
{
 struct rtl8169_private *tp = netdev_priv(dev);
 void __iomem *ioaddr = tp->mmio_addr;
 struct pci_dev *pdev = tp->pci_dev;
 ...
 rtl_set_rx_max_size(ioaddr);
 ...
 /* Enable all known interrupts by setting the interrupt mask. */
 RTL_W16(IntrMask, tp->intr_event);
}

This routine is used during the initialization of RTL8169 driver. It is obvious that it uses rtl_set_rx_max_size() to set the maximum size. This function its extremely straightforward, just look at this:

static void rtl_set_rx_max_size(void __iomem *ioaddr)
{
 /* Low hurts. Let's disable the filtering. */
 RTL_W16(RxMaxSize, 16383);
}

So, in fact the maximum frame size is hardcoded to 16383 bytes. This means that if this driver receives a frame larger than this value it will corrupt memory. Also, Michael Tokarev states that this bug is present even before 2.6.10 release. Of course, since it is dealing with ethernet frames it requires that the access to the local network of the victim. To fix this, they changed rtl_set_rx_max_size() to use rx_buf_sz like this:

-static void rtl_set_rx_max_size(void __iomem *ioaddr)
+static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)
 {
 /* Low hurts. Let's disable the filtering. */
-	RTL_W16(RxMaxSize, 16383);
+	RTL_W16(RxMaxSize, rx_buf_sz);
 }

Variable rx_buf_sz is part of rtl8169_private structure and it passed as an argument which is the current buffer’s maximum size. Consequently, the call to rtl_hw_start_8169() was updated like this:

-	rtl_set_rx_max_size(ioaddr);
+	rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);

Similar vulnerabilities was also present at the rest of the initialization routines which are rtl_hw_start_8168() and rtl_hw_start_8101() and they were patched accordingly.

Written by xorl

June 9, 2009 at 21:14

Posted in bugs, linux

4 Responses

Subscribe to comments with RSS.

  1. I am just the beginner in C, so maybe a stupid question: why do you need to pass ioaddr to the function if it is not used there?

    al

    June 10, 2009 at 09:07

  2. __iomem is defined in include/linux/compiler.h like that:

    #ifdef __CHECKER__
        ...
    # define __iomem        __attribute__((noderef, address_space(2)))
        ...
    #else
        ...
    # define __iomem
    

    which means that they use __iomem qualifier only when SPARSE is enabled. So, they probably have that parameter there either for future use or in order to have a standard format for similar functions.
    I’m not familiar with that driver however, so if anyone knows the exact reason please let us know. :)

    xorl

    June 10, 2009 at 10:59

  3. Hrm, can this result in anything greater than a dos?

    kage

    June 14, 2009 at 19:35

  4. Even though I was not able to do anything but a DoS with it (however I didn’t spend a lot of time with it), I believe that memory corruption vulnerabilities should be treated as possible code execution.

    xorl

    June 14, 2009 at 22:15


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