diff options
author | Al Viro <viro@ZenIV.linux.org.uk> | 2017-12-05 18:27:57 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-12-05 18:39:28 -0500 |
commit | a5739435b5a3b8c449f8844ecd71a3b1e89f0a33 (patch) | |
tree | b440fa1710f0e7c3779461cb1d851596d5b11ac5 | |
parent | 69c64866ce072dea1d1e59a0d61e0f66c0dffb76 (diff) |
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>
-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 = { | |||
1625 | }; | 1625 | }; |
1626 | 1626 | ||
1627 | /* Clone a kcm socket. */ | 1627 | /* Clone a kcm socket. */ |
1628 | static int kcm_clone(struct socket *osock, struct kcm_clone *info, | 1628 | static struct file *kcm_clone(struct socket *osock) |
1629 | struct socket **newsockp) | ||
1630 | { | 1629 | { |
1631 | struct socket *newsock; | 1630 | struct socket *newsock; |
1632 | struct sock *newsk; | 1631 | struct sock *newsk; |
1633 | struct file *newfile; | 1632 | struct file *file; |
1634 | int err, newfd; | ||
1635 | 1633 | ||
1636 | err = -ENFILE; | ||
1637 | newsock = sock_alloc(); | 1634 | newsock = sock_alloc(); |
1638 | if (!newsock) | 1635 | if (!newsock) |
1639 | goto out; | 1636 | return ERR_PTR(-ENFILE); |
1640 | 1637 | ||
1641 | newsock->type = osock->type; | 1638 | newsock->type = osock->type; |
1642 | newsock->ops = osock->ops; | 1639 | newsock->ops = osock->ops; |
1643 | 1640 | ||
1644 | __module_get(newsock->ops->owner); | 1641 | __module_get(newsock->ops->owner); |
1645 | 1642 | ||
1646 | newfd = get_unused_fd_flags(0); | ||
1647 | if (unlikely(newfd < 0)) { | ||
1648 | err = newfd; | ||
1649 | goto out_fd_fail; | ||
1650 | } | ||
1651 | |||
1652 | newfile = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name); | ||
1653 | if (IS_ERR(newfile)) { | ||
1654 | err = PTR_ERR(newfile); | ||
1655 | goto out_sock_alloc_fail; | ||
1656 | } | ||
1657 | |||
1658 | newsk = sk_alloc(sock_net(osock->sk), PF_KCM, GFP_KERNEL, | 1643 | newsk = sk_alloc(sock_net(osock->sk), PF_KCM, GFP_KERNEL, |
1659 | &kcm_proto, true); | 1644 | &kcm_proto, true); |
1660 | if (!newsk) { | 1645 | if (!newsk) { |
1661 | err = -ENOMEM; | 1646 | sock_release(newsock); |
1662 | goto out_sk_alloc_fail; | 1647 | return ERR_PTR(-ENOMEM); |
1663 | } | 1648 | } |
1664 | |||
1665 | sock_init_data(newsock, newsk); | 1649 | sock_init_data(newsock, newsk); |
1666 | init_kcm_sock(kcm_sk(newsk), kcm_sk(osock->sk)->mux); | 1650 | init_kcm_sock(kcm_sk(newsk), kcm_sk(osock->sk)->mux); |
1667 | 1651 | ||
1668 | fd_install(newfd, newfile); | 1652 | file = sock_alloc_file(newsock, 0, osock->sk->sk_prot_creator->name); |
1669 | *newsockp = newsock; | 1653 | if (IS_ERR(file)) |
1670 | info->fd = newfd; | 1654 | sock_release(newsock); |
1671 | |||
1672 | return 0; | ||
1673 | 1655 | ||
1674 | out_sk_alloc_fail: | 1656 | return file; |
1675 | fput(newfile); | ||
1676 | out_sock_alloc_fail: | ||
1677 | put_unused_fd(newfd); | ||
1678 | out_fd_fail: | ||
1679 | sock_release(newsock); | ||
1680 | out: | ||
1681 | return err; | ||
1682 | } | 1657 | } |
1683 | 1658 | ||
1684 | static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | 1659 | 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 | } | 1683 | } |
1709 | case SIOCKCMCLONE: { | 1684 | case SIOCKCMCLONE: { |
1710 | struct kcm_clone info; | 1685 | struct kcm_clone info; |
1711 | struct socket *newsock = NULL; | 1686 | struct file *file; |
1712 | 1687 | ||
1713 | err = kcm_clone(sock, &info, &newsock); | 1688 | info.fd = get_unused_fd_flags(0); |
1714 | if (!err) { | 1689 | if (unlikely(info.fd < 0)) |
1715 | if (copy_to_user((void __user *)arg, &info, | 1690 | return info.fd; |
1716 | sizeof(info))) { | ||
1717 | err = -EFAULT; | ||
1718 | sys_close(info.fd); | ||
1719 | } | ||
1720 | } | ||
1721 | 1691 | ||
1692 | file = kcm_clone(sock); | ||
1693 | if (IS_ERR(file)) { | ||
1694 | put_unused_fd(info.fd); | ||
1695 | return PTR_ERR(file); | ||
1696 | } | ||
1697 | if (copy_to_user((void __user *)arg, &info, | ||
1698 | sizeof(info))) { | ||
1699 | put_unused_fd(info.fd); | ||
1700 | fput(file); | ||
1701 | return -EFAULT; | ||
1702 | } | ||
1703 | fd_install(info.fd, file); | ||
1704 | err = 0; | ||
1722 | break; | 1705 | break; |
1723 | } | 1706 | } |
1724 | default: | 1707 | default: |