unexport sock_map_fd(), switch to sock_alloc_file()
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 18 Aug 2012 04:25:51 +0000 (00:25 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 27 Sep 2012 01:08:50 +0000 (21:08 -0400)
Both modular callers of sock_map_fd() had been buggy; sctp one leaks
descriptor and file if copy_to_user() fails, 9p one shouldn't be
exposing file in the descriptor table at all.

Switch both to sock_alloc_file(), export it, unexport sock_map_fd() and
make it static.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
include/linux/net.h
net/9p/trans_fd.c
net/sctp/socket.c
net/socket.c

index 99276c3dc89aef2f3d2014ca87b427877247d6f8..c8a9708d4d6604199478aba8511360ae07419208 100644 (file)
@@ -65,6 +65,7 @@ typedef enum {
 struct poll_table_struct;
 struct pipe_inode_info;
 struct inode;
+struct file;
 struct net;
 
 #define SOCK_ASYNC_NOSPACE     0
@@ -246,7 +247,7 @@ extern int               sock_sendmsg(struct socket *sock, struct msghdr *msg,
                                  size_t len);
 extern int          sock_recvmsg(struct socket *sock, struct msghdr *msg,
                                  size_t size, int flags);
-extern int          sock_map_fd(struct socket *sock, int flags);
+extern struct file  *sock_alloc_file(struct socket *sock, int flags);
 extern struct socket *sockfd_lookup(int fd, int *err);
 extern struct socket *sock_from_file(struct file *file, int *err);
 #define                     sockfd_put(sock) fput(sock->file)
index 6449bae157023c182de306d862a6c0a11f0b04e9..8c4e0b538a8a1b354d950ce6c018b515431e9fde 100644 (file)
@@ -793,30 +793,28 @@ static int p9_fd_open(struct p9_client *client, int rfd, int wfd)
 static int p9_socket_open(struct p9_client *client, struct socket *csocket)
 {
        struct p9_trans_fd *p;
-       int ret, fd;
+       struct file *file;
+       int ret;
 
        p = kmalloc(sizeof(struct p9_trans_fd), GFP_KERNEL);
        if (!p)
                return -ENOMEM;
 
        csocket->sk->sk_allocation = GFP_NOIO;
-       fd = sock_map_fd(csocket, 0);
-       if (fd < 0) {
+       file = sock_alloc_file(csocket, 0);
+       if (IS_ERR(file)) {
                pr_err("%s (%d): failed to map fd\n",
                       __func__, task_pid_nr(current));
                sock_release(csocket);
                kfree(p);
-               return fd;
+               return PTR_ERR(file);
        }
 
-       get_file(csocket->file);
-       get_file(csocket->file);
-       p->wr = p->rd = csocket->file;
+       get_file(file);
+       p->wr = p->rd = file;
        client->trans = p;
        client->status = Connected;
 
-       sys_close(fd);  /* still racy */
-
        p->rd->f_flags |= O_NONBLOCK;
 
        p->conn = p9_conn_create(client);
index 5e259817a7f34cd4a183139fe9c4bf5ee2ab6689..fb5931ca50d0fc4aad0ac49d60b7591d981ea30f 100644 (file)
@@ -70,6 +70,7 @@
 #include <linux/init.h>
 #include <linux/crypto.h>
 #include <linux/slab.h>
+#include <linux/file.h>
 
 #include <net/ip.h>
 #include <net/icmp.h>
@@ -4276,6 +4277,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
 {
        sctp_peeloff_arg_t peeloff;
        struct socket *newsock;
+       struct file *newfile;
        int retval = 0;
 
        if (len < sizeof(sctp_peeloff_arg_t))
@@ -4289,22 +4291,35 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
                goto out;
 
        /* Map the socket to an unused fd that can be returned to the user.  */
-       retval = sock_map_fd(newsock, 0);
+       retval = get_unused_fd();
        if (retval < 0) {
                sock_release(newsock);
                goto out;
        }
 
+       newfile = sock_alloc_file(newsock, 0);
+       if (unlikely(IS_ERR(newfile))) {
+               put_unused_fd(retval);
+               sock_release(newsock);
+               return PTR_ERR(newfile);
+       }
+
        SCTP_DEBUG_PRINTK("%s: sk: %p newsk: %p sd: %d\n",
                          __func__, sk, newsock->sk, retval);
 
        /* Return the fd mapped to the new socket.  */
+       if (put_user(len, optlen)) {
+               fput(newfile);
+               put_unused_fd(retval);
+               return -EFAULT;
+       }
        peeloff.sd = retval;
-       if (put_user(len, optlen))
+       if (copy_to_user(optval, &peeloff, len)) {
+               fput(newfile);
+               put_unused_fd(retval);
                return -EFAULT;
-       if (copy_to_user(optval, &peeloff, len))
-               retval = -EFAULT;
-
+       }
+       fd_install(retval, newfile);
 out:
        return retval;
 }
index a14ec19164b6ec48c83cc61c1de6004c84887c4e..38a14311f3a6ffc0f21a2500a826f39b4ec0271b 100644 (file)
@@ -346,7 +346,7 @@ static struct file_system_type sock_fs_type = {
  *     but we take care of internal coherence yet.
  */
 
-static struct file *sock_alloc_file(struct socket *sock, int flags)
+struct file *sock_alloc_file(struct socket *sock, int flags)
 {
        struct qstr name = { .name = "" };
        struct path path;
@@ -375,8 +375,9 @@ static struct file *sock_alloc_file(struct socket *sock, int flags)
        file->private_data = sock;
        return file;
 }
+EXPORT_SYMBOL(sock_alloc_file);
 
-int sock_map_fd(struct socket *sock, int flags)
+static int sock_map_fd(struct socket *sock, int flags)
 {
        struct file *newfile;
        int fd = get_unused_fd_flags(flags);
@@ -392,7 +393,6 @@ int sock_map_fd(struct socket *sock, int flags)
        put_unused_fd(fd);
        return PTR_ERR(newfile);
 }
-EXPORT_SYMBOL(sock_map_fd);
 
 struct socket *sock_from_file(struct file *file, int *err)
 {