diff options
Diffstat (limited to 'drivers/net/tun.c')
| -rw-r--r-- | drivers/net/tun.c | 39 | 
1 files changed, 31 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 95749006d687..a8ec589d1359 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -611,6 +611,14 @@ static void tun_queue_purge(struct tun_file *tfile)  	skb_queue_purge(&tfile->sk.sk_error_queue);  } +static void tun_cleanup_tx_array(struct tun_file *tfile) +{ +	if (tfile->tx_array.ring.queue) { +		skb_array_cleanup(&tfile->tx_array); +		memset(&tfile->tx_array, 0, sizeof(tfile->tx_array)); +	} +} +  static void __tun_detach(struct tun_file *tfile, bool clean)  {  	struct tun_file *ntfile; @@ -657,8 +665,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean)  			    tun->dev->reg_state == NETREG_REGISTERED)  				unregister_netdevice(tun->dev);  		} -		if (tun) -			skb_array_cleanup(&tfile->tx_array); +		tun_cleanup_tx_array(tfile);  		sock_put(&tfile->sk);  	}  } @@ -700,11 +707,13 @@ static void tun_detach_all(struct net_device *dev)  		/* Drop read queue */  		tun_queue_purge(tfile);  		sock_put(&tfile->sk); +		tun_cleanup_tx_array(tfile);  	}  	list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) {  		tun_enable_queue(tfile);  		tun_queue_purge(tfile);  		sock_put(&tfile->sk); +		tun_cleanup_tx_array(tfile);  	}  	BUG_ON(tun->numdisabled != 0); @@ -1952,8 +1961,11 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,  	tun_debug(KERN_INFO, tun, "tun_do_read\n"); -	if (!iov_iter_count(to)) +	if (!iov_iter_count(to)) { +		if (skb) +			kfree_skb(skb);  		return 0; +	}  	if (!skb) {  		/* Read frames from ring */ @@ -2069,22 +2081,24 @@ static int tun_recvmsg(struct socket *sock, struct msghdr *m, size_t total_len,  {  	struct tun_file *tfile = container_of(sock, struct tun_file, socket);  	struct tun_struct *tun = tun_get(tfile); +	struct sk_buff *skb = m->msg_control;  	int ret; -	if (!tun) -		return -EBADFD; +	if (!tun) { +		ret = -EBADFD; +		goto out_free_skb; +	}  	if (flags & ~(MSG_DONTWAIT|MSG_TRUNC|MSG_ERRQUEUE)) {  		ret = -EINVAL; -		goto out; +		goto out_put_tun;  	}  	if (flags & MSG_ERRQUEUE) {  		ret = sock_recv_errqueue(sock->sk, m, total_len,  					 SOL_PACKET, TUN_TX_TIMESTAMP);  		goto out;  	} -	ret = tun_do_read(tun, tfile, &m->msg_iter, flags & MSG_DONTWAIT, -			  m->msg_control); +	ret = tun_do_read(tun, tfile, &m->msg_iter, flags & MSG_DONTWAIT, skb);  	if (ret > (ssize_t)total_len) {  		m->msg_flags |= MSG_TRUNC;  		ret = flags & MSG_TRUNC ? ret : total_len; @@ -2092,6 +2106,13 @@ static int tun_recvmsg(struct socket *sock, struct msghdr *m, size_t total_len,  out:  	tun_put(tun);  	return ret; + +out_put_tun: +	tun_put(tun); +out_free_skb: +	if (skb) +		kfree_skb(skb); +	return ret;  }  static int tun_peek_len(struct socket *sock) @@ -2839,6 +2860,8 @@ static int tun_chr_open(struct inode *inode, struct file * file)  	sock_set_flag(&tfile->sk, SOCK_ZEROCOPY); +	memset(&tfile->tx_array, 0, sizeof(tfile->tx_array)); +  	return 0;  }  | 
