xorl %eax, %eax

Linux Kernel Console off-by-two Overflow

leave a comment »

I was reading kernel’s changelog and I came across of a really interesting vulnerability which was reported to the Linux Kernel Mailing List on 30 January 2009 by Mikulas Patocka. Linux kernels prior to release are vulnerable to this bug. The next code is taken from Linux kernel The vulnerable code can be found in drivers/char/selection.c. This driver is used to provide console selection operations. Keep in mind the following before moving to the vulnerable function:

35 /* Variables for selection control. */
39 static volatile int sel_start = -1;     /* cleared by clear_selection */
40 static int sel_end;

Now, let’s move the buggy function. Here it is:

135 /* set the current selection. Invoked by ioctl() or by kernel code. */
136 int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
137 {

It can also be invoked through an ioctl call, that makes things even more interesting.. Here are some notable segments of the above function:

139        int sel_mode, new_sel_start, new_sel_end, spc;
140        char *bp, *obp;
141        int i, ps, pe, multiplier;
         // Do stuff to get the new selection positions
266        sel_start = new_sel_start;
267        sel_end = new_sel_end;
269        /* Allocate a new buffer before freeing the old one ... */
270        multiplier = use_unicode ? 3 : 1;  /* chars can take up to 3 bytes */
271        bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL);
277        kfree(sel_buffer);
278        sel_buffer = bp;
281        for (i = sel_start; i <= sel_end; i += 2) {
282                c = sel_pos(i);
283                if (use_unicode)
284                        bp += store_utf8(c, bp);
300        return 0;
301 }

The invalid calculation is the one listed at line 271. This is used to allocate a new buffer. At line 270 we can clearly see that if use_unicode is set then the multiplier will be 3 (because of the 3 bytes length) and 1 in any other case. Then, it does this:

  bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL);

This might seem right on a quick look but think about it more carefully. The for loop at line 281 iterates through sel_start up to, and including sel_end (notice the <= not just <). This means that it needs an additional element for the last iteration. This was supposed to be the ‘+1‘ at line 271 but this would only work for non-unicode characters where the multiplier would be 1. If the character encoding is unicode (which means 3 bytes per element), the additional element in the final iteration of the for loop has to be 3 bytes long to fit a unicode character! However, on the above code it will always be 1 byte! This overflow can lead to code execution since the user controlled c character at line 282 gets written past the end of the heap allocated bp buffer! kmalloc() exploitation is not something trivial and it is also not well documented. I can only recall the great article of qobaiashi. Btw.. let’s hope that he’s fine wherever he is. And at last, here is the patch Patocka committed:

     multiplier = use_unicode ? 3 : 1;  /* chars can take up to 3 bytes */
-    bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL);
+    bp = kmalloc(((sel_end-sel_start)/2+1)*multiplier, GFP_KERNEL);
     if (!bp) {

The trigger is simple, just print out 3 unicode characters and then select them from the console. You might also be interested in direct call using IOCTLs…
To conclude, here is the funniest part.. SecurityFocus classified this vulnerability as a race condition and gave credit to Greg KH :P

Written by xorl

February 12, 2009 at 14:12

Posted in bugs, linux

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