Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+27 −44
Original line number Diff line number Diff line
@@ -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;

out_sk_alloc_fail:
	fput(newfile);
out_sock_alloc_fail:
	put_unused_fd(newfd);
out_fd_fail:
	file = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name);
	if (IS_ERR(file))
		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;
		struct file *file;

		err = kcm_clone(sock, &info, &newsock);
		if (!err) {
		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))) {
				err = -EFAULT;
				sys_close(info.fd);
			}
			put_unused_fd(info.fd);
			fput(file);
			return -EFAULT;
		}

		fd_install(info.fd, file);
		err = 0;
		break;
	}
	default: