Re: ZDI-CAN-17016: New Vulnerability Report

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a valid double copy bug.  I think/hope that /dev/dpt_i2o can
only be opened by root so I'm going to CC the public lists.

Last week we deleted the PMCRAID_PASSTHROUGH_IOCTL ioctl in commit
f16aa285e618 ("scsi: pmcraid: Remove the PMCRAID_PASSTHROUGH_IOCTL ioctl
implementation").  The temptation is to delete I2OUSRCMD as well.

regards,
dan carpenter

On Fri, Apr 01, 2022 at 01:11:18PM +0000, zdi-disclosures@xxxxxxxxxxxxxx wrote:
> ZDI-CAN-17016: Linux Kernel DPT I2O Controller Time-Of-Check Time-Of-Use Information Disclosure Vulnerability
> 
> -- CVSS -----------------------------------------
> 
> 6.7: AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:L
> 
> -- ABSTRACT -------------------------------------
> 
> Trend Micro's Zero Day Initiative has identified a vulnerability affecting the following products:
> Linux - Kernel
> 
> -- VULNERABILITY DETAILS ------------------------
> * Version tested:5.16.14
> * Installer file:linux-5.16.14.tar.xz
> * Platform tested:ubuntu 21.10 amd64
> 
> ---
> 
> ### Analysis
> 
> ```
> double fetch bug exist in the function adpt_i2o_passthru of DPT I2O controller
> it leads to copy the OOB memory to ring3
> ps. we didn't have the real hardware for testing, the POC is written based on modified DPT I2O controller
> ```
> 
> ~~~C++
> static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg)
> {
> ...
>         /* Copy in the user's I2O command */
>         if(copy_from_user(msg, user_msg, size)) {
>                 return -EFAULT;
>         }
> ...
>         sg_offset = (msg[0]>>4)&0xf;
>         msg[2] = 0x40000000; // IOCTL context
>         msg[3] = adpt_ioctl_to_context(pHba, reply);
>         if (msg[3] == (u32)-1) {
>                 rcode = -EBUSY;
>                 goto free;
>         }
> ...
>         if(sg_offset) {
>                 // TODO add 64 bit API
>                 struct sg_simple_element *sg =  (struct sg_simple_element*) (msg+sg_offset);
>                 sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
>                 if (sg_count > pHba->sg_tablesize){
>                         printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count);
>                         rcode = -EINVAL;
>                         goto free;
>                 }
> 
>                 for(i = 0; i < sg_count; i++) {
>                         int sg_size;
> 
>                         if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) {
>                                 printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i,  sg[i].flag_count);
>                                 rcode = -EINVAL;
>                                 goto cleanup;
>                         }
>                         sg_size = sg[i].flag_count & 0xffffff;
>                         /* Allocate memory for the transfer */
>                         p = dma_alloc_coherent(&pHba->pDev->dev, sg_size, &addr, GFP_KERNEL);                                   <--- (1) allocate buffer with the 1st fetched size
>                         if(!p) {
>                                 printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
>                                                 pHba->name,sg_size,i,sg_count);
>                                 rcode = -ENOMEM;
>                                 goto cleanup;
>                         }
>                         sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
>                         /* Copy in the user's SG buffer if necessary */
>                         if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
>                                 // sg_simple_element API is 32 bit
>                                 if (copy_from_user(p,(void __user *)(ulong)sg[i].addr_bus, sg_size)) {
>                                         printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
>                                         rcode = -EFAULT;
>                                         goto cleanup;
>                                 }
>                         }
>                         /* sg_simple_element API is 32 bit, but addr < 4GB */
>                         sg[i].addr_bus = addr;
>                 }
>         }
> ...
> 
>         if(sg_offset) {
> ...
>                 /* Copy in the user's I2O command */
>                 if (copy_from_user (msg, user_msg, size)) {
>                         rcode = -EFAULT;
>                         goto cleanup;
>                 }
>                 sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
> 
>                 // TODO add 64 bit API
>                 sg       = (struct sg_simple_element*)(msg + sg_offset);
>                 for (j = 0; j < sg_count; j++) {
>                         /* Copy out the SG list to user's buffer if necessary */
>                         if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
>                                 sg_size = sg[j].flag_count & 0xffffff;
>                                 // sg_simple_element API is 32 bit
>                                 if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) {           <--- (2) copy with the 2nd fetched size
>                                         printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
>                                         rcode = -EFAULT;
>                                         goto cleanup;
>                                 }
>                         }
>                 }
>         }
> ...
> }
> ~~~
> 
> 
> 
> debug log
> ```
> (gdb) u drivers/scsi/dpt_i2o.c:1748
> adpt_i2o_passthru (pHba=<optimized out>, arg=<optimized out>) at drivers/scsi/dpt_i2o.c:1748
> 1748                            if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
> (gdb) p/x sg[i].flag_count
> $4 = 0x14000010
> (gdb) p/x sg_size
> $5 = 0x10
> (gdb) l
> 1743                                    rcode = -ENOMEM;
> 1744                                    goto cleanup;
> 1745                            }
> 1746                            sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
> 1747                            /* Copy in the user's SG buffer if necessary */
> 1748                            if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
> 1749                                    // sg_simple_element API is 32 bit
> 1750                                    if (copy_from_user(p,(void __user *)(ulong)sg[i].addr_bus, sg_size)) {
> 1751                                            printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
> 1752                                            rcode = -EFAULT;
> (gdb) bt
> #0  adpt_i2o_passthru (pHba=<optimized out>, arg=<optimized out>) at drivers/scsi/dpt_i2o.c:1748
> #1  0xffffffffc00045c8 in adpt_ioctl (cmd=17484, arg=94117499510944, file=<optimized out>, inode=<optimized out>, inode=<optimized out>) at drivers/scsi/dpt_i2o.c:2006
> #2  0xffffffffc00053ce in adpt_unlocked_ioctl (file=<optimized out>, cmd=17484, arg=94117499510944) at drivers/scsi/dpt_i2o.c:2066
> #3  0xffffffff816207a2 in ?? ()
> #4  0x0000000000000000 in ?? ()
> (gdb) u drivers/scsi/dpt_i2o.c:1815
> adpt_i2o_passthru (pHba=<optimized out>, arg=<optimized out>) at drivers/scsi/dpt_i2o.c:1815
> 1815                            if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
> (gdb) l
> 1810
> 1811                    // TODO add 64 bit API
> 1812                    sg       = (struct sg_simple_element*)(msg + sg_offset);
> 1813                    for (j = 0; j < sg_count; j++) {
> 1814                            /* Copy out the SG list to user's buffer if necessary */
> 1815                            if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
> 1816                                    sg_size = sg[j].flag_count & 0xffffff;
> 1817                                    // sg_simple_element API is 32 bit
> 1818                                    if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) {
> 1819                                            printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
> (gdb) p/x sg[j].flag_count
> $6 = 0x10002000
> (gdb) u drivers/scsi/dpt_i2o.c:1816
> adpt_i2o_passthru (pHba=<optimized out>, arg=<optimized out>) at drivers/scsi/dpt_i2o.c:1818
> 1818                                    if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) {
> (gdb) p/x sg_size
> $7 = 0x2000
> (gdb) x/10xg sg_list[j]
> 0xffff8880066d6560:     0x4141414141414141      0x4141414141414141                      // OOB read 0x2000-0x10 bytes
> 0xffff8880066d6570:     0xb000007000000122      0x0000000000000000
> 0xffff8880066d6580:     0xffff888006798000      0xffff8880066d6540
> 0xffff8880066d6590:     0xe020000700000001      0x0000000000000000
> 0xffff8880066d65a0:     0xffff88800e6dc400      0xa180005900000120
> (gdb) bt
> #0  adpt_i2o_passthru (pHba=<optimized out>, arg=<optimized out>) at drivers/scsi/dpt_i2o.c:1818
> #1  0xffffffffc00045c8 in adpt_ioctl (cmd=17484, arg=94117499510944, file=<optimized out>, inode=<optimized out>, inode=<optimized out>) at drivers/scsi/dpt_i2o.c:2006
> #2  0xffffffffc00053ce in adpt_unlocked_ioctl (file=<optimized out>, cmd=17484, arg=94117499510944) at drivers/scsi/dpt_i2o.c:2066
> #3  0xffffffff816207a2 in ?? ()
> #4  0x0000000000000000 in ?? ()
> (gdb)
> ```
> 
> 
> -- CREDIT ---------------------------------------
> This vulnerability was discovered by:
> Lucas Leong (@_wmliang_) and Reno Robert of Trend Micro Zero Day Initiative
> 
> -- FURTHER DETAILS ------------------------------
> 
> If supporting files were contained with this report they are provided within a password protected ZIP file. The password is the ZDI candidate number in the form: ZDI-CAN-XXXX where XXXX is the ID number.
> 
> Please confirm receipt of this report. We expect all vendors to remediate ZDI vulnerabilities within 120 days of the reported date. If you are ready to release a patch at any point leading up to the deadline, please coordinate with us so that we may release our advisory detailing the issue. If the 120-day deadline is reached and no patch has been made available we will release a limited public advisory with our own mitigations, so that the public can protect themselves in the absence of a patch. Please keep us updated regarding the status of this issue and feel free to contact us at any time:
> 
> Zero Day Initiative
> zdi-disclosures@xxxxxxxxxxxxxx
> 
> The PGP key used for all ZDI vendor communications is available from:
> 
>   http://www.zerodayinitiative.com/documents/disclosures-pgp-key.asc 
> 
> -- INFORMATION ABOUT THE ZDI --------------------
> Established by TippingPoint and acquired by Trend Micro, the Zero Day Initiative (ZDI) neither re-sells vulnerability details nor exploit code. Instead, upon notifying the affected product vendor, the ZDI provides its Trend Micro TippingPoint customers with zero day protection through its intrusion prevention technology. Explicit details regarding the specifics of the vulnerability are not exposed to any parties until an official vendor patch is publicly available.
> 
> Please contact us for further details or refer to:
> 
>   http://www.zerodayinitiative.com 
> 
> -- DISCLOSURE POLICY ----------------------------
> 
> Our vulnerability disclosure policy is available online at:
> 
>   http://www.zerodayinitiative.com/advisories/disclosure_policy/ 
> 
> TREND MICRO EMAIL NOTICE
> 
> The information contained in this email and any attachments is confidential and may be subject to copyright or other intellectual property protection. If you are not the intended recipient, you are not authorized to use or disclose this information, and we request that you notify us by reply mail or telephone and delete the original message from your mail system.
> 
> For details about what personal information we collect and why, please see our Privacy Notice on our website at: Read privacy policy<http://www.trendmicro.com/privacy >



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux