Commit a5739435 authored by Al Viro's avatar Al Viro Committed by David S. Miller

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: default avatarTom Herbert <tom@herbertland.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 69c64866
...@@ -1625,60 +1625,35 @@ static struct proto kcm_proto = { ...@@ -1625,60 +1625,35 @@ static struct proto kcm_proto = {
}; };
/* Clone a kcm socket. */ /* Clone a kcm socket. */
static int kcm_clone(struct socket *osock, struct kcm_clone *info, static struct file *kcm_clone(struct socket *osock)
struct socket **newsockp)
{ {
struct socket *newsock; struct socket *newsock;
struct sock *newsk; struct sock *newsk;
struct file *newfile; struct file *file;
int err, newfd;
err = -ENFILE;
newsock = sock_alloc(); newsock = sock_alloc();
if (!newsock) if (!newsock)
goto out; return ERR_PTR(-ENFILE);
newsock->type = osock->type; newsock->type = osock->type;
newsock->ops = osock->ops; newsock->ops = osock->ops;
__module_get(newsock->ops->owner); __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, newsk = sk_alloc(sock_net(osock->sk), PF_KCM, GFP_KERNEL,
&kcm_proto, true); &kcm_proto, true);
if (!newsk) { if (!newsk) {
err = -ENOMEM; sock_release(newsock);
goto out_sk_alloc_fail; return ERR_PTR(-ENOMEM);
} }
sock_init_data(newsock, newsk); sock_init_data(newsock, newsk);
init_kcm_sock(kcm_sk(newsk), kcm_sk(osock->sk)->mux); init_kcm_sock(kcm_sk(newsk), kcm_sk(osock->sk)->mux);
fd_install(newfd, newfile); file = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name);
*newsockp = newsock; if (IS_ERR(file))
info->fd = newfd; sock_release(newsock);
return 0;
out_sk_alloc_fail: return file;
fput(newfile);
out_sock_alloc_fail:
put_unused_fd(newfd);
out_fd_fail:
sock_release(newsock);
out:
return err;
} }
static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) 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) ...@@ -1708,17 +1683,25 @@ static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
} }
case SIOCKCMCLONE: { case SIOCKCMCLONE: {
struct kcm_clone info; struct kcm_clone info;
struct socket *newsock = NULL; struct file *file;
err = kcm_clone(sock, &info, &newsock); info.fd = get_unused_fd_flags(0);
if (!err) { if (unlikely(info.fd < 0))
if (copy_to_user((void __user *)arg, &info, return info.fd;
sizeof(info))) {
err = -EFAULT;
sys_close(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; break;
} }
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment