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 (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