[SCSI] aacraid: prevent copy_from_user() BUG!
authorMark Salyzyn <Mark_Salyzyn@adaptec.com>
Wed, 28 May 2008 19:32:55 +0000 (15:32 -0400)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Sat, 12 Jul 2008 13:22:24 +0000 (08:22 -0500)
Seen:

kernel BUG at arch/i386/lib/usercopy.c:872

under a 2.6.18-8.el5 kernel. Traced it to a garbage-in/garbage-out
ioctl condition in the aacraid driver.

Adaptec's special ioctl scb passthrough needs to check the validity of
the individual scatter gather count fields to the maximum the adapter
supports. Doing so will have the side effect of preventing
copy_from_user() from bugging out while populating the dma buffers.
This is a hardening effort, issue was triggered by an errant version
of the management tools and thus the BUG should not be seen in the
field.

[jejb: fixed up compile failure]
Signed-off-by: Mark Salyzyn <aacraid@adaptec.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/scsi/aacraid/commctrl.c

index 5fd83deab36ca6f1bf0834c83e4714fae63b124a..a7355260cfcfc4d15b0354ab07dc90ad41761407 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/kthread.h>
 #include <linux/semaphore.h>
 #include <asm/uaccess.h>
+#include <scsi/scsi_host.h>
 
 #include "aacraid.h"
 
@@ -581,6 +582,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
                        for (i = 0; i < upsg->count; i++) {
                                u64 addr;
                                void* p;
+                               if (upsg->sg[i].count >
+                                   (dev->adapter_info.options &
+                                    AAC_OPT_NEW_COMM) ?
+                                     (dev->scsi_host_ptr->max_sectors << 9) :
+                                     65536) {
+                                       rcode = -EINVAL;
+                                       goto cleanup;
+                               }
                                /* Does this really need to be GFP_DMA? */
                                p = kmalloc(upsg->sg[i].count,GFP_KERNEL|__GFP_DMA);
                                if(!p) {
@@ -625,6 +634,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
                        for (i = 0; i < usg->count; i++) {
                                u64 addr;
                                void* p;
+                               if (usg->sg[i].count >
+                                   (dev->adapter_info.options &
+                                    AAC_OPT_NEW_COMM) ?
+                                     (dev->scsi_host_ptr->max_sectors << 9) :
+                                     65536) {
+                                       rcode = -EINVAL;
+                                       goto cleanup;
+                               }
                                /* Does this really need to be GFP_DMA? */
                                p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
                                if(!p) {
@@ -667,6 +684,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
                        for (i = 0; i < upsg->count; i++) {
                                uintptr_t addr;
                                void* p;
+                               if (usg->sg[i].count >
+                                   (dev->adapter_info.options &
+                                    AAC_OPT_NEW_COMM) ?
+                                     (dev->scsi_host_ptr->max_sectors << 9) :
+                                     65536) {
+                                       rcode = -EINVAL;
+                                       goto cleanup;
+                               }
                                /* Does this really need to be GFP_DMA? */
                                p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
                                if(!p) {
@@ -698,6 +723,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
                        for (i = 0; i < upsg->count; i++) {
                                dma_addr_t addr;
                                void* p;
+                               if (upsg->sg[i].count >
+                                   (dev->adapter_info.options &
+                                    AAC_OPT_NEW_COMM) ?
+                                     (dev->scsi_host_ptr->max_sectors << 9) :
+                                     65536) {
+                                       rcode = -EINVAL;
+                                       goto cleanup;
+                               }
                                p = kmalloc(upsg->sg[i].count, GFP_KERNEL);
                                if (!p) {
                                        dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",