FreeBSD 10 qlxge/qlxgbe Driver IOCTL Multiple Kernel Memory Leak



EKU-ID: 3644 CVE: OSVDB-ID:
Author: x90c Published: 2013-11-18 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


XADV-2013006
FreeBSD <= 10 kernel qlxge/qlxgbe Driver IOCTL Multiple Kernel Memory Leak Bugs
  
  
1. Overview
  
The qlxge Driver is Qlogic 10Gb Ethernet Driver for Qlogic 8100
Series CNA Adapter [1]. The qlxgbe for the QLogic 8300 series
of the same ethernet driver.
  
The qlxge/qlxgbe Driver in freebsd <= 10 has vulnerabilities to leak
arbitrary kernel memory to the userspace. It's occured at qls_eioctl()
/ ql_eioctl() kernel function and because no sanity check.
  
* Vulnerable Source Code:
  - qlxge: http://fxr.watson.org/fxr/source/dev/qlxge/qls_ioctl.c?v=FREEBSD10
  - qlxgbe: http://fxr.watson.org/fxr/source/dev/qlxgbe/ql_ioctl.c?v=FREEBSD10
  
* Credit:
  - x90c <geinblues@gmail.com>
    (site: http://www.x90c.org)
  
* References:
  [1] http://fxr.watson.org/fxr/source/dev/qlxge/README.txt?v=FREEBSD10
  [2] http://fxr.watson.org/fxr/source/dev/ath/if_ath.c?v=FREEBSD10#L5881
  
  
2. Details
  
2.1 The vulerability for the qlxge driver
  
[/dev/qlxge/qls_ioctl.c?v=FREEBSD10#L80]
----
...
   40 #include "qls_ioctl.h"
   41 #include "qls_dump.h"
   42 extern qls_mpi_coredump_t ql_mpi_coredump; // XXX The leak kmem!
   43 
   44 static int qls_eioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
   45                 struct thread *td);
   46 
   47 static struct cdevsw qla_cdevsw = {
   48         .d_version = D_VERSION,
   49         .d_ioctl = qls_eioctl,  // XXX qls_eioctl.
   50         .d_name = "qlxge",
   51 };
   52 
...
  
   80 static int
   81 qls_eioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
   82         struct thread *td)
   83 {
   84         qla_host_t *ha;
   85         int rval = 0;
   86         device_t pci_dev;
   87 
   88         qls_mpi_dump_t *mpi_dump;
   89 
   90         if ((ha = (qla_host_t *)dev->si_drv1) == NULL)
   91                 return ENXIO;
   92 
   93         pci_dev= ha->pci_dev;
   94 
   95         switch(cmd) {
   96 
   97         case QLA_MPI_DUMP:
   98                 mpi_dump = (qls_mpi_dump_t *)data; // mpi_dump = data(arg).
   99 
  100                 if (mpi_dump->size == 0) {
  101                         mpi_dump->size = sizeof (qls_mpi_coredump_t);
  
  102                 } else { // XXX mpi_dump->size > 0?
  
  103                         if (mpi_dump->size < sizeof (qls_mpi_coredump_t))
  104                                 rval = EINVAL;
  
  105                         else { // XXX mpi_dump_size > qls_mpi_coredump_t struct size?
  
  106                                 qls_mpi_core_dump(ha);
  
              /* XXX copy ql_mpi_coredump(static kmem) to userspace with 
               *     mpi_dump->size(arg). Kernel memory leak occured!
                                       */
  107                                 rval = copyout( &ql_mpi_coredump,
  108                                                 mpi_dump->dbuf,
  109                                                 mpi_dump->size);
----
  
  
2.2 The vulerability for the qlxgbe driver
  
[/dev/qlxgbe/ql_ioctl.c?v=FREEBSD10#L79]
----
46 static struct cdevsw qla_cdevsw = {
   47         .d_version = D_VERSION,
   48         .d_ioctl = ql_eioctl, /* XXX ql_eioctl! */
   49         .d_name = "qlcnic",
   50 };
  
...
  
  79 static int
   80 ql_eioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
   81         struct thread *td)
   82 {
   83         qla_host_t *ha;
  
...
  
   90         qla_rd_fw_dump_t *fw_dump;
   91         union {
   92                 qla_reg_val_t *rv;
   93                 qla_rd_flash_t *rdf;
   94                 qla_wr_flash_t *wrf;
   95                 qla_erase_flash_t *erf;
   96                 qla_offchip_mem_val_t *mem;
   97         } u;
   98 
   99 
  100         if ((ha = (qla_host_t *)dev->si_drv1) == NULL) /* XXX ha = dev->si_drv1. */
  101                 return ENXIO;
  102 
...
  105         switch(cmd) {
  106 
...
  218         case QLA_RD_FW_DUMP: /* XXX QLA_RD_FW_DUMP ioctl cmd */
  219 
  220                 if (ha->hw.mdump_init == 0) {
  221                         rval = EINVAL;
  222                         break;
  223                 }
  224                 
  225                 fw_dump = (qla_rd_fw_dump_t *)data; // XXX fw_dump = data(arg)
  
          /* XXX no sanity check and copy arbitrary ha... (the kmem)
                       *     kmem to userspace (kmem leak occured!)
                       */
  226                 if ((rval = copyout(ha->hw.dma_buf.minidump.dma_b,
  227                         fw_dump->md_template, fw_dump->template_size)))
  228                         rval = ENXIO;
  229                 break;
----
  
  
3. Patch code
  
[freebsd_qlxge_kmem_leak.patch]
----
+   if(mpi_dump->size > sizeof(qls_mpi_coredump_t))
+           return EINVAL;
  
    rval = copyout( &ql_mpi_coredump,
                    mpi_dump->dbuf,
                    mpi_dump->size);
----
  
[freebsd_qlxgbe_kmem_leak.patch]
----
+   if(fw_dump->template_size > sizeof(qla_host_t))
+       return EINVAL;
    if ((rval = copyout(ha->hw.dma_buf.minidump.dma_b,
        fw_dump->md_template, fw_dump->template_size)))
----
  
  
There's the vendor patch code.
  
[qlxg.diff]
----
Index: sys/dev/qlxgbe/ql_ioctl.c
===================================================================
--- sys/dev/qlxgbe/ql_ioctl.c  (revision 258154)
+++ sys/dev/qlxgbe/ql_ioctl.c  (working copy)
@@ -223,6 +223,10 @@ ql_eioctl(struct cdev *dev, u_long cmd, caddr_t da
     }
       
     fw_dump = (qla_rd_fw_dump_t *)data;
+    if (fw_dump->template_size < ha->hw.dma_buf.minidump.size)
+      return (EINVAL);
+    else
+      fw_dump->template_size = ha->hw.dma_buf.minidump.size;
     if ((rval = copyout(ha->hw.dma_buf.minidump.dma_b,
       fw_dump->md_template, fw_dump->template_size)))
       rval = ENXIO;
Index: sys/dev/qlxge/qls_ioctl.c
===================================================================
--- sys/dev/qlxge/qls_ioctl.c  (revision 258154)
+++ sys/dev/qlxge/qls_ioctl.c  (working copy)
@@ -103,10 +103,13 @@ qls_eioctl(struct cdev *dev, u_long cmd, caddr_t d
       if (mpi_dump->size < sizeof (qls_mpi_coredump_t))
         rval = EINVAL;
       else {
-        qls_mpi_core_dump(ha);
-        rval = copyout( &ql_mpi_coredump,
-            mpi_dump->dbuf,
-            mpi_dump->size);
+        mpi_dump->size = sizeof(qls_mpi_coredump_t);
+        if (qls_mpi_core_dump(ha) == 0) {
+          rval = copyout( &ql_mpi_coredump,
+              mpi_dump->dbuf,
+              mpi_dump->size);
+        } else
+          rval = ENXIO;
   
         if (rval) {
           device_printf(ha->pci_dev,
----
  
  
4. Vendor Status
  
- 2013/11/12 I discovered two kernel memory leaks.
- 2013/11/14 Report to the vendor of secteam@freebsd.org.
- 2013/11/15 The vendor response with the coordination 
             with the vendor patch code. (will be freebsd's advisory)
- 2013/11/16 Cve-id for each bug request to the cve-assign@mitre.org.
- 2013/11/16 The original advisory released on full-disclosure, bugtraq.
  
  
EOF