diff options
| author | Al Viro <viro@ZenIV.linux.org.uk> | 2017-12-05 23:27:57 +0000 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-12-05 18:39:28 -0500 | 
| commit | a5739435b5a3b8c449f8844ecd71a3b1e89f0a33 (patch) | |
| tree | b440fa1710f0e7c3779461cb1d851596d5b11ac5 /net/kcm | |
| parent | 69c64866ce072dea1d1e59a0d61e0f66c0dffb76 (diff) | |
| download | linux-a5739435b5a3b8c449f8844ecd71a3b1e89f0a33.tar.bz2 | |
fix kcm_clone()
1) it's fput() or sock_release(), not both
2) don't do fd_install() until the last failure exit.
3) not a bug per se, but... don't attach socket to struct file
   until it's set up.
Take reserving descriptor into the caller, move fd_install() to the
caller, sanitize failure exits and calling conventions.
Cc: stable@vger.kernel.org # v4.6+
Acked-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/kcm')
| -rw-r--r-- | net/kcm/kcmsock.c | 71 | 
1 files changed, 27 insertions, 44 deletions
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 0b750a22c4b9..c5fa634e63ca 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1625,60 +1625,35 @@ static struct proto kcm_proto = {  };  /* Clone a kcm socket. */ -static int kcm_clone(struct socket *osock, struct kcm_clone *info, -		     struct socket **newsockp) +static struct file *kcm_clone(struct socket *osock)  {  	struct socket *newsock;  	struct sock *newsk; -	struct file *newfile; -	int err, newfd; +	struct file *file; -	err = -ENFILE;  	newsock = sock_alloc();  	if (!newsock) -		goto out; +		return ERR_PTR(-ENFILE);  	newsock->type = osock->type;  	newsock->ops = osock->ops;  	__module_get(newsock->ops->owner); -	newfd = get_unused_fd_flags(0); -	if (unlikely(newfd < 0)) { -		err = newfd; -		goto out_fd_fail; -	} - -	newfile = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name); -	if (IS_ERR(newfile)) { -		err = PTR_ERR(newfile); -		goto out_sock_alloc_fail; -	} -  	newsk = sk_alloc(sock_net(osock->sk), PF_KCM, GFP_KERNEL,  			 &kcm_proto, true);  	if (!newsk) { -		err = -ENOMEM; -		goto out_sk_alloc_fail; +		sock_release(newsock); +		return ERR_PTR(-ENOMEM);  	} -  	sock_init_data(newsock, newsk);  	init_kcm_sock(kcm_sk(newsk), kcm_sk(osock->sk)->mux); -	fd_install(newfd, newfile); -	*newsockp = newsock; -	info->fd = newfd; - -	return 0; +	file = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name); +	if (IS_ERR(file)) +		sock_release(newsock); -out_sk_alloc_fail: -	fput(newfile); -out_sock_alloc_fail: -	put_unused_fd(newfd); -out_fd_fail: -	sock_release(newsock); -out: -	return err; +	return file;  }  static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) @@ -1708,17 +1683,25 @@ static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)  	}  	case SIOCKCMCLONE: {  		struct kcm_clone info; -		struct socket *newsock = NULL; - -		err = kcm_clone(sock, &info, &newsock); -		if (!err) { -			if (copy_to_user((void __user *)arg, &info, -					 sizeof(info))) { -				err = -EFAULT; -				sys_close(info.fd); -			} -		} +		struct file *file; + +		info.fd = get_unused_fd_flags(0); +		if (unlikely(info.fd < 0)) +			return info.fd; +		file = kcm_clone(sock); +		if (IS_ERR(file)) { +			put_unused_fd(info.fd); +			return PTR_ERR(file); +		} +		if (copy_to_user((void __user *)arg, &info, +				 sizeof(info))) { +			put_unused_fd(info.fd); +			fput(file); +			return -EFAULT; +		} +		fd_install(info.fd, file); +		err = 0;  		break;  	}  	default:  |