4.4-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Miklos Szeredi <mszer...@redhat.com>

commit eb0a4a47ae89aaa0674ab3180de6a162f3be2ddf upstream.

Overlayfs uses separate inodes even in the case of hard links on the
underlying filesystems.  This is a problem for AF_UNIX socket
implementation which indexes sockets based on the inode.  This resulted in
hard linked sockets not working.

The fix is to use the real, underlying inode.

Test case follows:

-- ovl-sock-test.c --
#include <unistd.h>
#include <err.h>
#include <sys/socket.h>
#include <sys/un.h>

#define SOCK "test-sock"
#define SOCK2 "test-sock2"

int main(void)
{
        int fd, fd2;
        struct sockaddr_un addr = {
                .sun_family = AF_UNIX,
                .sun_path = SOCK,
        };
        struct sockaddr_un addr2 = {
                .sun_family = AF_UNIX,
                .sun_path = SOCK2,
        };

        unlink(SOCK);
        unlink(SOCK2);
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
                err(1, "socket");
        if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
                err(1, "bind");
        if (listen(fd, 0) == -1)
                err(1, "listen");
        if (link(SOCK, SOCK2) == -1)
                err(1, "link");
        if ((fd2 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
                err(1, "socket");
        if (connect(fd2, (struct sockaddr *) &addr2, sizeof(addr2)) == -1)
                err (1, "connect");
        return 0;
}
----

Reported-by: Alexander Morozov <alexandr.moro...@docker.com>
Signed-off-by: Miklos Szeredi <mszer...@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 net/unix/af_unix.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -315,7 +315,7 @@ static struct sock *unix_find_socket_byi
                    &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {
                struct dentry *dentry = unix_sk(s)->path.dentry;
 
-               if (dentry && d_backing_inode(dentry) == i) {
+               if (dentry && d_real_inode(dentry) == i) {
                        sock_hold(s);
                        goto found;
                }
@@ -911,7 +911,7 @@ static struct sock *unix_find_other(stru
                err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path);
                if (err)
                        goto fail;
-               inode = d_backing_inode(path.dentry);
+               inode = d_real_inode(path.dentry);
                err = inode_permission(inode, MAY_WRITE);
                if (err)
                        goto put_fail;
@@ -1048,7 +1048,7 @@ static int unix_bind(struct socket *sock
                        goto out_up;
                }
                addr->hash = UNIX_HASH_SIZE;
-               hash = d_backing_inode(dentry)->i_ino & (UNIX_HASH_SIZE - 1);
+               hash = d_real_inode(dentry)->i_ino & (UNIX_HASH_SIZE - 1);
                spin_lock(&unix_table_lock);
                u->path = u_path;
                list = &unix_socket_table[hash];


Reply via email to