Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: libcxgbi: simplify task->hdr allocation for mgmt cmds

In case of mgmt cmds, task->hdr is dereferenced after transmitting the
pdu in iscsi_tcp_task_xmit(). To handle this case current code
increments the Tx skb reference count and frees the skb in
cxgbi_cleanup_task(). In some error cases this results in skb leak.

To fix this in case of mgmt cmds, allocate a separate buffer for iSCSI
hdr and free this buffer in cxgbi_cleanup_task().

Signed-off-by: Varun Prakash <varun@chelsio.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Varun Prakash and committed by
Martin K. Petersen
22698483 9b3a081f

+28 -16
+28 -15
drivers/scsi/cxgbi/libcxgbi.c
··· 1887 1887 struct iscsi_tcp_task *tcp_task = task->dd_data; 1888 1888 struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task); 1889 1889 struct scsi_cmnd *sc = task->sc; 1890 + struct cxgbi_sock *csk = cconn->cep->csk; 1891 + struct net_device *ndev = cdev->ports[csk->port_id]; 1890 1892 int headroom = SKB_TX_ISCSI_PDU_HEADER_MAX; 1891 1893 1892 1894 tcp_task->dd_data = tdata; 1893 1895 task->hdr = NULL; 1894 - 1895 - if (tdata->skb) { 1896 - kfree_skb(tdata->skb); 1897 - tdata->skb = NULL; 1898 - } 1899 1896 1900 1897 if (SKB_MAX_HEAD(cdev->skb_tx_rsvd) > (512 * MAX_SKB_FRAGS) && 1901 1898 (opcode == ISCSI_OP_SCSI_DATA_OUT || ··· 1905 1908 1906 1909 tdata->skb = alloc_skb(cdev->skb_tx_rsvd + headroom, GFP_ATOMIC); 1907 1910 if (!tdata->skb) { 1908 - struct cxgbi_sock *csk = cconn->cep->csk; 1909 - struct net_device *ndev = cdev->ports[csk->port_id]; 1910 1911 ndev->stats.tx_dropped++; 1911 1912 return -ENOMEM; 1912 1913 } 1913 1914 1914 - skb_get(tdata->skb); 1915 1915 skb_reserve(tdata->skb, cdev->skb_tx_rsvd); 1916 - task->hdr = (struct iscsi_hdr *)tdata->skb->data; 1916 + 1917 + if (task->sc) { 1918 + task->hdr = (struct iscsi_hdr *)tdata->skb->data; 1919 + } else { 1920 + task->hdr = kzalloc(SKB_TX_ISCSI_PDU_HEADER_MAX, GFP_KERNEL); 1921 + if (!task->hdr) { 1922 + __kfree_skb(tdata->skb); 1923 + tdata->skb = NULL; 1924 + ndev->stats.tx_dropped++; 1925 + return -ENOMEM; 1926 + } 1927 + } 1917 1928 task->hdr_max = SKB_TX_ISCSI_PDU_HEADER_MAX; /* BHS + AHS */ 1918 1929 1919 1930 /* data_out uses scsi_cmd's itt */ ··· 2065 2060 unsigned int datalen; 2066 2061 int err; 2067 2062 2068 - if (!skb || cxgbi_skcb_test_flag(skb, SKCBF_TX_DONE)) { 2063 + if (!skb) { 2069 2064 log_debug(1 << CXGBI_DBG_ISCSI | 1 << CXGBI_DBG_PDU_TX, 2070 - "task 0x%p, skb 0x%p\n", task, skb); 2065 + "task 0x%p\n", task); 2071 2066 return 0; 2072 2067 } 2073 2068 ··· 2079 2074 return -EPIPE; 2080 2075 } 2081 2076 2077 + tdata->skb = NULL; 2082 2078 datalen = skb->data_len; 2083 2079 2084 2080 /* write ppod first if using ofldq to write ppod */ ··· 2092 2086 task); 2093 2087 /* continue. Let fl get the data */ 2094 2088 } 2089 + 2090 + if (!task->sc) 2091 + memcpy(skb->data, task->hdr, SKB_TX_ISCSI_PDU_HEADER_MAX); 2095 2092 2096 2093 err = cxgbi_sock_send_pdus(cconn->cep->csk, skb); 2097 2094 if (err > 0) { ··· 2111 2102 pdulen += ISCSI_DIGEST_SIZE; 2112 2103 2113 2104 task->conn->txdata_octets += pdulen; 2114 - cxgbi_skcb_set_flag(skb, SKCBF_TX_DONE); 2115 2105 return 0; 2116 2106 } 2117 2107 ··· 2119 2111 "task 0x%p, skb 0x%p, len %u/%u, %d EAGAIN.\n", 2120 2112 task, skb, skb->len, skb->data_len, err); 2121 2113 /* reset skb to send when we are called again */ 2114 + tdata->skb = skb; 2122 2115 return err; 2123 2116 } 2124 2117 ··· 2127 2118 "itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n", 2128 2119 task->itt, skb, skb->len, skb->data_len, err); 2129 2120 2130 - __kfree_skb(tdata->skb); 2131 - tdata->skb = NULL; 2121 + __kfree_skb(skb); 2132 2122 2133 2123 iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err); 2134 2124 iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED); ··· 2152 2144 task, tdata->skb, task->hdr_itt); 2153 2145 2154 2146 tcp_task->dd_data = NULL; 2147 + 2148 + if (!task->sc) 2149 + kfree(task->hdr); 2150 + task->hdr = NULL; 2151 + 2155 2152 /* never reached the xmit task callout */ 2156 2153 if (tdata->skb) { 2157 - kfree_skb(tdata->skb); 2154 + __kfree_skb(tdata->skb); 2158 2155 tdata->skb = NULL; 2159 2156 } 2160 2157
-1
drivers/scsi/cxgbi/libcxgbi.h
··· 205 205 SKCBF_TX_NEED_HDR, /* packet needs a header */ 206 206 SKCBF_TX_MEM_WRITE, /* memory write */ 207 207 SKCBF_TX_FLAG_COMPL, /* wr completion flag */ 208 - SKCBF_TX_DONE, /* skb tx done */ 209 208 SKCBF_RX_COALESCED, /* received whole pdu */ 210 209 SKCBF_RX_HDR, /* received pdu header */ 211 210 SKCBF_RX_DATA, /* received pdu payload */