forked from mirrors/linux
		
	tipc: fix shutdown() of connectionless socket
syzbot is reporting hung task at nbd_ioctl() [1], for there are two
problems regarding TIPC's connectionless socket's shutdown() operation.
----------
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/nbd.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
        const int fd = open("/dev/nbd0", 3);
        alarm(5);
        ioctl(fd, NBD_SET_SOCK, socket(PF_TIPC, SOCK_DGRAM, 0));
        ioctl(fd, NBD_DO_IT, 0); /* To be interrupted by SIGALRM. */
        return 0;
}
----------
One problem is that wait_for_completion() from flush_workqueue() from
nbd_start_device_ioctl() from nbd_ioctl() cannot be completed when
nbd_start_device_ioctl() received a signal at wait_event_interruptible(),
for tipc_shutdown() from kernel_sock_shutdown(SHUT_RDWR) from
nbd_mark_nsock_dead() from sock_shutdown() from nbd_start_device_ioctl()
is failing to wake up a WQ thread sleeping at wait_woken() from
tipc_wait_for_rcvmsg() from sock_recvmsg() from sock_xmit() from
nbd_read_stat() from recv_work() scheduled by nbd_start_device() from
nbd_start_device_ioctl(). Fix this problem by always invoking
sk->sk_state_change() (like inet_shutdown() does) when tipc_shutdown() is
called.
The other problem is that tipc_wait_for_rcvmsg() cannot return when
tipc_shutdown() is called, for tipc_shutdown() sets sk->sk_shutdown to
SEND_SHUTDOWN (despite "how" is SHUT_RDWR) while tipc_wait_for_rcvmsg()
needs sk->sk_shutdown set to RCV_SHUTDOWN or SHUTDOWN_MASK. Fix this
problem by setting sk->sk_shutdown to SHUTDOWN_MASK (like inet_shutdown()
does) when the socket is connectionless.
[1] https://syzkaller.appspot.com/bug?id=3fe51d307c1f0a845485cf1798aa059d12bf18b2
Reported-by: syzbot <syzbot+e36f41d207137b5d12f7@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									05d4487197
								
							
						
					
					
						commit
						2a63866c8b
					
				
					 1 changed files with 6 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -2771,18 +2771,21 @@ static int tipc_shutdown(struct socket *sock, int how)
 | 
			
		|||
 | 
			
		||||
	trace_tipc_sk_shutdown(sk, NULL, TIPC_DUMP_ALL, " ");
 | 
			
		||||
	__tipc_shutdown(sock, TIPC_CONN_SHUTDOWN);
 | 
			
		||||
	if (tipc_sk_type_connectionless(sk))
 | 
			
		||||
		sk->sk_shutdown = SHUTDOWN_MASK;
 | 
			
		||||
	else
 | 
			
		||||
		sk->sk_shutdown = SEND_SHUTDOWN;
 | 
			
		||||
 | 
			
		||||
	if (sk->sk_state == TIPC_DISCONNECTING) {
 | 
			
		||||
		/* Discard any unreceived messages */
 | 
			
		||||
		__skb_queue_purge(&sk->sk_receive_queue);
 | 
			
		||||
 | 
			
		||||
		/* Wake up anyone sleeping in poll */
 | 
			
		||||
		sk->sk_state_change(sk);
 | 
			
		||||
		res = 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		res = -ENOTCONN;
 | 
			
		||||
	}
 | 
			
		||||
	/* Wake up anyone sleeping in poll. */
 | 
			
		||||
	sk->sk_state_change(sk);
 | 
			
		||||
 | 
			
		||||
	release_sock(sk);
 | 
			
		||||
	return res;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue