xorl %eax, %eax

ac_ioctl() Overflow

leave a comment »

Credit: Zvonimir Rakamaric
Affects: 2.6.11.4 – 2.6.27.6
Fixed: 2.6.28-rc1

Code from 2.6.27.6 drivers/char/applicom.c
Where this is a driver for Applicom Industrial Cards

690 static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
691
692 {                               /* @ ADG ou ATO selon le cas */
694         unsigned char IndexCard;
695         void __iomem *pmem;
696         int ret = 0;
697         volatile unsigned char byte_reset_it;
698         struct st_ram_io *adgl;
699         void __user *argp = (void __user *)arg;

Nothing to discuss here, so…

700
701         /* In general, the device is only openable by root anyway, so we're not
702            particularly concerned that bogus ioctls can flood the console. */
703
704         adgl = kmalloc(sizeof(struct st_ram_io), GFP_KERNEL);
705         if (!adgl)
706                 return -ENOMEM;

This allocates space for user arguments and following it stores user’s arguments (argument pointer – argp) to this
location:

707
708         if (copy_from_user(adgl, argp, sizeof(struct st_ram_io))) {
709                 kfree(adgl);
710                 return -EFAULT;
711         }

This means that ‘adgl’ contains completely user controlled data. Now, IndexCard gets initialized with st_ram_io.num_card which is defined at drivers/char/applicom.h as:

54 struct st_ram_io
55 {
      ...
80       unsigned char num_card;
      ...
82 };

Here is the assignment of applicom.c

712
713         IndexCard = adgl->num_card-1;
714

The following part is one of the most interesting ones. Have a look at:

 715         if(cmd != 0 && cmd != 6 &&
 716            ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
 717                 static int warncount = 10; 
 718                 if (warncount) {
 719                         printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1);
 720                         warncount--;
 721                 }
 722                 kfree(adgl);
 723                 return -EINVAL;
 724         }

As you can see.. If ‘cmd’ does not equal 0 or 6 then an error is encountered and the driver terminates with EINVAL. But this check at line 716 also checks the length of IndexCard to be less than or equal to MAX_BOARD From the same source file:

49 #define MAX_BOARD 8             /* maximum of pc board possible */

If this check fails (for example, if cmd is 0 or 6) then no bound check is performed against IndexCard and in case of cmd equal to zero, this will be executed:

726         switch (cmd) {
 727
 728         case 0:
 729                 pmem = apbs[IndexCard].RamIO;
 730                 for (i = 0; i < sizeof(struct st_ram_io); i++)
 731                         ((unsigned char *)adgl)[i]=readb(pmem++);
 732                 if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
 733                         ret = -EFAULT;
 734                 break;

This leads to pmem pointing to some undefined location since IndexCard can have arbitrary user controlled value. Next, the copy_to_user() call using pmem as source can lead to information
leak from the kernel. In case of cmd equal to 6 various info regarding the driver and the cards will be printed, the only interesting part is:

 789         case 6:
 790                 printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n");
 791                 printk(KERN_INFO "Number of installed boards . %d\n", (int)numboards);
                           ...
 805                         printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
 806                                i+1,
 807                                (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4),
 808                                (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF),
 809                                boardname);
                           ...
 830                         if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
 831                                 printk(KERN_INFO "Process in write pending board %d\n",i+1);
 832                 }
 833                 break;

Which it could lead to an information leak, but clearly in a much more limited way in comparison to cmd equal to 0.

Written by xorl

January 1, 2009 at 07:58

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