| From eb0a4a47ae89aaa0674ab3180de6a162f3be2ddf Mon Sep 17 00:00:00 2001 |
| From: Miklos Szeredi <mszeredi@redhat.com> |
| Date: Fri, 20 May 2016 22:13:45 +0200 |
| Subject: af_unix: fix hard linked sockets on overlay |
| |
| From: Miklos Szeredi <mszeredi@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.morozov@docker.com> |
| Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@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]; |