summaryrefslogtreecommitdiffstats
path: root/net/unix/af_unix.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2008-11-24 19:54:37 +0100
committerThomas Gleixner <tglx@linutronix.de>2008-11-24 19:54:37 +0100
commit3e1d7a6219ab64e13b10b1a77c0625db9a8bd8db (patch)
treec682da7317845d7b1336e3d8498cf83bdf8f5900 /net/unix/af_unix.c
parent42569c39917a08e8de1e8b5685463be7b74baebd (diff)
parent13d428afc007fcfcd6deeb215618f54cf9c0cae6 (diff)
Merge branch 'linus' into core/futexes
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r--net/unix/af_unix.c53
1 files changed, 36 insertions, 17 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 015606b54d9..eb90f77bb0e 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1,7 +1,7 @@
/*
* NET4: Implementation of BSD Unix domain sockets.
*
- * Authors: Alan Cox, <alan.cox@linux.org>
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -711,28 +711,30 @@ static struct sock *unix_find_other(struct net *net,
int type, unsigned hash, int *error)
{
struct sock *u;
- struct nameidata nd;
+ struct path path;
int err = 0;
if (sunname->sun_path[0]) {
- err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd);
+ struct inode *inode;
+ err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path);
if (err)
goto fail;
- err = vfs_permission(&nd, MAY_WRITE);
+ inode = path.dentry->d_inode;
+ err = inode_permission(inode, MAY_WRITE);
if (err)
goto put_fail;
err = -ECONNREFUSED;
- if (!S_ISSOCK(nd.path.dentry->d_inode->i_mode))
+ if (!S_ISSOCK(inode->i_mode))
goto put_fail;
- u = unix_find_socket_byinode(net, nd.path.dentry->d_inode);
+ u = unix_find_socket_byinode(net, inode);
if (!u)
goto put_fail;
if (u->sk_type == type)
- touch_atime(nd.path.mnt, nd.path.dentry);
+ touch_atime(path.mnt, path.dentry);
- path_put(&nd.path);
+ path_put(&path);
err=-EPROTOTYPE;
if (u->sk_type != type) {
@@ -753,7 +755,7 @@ static struct sock *unix_find_other(struct net *net,
return u;
put_fail:
- path_put(&nd.path);
+ path_put(&path);
fail:
*error=err;
return NULL;
@@ -1300,14 +1302,23 @@ static void unix_destruct_fds(struct sk_buff *skb)
sock_wfree(skb);
}
-static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
int i;
+
+ /*
+ * Need to duplicate file references for the sake of garbage
+ * collection. Otherwise a socket in the fps might become a
+ * candidate for GC while the skb is not yet queued.
+ */
+ UNIXCB(skb).fp = scm_fp_dup(scm->fp);
+ if (!UNIXCB(skb).fp)
+ return -ENOMEM;
+
for (i=scm->fp->count-1; i>=0; i--)
unix_inflight(scm->fp->fp[i]);
- UNIXCB(skb).fp = scm->fp;
skb->destructor = unix_destruct_fds;
- scm->fp = NULL;
+ return 0;
}
/*
@@ -1366,8 +1377,11 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto out;
memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
- if (siocb->scm->fp)
- unix_attach_fds(siocb->scm, skb);
+ if (siocb->scm->fp) {
+ err = unix_attach_fds(siocb->scm, skb);
+ if (err)
+ goto out_free;
+ }
unix_get_secdata(siocb->scm, skb);
skb_reset_transport_header(skb);
@@ -1536,8 +1550,13 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
size = min_t(int, size, skb_tailroom(skb));
memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
- if (siocb->scm->fp)
- unix_attach_fds(siocb->scm, skb);
+ if (siocb->scm->fp) {
+ err = unix_attach_fds(siocb->scm, skb);
+ if (err) {
+ kfree_skb(skb);
+ goto out_err;
+ }
+ }
if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) {
kfree_skb(skb);
@@ -2211,7 +2230,7 @@ static int unix_net_init(struct net *net)
#endif
error = 0;
out:
- return 0;
+ return error;
}
static void unix_net_exit(struct net *net)