diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2011-06-24 16:11:54 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-06-29 17:43:08 -0400 |
commit | 03adb5f91280b433c3685d0ee86b2e1424af3d88 (patch) | |
tree | a3c034aa17d24d0355c5117a7781e473910c640c /drivers/scsi/iscsi_tcp.c | |
parent | f457a46f179df41b0f6d80dee33b6e629945f276 (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.c | 61 |
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 | */ |
111 | static inline int iscsi_sw_sk_state_check(struct sock *sk) | 113 | static 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 | ||
124 | static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag) | 126 | static 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 | ||
151 | static void iscsi_sw_tcp_state_change(struct sock *sk) | 158 | static 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 | **/ |
179 | static void iscsi_sw_tcp_write_space(struct sock *sk) | 189 | static 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 | ||
611 | static int | 632 | static int |