diff options
author | Ying Xue <ying.xue@windriver.com> | 2013-12-26 21:18:28 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-12-29 22:24:07 -0500 |
commit | 84602761ca4495dd409be936dfa93ed20c946684 (patch) | |
tree | 5936e501cc24cc66cf298a8e6713cbb04b4427b0 /net/tipc/socket.c | |
parent | 8eb9bff0edefcce50116ec50397a60dd626022d6 (diff) |
tipc: fix deadlock during socket release
A deadlock might occur if name table is withdrawn in socket release
routine, and while packets are still being received from bearer.
CPU0 CPU1
T0: recv_msg() release()
T1: tipc_recv_msg() tipc_withdraw()
T2: [grab node lock] [grab port lock]
T3: tipc_link_wakeup_ports() tipc_nametbl_withdraw()
T4: [grab port lock]* named_cluster_distribute()
T5: wakeupdispatch() tipc_link_send()
T6: [grab node lock]*
The opposite order of holding port lock and node lock on above two
different paths may result in a deadlock. If socket lock instead of
port lock is used to protect port instance in tipc_withdraw(), the
reverse order of holding port lock and node lock will be eliminated,
as a result, the deadlock is killed as well.
Reported-by: Lars Everbrand <lars.everbrand@ericsson.com>
Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Signed-off-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r-- | net/tipc/socket.c | 46 |
1 files changed, 31 insertions, 15 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 3b61851bb927..e741416d1d24 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
@@ -354,7 +354,7 @@ static int release(struct socket *sock) | |||
354 | * Delete TIPC port; this ensures no more messages are queued | 354 | * Delete TIPC port; this ensures no more messages are queued |
355 | * (also disconnects an active connection & sends a 'FIN-' to peer) | 355 | * (also disconnects an active connection & sends a 'FIN-' to peer) |
356 | */ | 356 | */ |
357 | res = tipc_deleteport(tport->ref); | 357 | res = tipc_deleteport(tport); |
358 | 358 | ||
359 | /* Discard any remaining (connection-based) messages in receive queue */ | 359 | /* Discard any remaining (connection-based) messages in receive queue */ |
360 | __skb_queue_purge(&sk->sk_receive_queue); | 360 | __skb_queue_purge(&sk->sk_receive_queue); |
@@ -386,30 +386,46 @@ static int release(struct socket *sock) | |||
386 | */ | 386 | */ |
387 | static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len) | 387 | static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len) |
388 | { | 388 | { |
389 | struct sock *sk = sock->sk; | ||
389 | struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; | 390 | struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; |
390 | u32 portref = tipc_sk_port(sock->sk)->ref; | 391 | struct tipc_port *tport = tipc_sk_port(sock->sk); |
392 | int res = -EINVAL; | ||
391 | 393 | ||
392 | if (unlikely(!uaddr_len)) | 394 | lock_sock(sk); |
393 | return tipc_withdraw(portref, 0, NULL); | 395 | if (unlikely(!uaddr_len)) { |
396 | res = tipc_withdraw(tport, 0, NULL); | ||
397 | goto exit; | ||
398 | } | ||
394 | 399 | ||
395 | if (uaddr_len < sizeof(struct sockaddr_tipc)) | 400 | if (uaddr_len < sizeof(struct sockaddr_tipc)) { |
396 | return -EINVAL; | 401 | res = -EINVAL; |
397 | if (addr->family != AF_TIPC) | 402 | goto exit; |
398 | return -EAFNOSUPPORT; | 403 | } |
404 | if (addr->family != AF_TIPC) { | ||
405 | res = -EAFNOSUPPORT; | ||
406 | goto exit; | ||
407 | } | ||
399 | 408 | ||
400 | if (addr->addrtype == TIPC_ADDR_NAME) | 409 | if (addr->addrtype == TIPC_ADDR_NAME) |
401 | addr->addr.nameseq.upper = addr->addr.nameseq.lower; | 410 | addr->addr.nameseq.upper = addr->addr.nameseq.lower; |
402 | else if (addr->addrtype != TIPC_ADDR_NAMESEQ) | 411 | else if (addr->addrtype != TIPC_ADDR_NAMESEQ) { |
403 | return -EAFNOSUPPORT; | 412 | res = -EAFNOSUPPORT; |
413 | goto exit; | ||
414 | } | ||
404 | 415 | ||
405 | if ((addr->addr.nameseq.type < TIPC_RESERVED_TYPES) && | 416 | if ((addr->addr.nameseq.type < TIPC_RESERVED_TYPES) && |
406 | (addr->addr.nameseq.type != TIPC_TOP_SRV) && | 417 | (addr->addr.nameseq.type != TIPC_TOP_SRV) && |
407 | (addr->addr.nameseq.type != TIPC_CFG_SRV)) | 418 | (addr->addr.nameseq.type != TIPC_CFG_SRV)) { |
408 | return -EACCES; | 419 | res = -EACCES; |
420 | goto exit; | ||
421 | } | ||
409 | 422 | ||
410 | return (addr->scope > 0) ? | 423 | res = (addr->scope > 0) ? |
411 | tipc_publish(portref, addr->scope, &addr->addr.nameseq) : | 424 | tipc_publish(tport, addr->scope, &addr->addr.nameseq) : |
412 | tipc_withdraw(portref, -addr->scope, &addr->addr.nameseq); | 425 | tipc_withdraw(tport, -addr->scope, &addr->addr.nameseq); |
426 | exit: | ||
427 | release_sock(sk); | ||
428 | return res; | ||
413 | } | 429 | } |
414 | 430 | ||
415 | /** | 431 | /** |