aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/iscsi_tcp.c
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2011-06-24 16:11:54 -0400
committerJames Bottomley <JBottomley@Parallels.com>2011-06-29 17:43:08 -0400
commit03adb5f91280b433c3685d0ee86b2e1424af3d88 (patch)
treea3c034aa17d24d0355c5117a7781e473910c640c /drivers/scsi/iscsi_tcp.c
parentf457a46f179df41b0f6d80dee33b6e629945f276 (diff)
[SCSI] iscsi_tcp: fix locking around iscsi sk user data
iscsi_sw_tcp_conn_restore_callbacks could have set the sk_user_data field to NULL then iscsi_sw_tcp_data_ready could read that and try to access the NULL pointer. This adds some checks for NULL sk_user_data in the sk callback functions and it uses the sk_callback_lock to set/get that sk_user_data field. Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/iscsi_tcp.c')
-rw-r--r--drivers/scsi/iscsi_tcp.c61
1 files changed, 41 insertions, 20 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 3df985305f69..7724414588fa 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -107,10 +107,12 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
107 * If the socket is in CLOSE or CLOSE_WAIT we should 107 * If the socket is in CLOSE or CLOSE_WAIT we should
108 * not close the connection if there is still some 108 * not close the connection if there is still some
109 * data pending. 109 * data pending.
110 *
111 * Must be called with sk_callback_lock.
110 */ 112 */
111static inline int iscsi_sw_sk_state_check(struct sock *sk) 113static inline int iscsi_sw_sk_state_check(struct sock *sk)
112{ 114{
113 struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data; 115 struct iscsi_conn *conn = sk->sk_user_data;
114 116
115 if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) && 117 if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) &&
116 !atomic_read(&sk->sk_rmem_alloc)) { 118 !atomic_read(&sk->sk_rmem_alloc)) {
@@ -123,11 +125,17 @@ static inline int iscsi_sw_sk_state_check(struct sock *sk)
123 125
124static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag) 126static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
125{ 127{
126 struct iscsi_conn *conn = sk->sk_user_data; 128 struct iscsi_conn *conn;
127 struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 129 struct iscsi_tcp_conn *tcp_conn;
128 read_descriptor_t rd_desc; 130 read_descriptor_t rd_desc;
129 131
130 read_lock(&sk->sk_callback_lock); 132 read_lock(&sk->sk_callback_lock);
133 conn = sk->sk_user_data;
134 if (!conn) {
135 read_unlock(&sk->sk_callback_lock);
136 return;
137 }
138 tcp_conn = conn->dd_data;
131 139
132 /* 140 /*
133 * Use rd_desc to pass 'conn' to iscsi_tcp_recv. 141 * Use rd_desc to pass 'conn' to iscsi_tcp_recv.
@@ -141,11 +149,10 @@ static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
141 149
142 iscsi_sw_sk_state_check(sk); 150 iscsi_sw_sk_state_check(sk);
143 151
144 read_unlock(&sk->sk_callback_lock);
145
146 /* If we had to (atomically) map a highmem page, 152 /* If we had to (atomically) map a highmem page,
147 * unmap it now. */ 153 * unmap it now. */
148 iscsi_tcp_segment_unmap(&tcp_conn->in.segment); 154 iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
155 read_unlock(&sk->sk_callback_lock);
149} 156}
150 157
151static void iscsi_sw_tcp_state_change(struct sock *sk) 158static void iscsi_sw_tcp_state_change(struct sock *sk)
@@ -157,8 +164,11 @@ static void iscsi_sw_tcp_state_change(struct sock *sk)
157 void (*old_state_change)(struct sock *); 164 void (*old_state_change)(struct sock *);
158 165
159 read_lock(&sk->sk_callback_lock); 166 read_lock(&sk->sk_callback_lock);
160 167 conn = sk->sk_user_data;
161 conn = (struct iscsi_conn*)sk->sk_user_data; 168 if (!conn) {
169 read_unlock(&sk->sk_callback_lock);
170 return;
171 }
162 session = conn->session; 172 session = conn->session;
163 173
164 iscsi_sw_sk_state_check(sk); 174 iscsi_sw_sk_state_check(sk);
@@ -178,11 +188,25 @@ static void iscsi_sw_tcp_state_change(struct sock *sk)
178 **/ 188 **/
179static void iscsi_sw_tcp_write_space(struct sock *sk) 189static void iscsi_sw_tcp_write_space(struct sock *sk)
180{ 190{
181 struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data; 191 struct iscsi_conn *conn;
182 struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 192 struct iscsi_tcp_conn *tcp_conn;
183 struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 193 struct iscsi_sw_tcp_conn *tcp_sw_conn;
194 void (*old_write_space)(struct sock *);
195
196 read_lock_bh(&sk->sk_callback_lock);
197 conn = sk->sk_user_data;
198 if (!conn) {
199 read_unlock_bh(&sk->sk_callback_lock);
200 return;
201 }
202
203 tcp_conn = conn->dd_data;
204 tcp_sw_conn = tcp_conn->dd_data;
205 old_write_space = tcp_sw_conn->old_write_space;
206 read_unlock_bh(&sk->sk_callback_lock);
207
208 old_write_space(sk);
184 209
185 tcp_sw_conn->old_write_space(sk);
186 ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n"); 210 ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n");
187 iscsi_conn_queue_work(conn); 211 iscsi_conn_queue_work(conn);
188} 212}
@@ -592,20 +616,17 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
592 /* userspace may have goofed up and not bound us */ 616 /* userspace may have goofed up and not bound us */
593 if (!sock) 617 if (!sock)
594 return; 618 return;
595 /*
596 * Make sure our recv side is stopped.
597 * Older tools called conn stop before ep_disconnect
598 * so IO could still be coming in.
599 */
600 write_lock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
601 set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
602 write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
603 619
604 sock->sk->sk_err = EIO; 620 sock->sk->sk_err = EIO;
605 wake_up_interruptible(sk_sleep(sock->sk)); 621 wake_up_interruptible(sk_sleep(sock->sk));
606 622
607 iscsi_conn_stop(cls_conn, flag); 623 /* stop xmit side */
624 iscsi_suspend_tx(conn);
625
626 /* stop recv side and release socket */
608 iscsi_sw_tcp_release_conn(conn); 627 iscsi_sw_tcp_release_conn(conn);
628
629 iscsi_conn_stop(cls_conn, flag);
609} 630}
610 631
611static int 632static int