unexport sock_map_fd(), switch to sock_alloc_file()
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>
diff --git a/include/linux/net.h b/include/linux/net.h
index 99276c3..c8a9708 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -65,6 +65,7 @@
struct poll_table_struct;
struct pipe_inode_info;
struct inode;
+struct file;
struct net;
#define SOCK_ASYNC_NOSPACE 0
@@ -246,7 +247,7 @@
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)
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index 6449bae..8c4e0b5 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -793,30 +793,28 @@
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);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 5e25981..fb5931c 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -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 @@
{
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 @@
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. */
- peeloff.sd = retval;
- if (put_user(len, optlen))
+ if (put_user(len, optlen)) {
+ fput(newfile);
+ put_unused_fd(retval);
return -EFAULT;
- if (copy_to_user(optval, &peeloff, len))
- retval = -EFAULT;
-
+ }
+ peeloff.sd = retval;
+ if (copy_to_user(optval, &peeloff, len)) {
+ fput(newfile);
+ put_unused_fd(retval);
+ return -EFAULT;
+ }
+ fd_install(retval, newfile);
out:
return retval;
}
diff --git a/net/socket.c b/net/socket.c
index a14ec19..38a1431 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -346,7 +346,7 @@
* 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 @@
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 @@
put_unused_fd(fd);
return PTR_ERR(newfile);
}
-EXPORT_SYMBOL(sock_map_fd);
struct socket *sock_from_file(struct file *file, int *err)
{