aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Long <lucien.xin@gmail.com>2016-09-28 14:55:44 -0400
committerDavid S. Miller <davem@davemloft.net>2016-09-30 02:08:57 -0400
commit1cceda7849809a8857fd9f26efe8846506c710e1 (patch)
treefbdd3dec3706dabe2ef7d83e8c78a1f4e224ceb3
parent75b005b949d3dc93b526c3da0a750fd1fc9a703a (diff)
sctp: fix the issue sctp_diag uses lock_sock in rcu_read_lock
When sctp dumps all the ep->assocs, it needs to lock_sock first, but now it locks sock in rcu_read_lock, and lock_sock may sleep, which would break rcu_read_lock. This patch is to get and hold one sock when traversing the list. After that and get out of rcu_read_lock, lock and dump it. Then it will traverse the list again to get the next one until all sctp socks are dumped. For sctp_diag_dump_one, it fixes this issue by holding asoc and moving cb() out of rcu_read_lock in sctp_transport_lookup_process. Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Signed-off-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/sctp/sctp_diag.c58
-rw-r--r--net/sctp/socket.c10
2 files changed, 47 insertions, 21 deletions
diff --git a/net/sctp/sctp_diag.c b/net/sctp/sctp_diag.c
index f3508aa75815..cef0cee182d4 100644
--- a/net/sctp/sctp_diag.c
+++ b/net/sctp/sctp_diag.c
@@ -272,28 +272,17 @@ out:
272 return err; 272 return err;
273} 273}
274 274
275static int sctp_tsp_dump(struct sctp_transport *tsp, void *p) 275static int sctp_sock_dump(struct sock *sk, void *p)
276{ 276{
277 struct sctp_endpoint *ep = tsp->asoc->ep; 277 struct sctp_endpoint *ep = sctp_sk(sk)->ep;
278 struct sctp_comm_param *commp = p; 278 struct sctp_comm_param *commp = p;
279 struct sock *sk = ep->base.sk;
280 struct sk_buff *skb = commp->skb; 279 struct sk_buff *skb = commp->skb;
281 struct netlink_callback *cb = commp->cb; 280 struct netlink_callback *cb = commp->cb;
282 const struct inet_diag_req_v2 *r = commp->r; 281 const struct inet_diag_req_v2 *r = commp->r;
283 struct sctp_association *assoc = 282 struct sctp_association *assoc;
284 list_entry(ep->asocs.next, struct sctp_association, asocs);
285 int err = 0; 283 int err = 0;
286 284
287 /* find the ep only once through the transports by this condition */
288 if (tsp->asoc != assoc)
289 goto out;
290
291 if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family)
292 goto out;
293
294 lock_sock(sk); 285 lock_sock(sk);
295 if (sk != assoc->base.sk)
296 goto release;
297 list_for_each_entry(assoc, &ep->asocs, asocs) { 286 list_for_each_entry(assoc, &ep->asocs, asocs) {
298 if (cb->args[4] < cb->args[1]) 287 if (cb->args[4] < cb->args[1])
299 goto next; 288 goto next;
@@ -312,7 +301,7 @@ static int sctp_tsp_dump(struct sctp_transport *tsp, void *p)
312 cb->nlh->nlmsg_seq, 301 cb->nlh->nlmsg_seq,
313 NLM_F_MULTI, cb->nlh) < 0) { 302 NLM_F_MULTI, cb->nlh) < 0) {
314 cb->args[3] = 1; 303 cb->args[3] = 1;
315 err = 2; 304 err = 1;
316 goto release; 305 goto release;
317 } 306 }
318 cb->args[3] = 1; 307 cb->args[3] = 1;
@@ -321,7 +310,7 @@ static int sctp_tsp_dump(struct sctp_transport *tsp, void *p)
321 sk_user_ns(NETLINK_CB(cb->skb).sk), 310 sk_user_ns(NETLINK_CB(cb->skb).sk),
322 NETLINK_CB(cb->skb).portid, 311 NETLINK_CB(cb->skb).portid,
323 cb->nlh->nlmsg_seq, 0, cb->nlh) < 0) { 312 cb->nlh->nlmsg_seq, 0, cb->nlh) < 0) {
324 err = 2; 313 err = 1;
325 goto release; 314 goto release;
326 } 315 }
327next: 316next:
@@ -333,10 +322,35 @@ next:
333 cb->args[4] = 0; 322 cb->args[4] = 0;
334release: 323release:
335 release_sock(sk); 324 release_sock(sk);
325 sock_put(sk);
336 return err; 326 return err;
327}
328
329static int sctp_get_sock(struct sctp_transport *tsp, void *p)
330{
331 struct sctp_endpoint *ep = tsp->asoc->ep;
332 struct sctp_comm_param *commp = p;
333 struct sock *sk = ep->base.sk;
334 struct netlink_callback *cb = commp->cb;
335 const struct inet_diag_req_v2 *r = commp->r;
336 struct sctp_association *assoc =
337 list_entry(ep->asocs.next, struct sctp_association, asocs);
338
339 /* find the ep only once through the transports by this condition */
340 if (tsp->asoc != assoc)
341 goto out;
342
343 if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family)
344 goto out;
345
346 sock_hold(sk);
347 cb->args[5] = (long)sk;
348
349 return 1;
350
337out: 351out:
338 cb->args[2]++; 352 cb->args[2]++;
339 return err; 353 return 0;
340} 354}
341 355
342static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) 356static int sctp_ep_dump(struct sctp_endpoint *ep, void *p)
@@ -472,10 +486,18 @@ skip:
472 * 2 : to record the transport pos of this time's traversal 486 * 2 : to record the transport pos of this time's traversal
473 * 3 : to mark if we have dumped the ep info of the current asoc 487 * 3 : to mark if we have dumped the ep info of the current asoc
474 * 4 : to work as a temporary variable to traversal list 488 * 4 : to work as a temporary variable to traversal list
489 * 5 : to save the sk we get from travelsing the tsp list.
475 */ 490 */
476 if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) 491 if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)))
477 goto done; 492 goto done;
478 sctp_for_each_transport(sctp_tsp_dump, net, cb->args[2], &commp); 493
494next:
495 cb->args[5] = 0;
496 sctp_for_each_transport(sctp_get_sock, net, cb->args[2], &commp);
497
498 if (cb->args[5] && !sctp_sock_dump((struct sock *)cb->args[5], &commp))
499 goto next;
500
479done: 501done:
480 cb->args[1] = cb->args[4]; 502 cb->args[1] = cb->args[4];
481 cb->args[4] = 0; 503 cb->args[4] = 0;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 9fc417a8b476..8ed2d99bde6d 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4469,17 +4469,21 @@ int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
4469 const union sctp_addr *paddr, void *p) 4469 const union sctp_addr *paddr, void *p)
4470{ 4470{
4471 struct sctp_transport *transport; 4471 struct sctp_transport *transport;
4472 int err = 0; 4472 int err = -ENOENT;
4473 4473
4474 rcu_read_lock(); 4474 rcu_read_lock();
4475 transport = sctp_addrs_lookup_transport(net, laddr, paddr); 4475 transport = sctp_addrs_lookup_transport(net, laddr, paddr);
4476 if (!transport || !sctp_transport_hold(transport)) 4476 if (!transport || !sctp_transport_hold(transport))
4477 goto out; 4477 goto out;
4478 err = cb(transport, p); 4478
4479 sctp_association_hold(transport->asoc);
4479 sctp_transport_put(transport); 4480 sctp_transport_put(transport);
4480 4481
4481out:
4482 rcu_read_unlock(); 4482 rcu_read_unlock();
4483 err = cb(transport, p);
4484 sctp_association_put(transport->asoc);
4485
4486out:
4483 return err; 4487 return err;
4484} 4488}
4485EXPORT_SYMBOL_GPL(sctp_transport_lookup_process); 4489EXPORT_SYMBOL_GPL(sctp_transport_lookup_process);