2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* NET4: Implementation of BSD Unix domain sockets.
|
|
|
|
*
|
2008-10-14 10:01:08 +08:00
|
|
|
* Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Fixes:
|
|
|
|
* Linus Torvalds : Assorted bug cures.
|
|
|
|
* Niibe Yutaka : async I/O support.
|
|
|
|
* Carsten Paeth : PF_UNIX check, address fixes.
|
|
|
|
* Alan Cox : Limit size of allocated blocks.
|
|
|
|
* Alan Cox : Fixed the stupid socketpair bug.
|
|
|
|
* Alan Cox : BSD compatibility fine tuning.
|
|
|
|
* Alan Cox : Fixed a bug in connect when interrupted.
|
|
|
|
* Alan Cox : Sorted out a proper draft version of
|
|
|
|
* file descriptor passing hacked up from
|
|
|
|
* Mike Shaver's work.
|
|
|
|
* Marty Leisner : Fixes to fd passing
|
|
|
|
* Nick Nevin : recvmsg bugfix.
|
|
|
|
* Alan Cox : Started proper garbage collector
|
|
|
|
* Heiko EiBfeldt : Missing verify_area check
|
|
|
|
* Alan Cox : Started POSIXisms
|
|
|
|
* Andreas Schwab : Replace inode by dentry for proper
|
|
|
|
* reference counting
|
|
|
|
* Kirk Petersen : Made this a module
|
|
|
|
* Christoph Rohland : Elegant non-blocking accept/connect algorithm.
|
|
|
|
* Lots of bug fixes.
|
|
|
|
* Alexey Kuznetosv : Repaired (I hope) bugs introduces
|
|
|
|
* by above two patches.
|
|
|
|
* Andrea Arcangeli : If possible we block in connect(2)
|
|
|
|
* if the max backlog of the listen socket
|
|
|
|
* is been reached. This won't break
|
|
|
|
* old apps and it will avoid huge amount
|
|
|
|
* of socks hashed (this for unix_gc()
|
|
|
|
* performances reasons).
|
|
|
|
* Security fix that limits the max
|
|
|
|
* number of socks to 2*max_files and
|
|
|
|
* the number of skb queueable in the
|
|
|
|
* dgram receiver.
|
|
|
|
* Artur Skawina : Hash function optimizations
|
|
|
|
* Alexey Kuznetsov : Full scale SMP. Lot of bugs are introduced 8)
|
|
|
|
* Malcolm Beattie : Set peercred for socketpair
|
|
|
|
* Michal Ostrowski : Module initialization cleanup.
|
|
|
|
* Arnaldo C. Melo : Remove MOD_{INC,DEC}_USE_COUNT,
|
|
|
|
* the core infrastructure is doing that
|
|
|
|
* for all net proto families now (2.5.69+)
|
|
|
|
*
|
|
|
|
* Known differences from reference BSD that was tested:
|
|
|
|
*
|
|
|
|
* [TO FIX]
|
|
|
|
* ECONNREFUSED is not returned from one end of a connected() socket to the
|
|
|
|
* other the moment one end closes.
|
|
|
|
* fstat() doesn't return st_dev=0, and give the blksize as high water mark
|
|
|
|
* and a fake inode identifier (nor the BSD first socket fstat twice bug).
|
|
|
|
* [NOT TO FIX]
|
|
|
|
* accept() returns a path name even if the connecting socket has closed
|
|
|
|
* in the meantime (BSD loses the path and gives up).
|
|
|
|
* accept() returns 0 length path for an unbound connector. BSD returns 16
|
|
|
|
* and a null first byte in the path (but not for gethost/peername - BSD bug ??)
|
|
|
|
* socketpair(...SOCK_RAW..) doesn't panic the kernel.
|
|
|
|
* BSD af_unix apparently has connect forgetting to block properly.
|
|
|
|
* (need to check this with the POSIX spec in detail)
|
|
|
|
*
|
|
|
|
* Differences from 2.0.0-11-... (ANK)
|
|
|
|
* Bug fixes and improvements.
|
|
|
|
* - client shutdown killed server socket.
|
|
|
|
* - removed all useless cli/sti pairs.
|
|
|
|
*
|
|
|
|
* Semantic changes/extensions.
|
|
|
|
* - generic control message passing.
|
|
|
|
* - SCM_CREDENTIALS control message.
|
|
|
|
* - "Abstract" (not FS based) socket bindings.
|
|
|
|
* Abstract names are sequences of bytes (not zero terminated)
|
|
|
|
* started by 0, so that this name space does not intersect
|
|
|
|
* with BSD names.
|
|
|
|
*/
|
|
|
|
|
2013-12-06 18:03:36 +08:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/signal.h>
|
2017-02-09 01:51:30 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/dcache.h>
|
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/un.h>
|
|
|
|
#include <linux/fcntl.h>
|
2021-12-29 08:49:13 +08:00
|
|
|
#include <linux/filter.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/termios.h>
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
#include <linux/net.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/slab.h>
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/netdevice.h>
|
2007-09-12 18:01:34 +08:00
|
|
|
#include <net/net_namespace.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/sock.h>
|
2005-08-10 11:08:28 +08:00
|
|
|
#include <net/tcp_states.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/af_unix.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <net/scm.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <net/checksum.h>
|
|
|
|
#include <linux/security.h>
|
2023-01-26 15:14:21 +08:00
|
|
|
#include <linux/splice.h>
|
2013-05-07 07:50:21 +08:00
|
|
|
#include <linux/freezer.h>
|
unix: add ioctl to open a unix socket file with O_PATH
This ioctl opens a file to which a socket is bound and
returns a file descriptor. The caller has to have CAP_NET_ADMIN
in the socket network namespace.
Currently it is impossible to get a path and a mount point
for a socket file. socket_diag reports address, device ID and inode
number for unix sockets. An address can contain a relative path or
a file may be moved somewhere. And these properties say nothing about
a mount namespace and a mount point of a socket file.
With the introduced ioctl, we can get a path by reading
/proc/self/fd/X and get mnt_id from /proc/self/fdinfo/X.
In CRIU we are going to use this ioctl to dump and restore unix socket.
Here is an example how it can be used:
$ strace -e socket,bind,ioctl ./test /tmp/test_sock
socket(AF_UNIX, SOCK_STREAM, 0) = 3
bind(3, {sa_family=AF_UNIX, sun_path="test_sock"}, 11) = 0
ioctl(3, SIOCUNIXFILE, 0) = 4
^Z
$ ss -a | grep test_sock
u_str LISTEN 0 1 test_sock 17798 * 0
$ ls -l /proc/760/fd/{3,4}
lrwx------ 1 root root 64 Feb 1 09:41 3 -> 'socket:[17798]'
l--------- 1 root root 64 Feb 1 09:41 4 -> /tmp/test_sock
$ cat /proc/760/fdinfo/4
pos: 0
flags: 012000000
mnt_id: 40
$ cat /proc/self/mountinfo | grep "^40\s"
40 19 0:37 / /tmp rw shared:23 - tmpfs tmpfs rw
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-02 03:00:45 +08:00
|
|
|
#include <linux/file.h>
|
2021-08-14 09:57:15 +08:00
|
|
|
#include <linux/btf_ids.h>
|
2023-10-12 02:51:06 +08:00
|
|
|
#include <linux/bpf-cgroup.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-02-09 00:01:44 +08:00
|
|
|
#include "scm.h"
|
|
|
|
|
2010-10-27 05:22:44 +08:00
|
|
|
static atomic_long_t unix_nr_socks;
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
static struct hlist_head bsd_socket_buckets[UNIX_HASH_SIZE / 2];
|
|
|
|
static spinlock_t bsd_socket_locks[UNIX_HASH_SIZE / 2];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:28 +08:00
|
|
|
/* SMP locking strategy:
|
2022-06-22 01:19:13 +08:00
|
|
|
* hash table is protected with spinlock.
|
|
|
|
* each socket state is protected by separate spinlock.
|
2021-11-24 10:14:28 +08:00
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:28 +08:00
|
|
|
static unsigned int unix_unbound_hash(struct sock *sk)
|
2012-06-08 13:03:21 +08:00
|
|
|
{
|
2021-11-24 10:14:28 +08:00
|
|
|
unsigned long hash = (unsigned long)sk;
|
2012-06-08 13:03:21 +08:00
|
|
|
|
|
|
|
hash ^= hash >> 16;
|
|
|
|
hash ^= hash >> 8;
|
2021-11-24 10:14:28 +08:00
|
|
|
hash ^= sk->sk_type;
|
|
|
|
|
2022-07-06 07:37:15 +08:00
|
|
|
return hash & UNIX_HASH_MOD;
|
2021-11-24 10:14:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int unix_bsd_hash(struct inode *i)
|
|
|
|
{
|
2022-06-22 01:19:09 +08:00
|
|
|
return i->i_ino & UNIX_HASH_MOD;
|
2021-11-24 10:14:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int unix_abstract_hash(struct sockaddr_un *sunaddr,
|
|
|
|
int addr_len, int type)
|
|
|
|
{
|
|
|
|
__wsum csum = csum_partial(sunaddr, addr_len, 0);
|
|
|
|
unsigned int hash;
|
|
|
|
|
|
|
|
hash = (__force unsigned int)csum_fold(csum);
|
|
|
|
hash ^= hash >> 8;
|
|
|
|
hash ^= type;
|
|
|
|
|
2022-07-06 07:37:15 +08:00
|
|
|
return UNIX_HASH_MOD + 1 + (hash & UNIX_HASH_MOD);
|
2012-06-08 13:03:21 +08:00
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
static void unix_table_double_lock(struct net *net,
|
|
|
|
unsigned int hash1, unsigned int hash2)
|
2021-11-24 10:14:30 +08:00
|
|
|
{
|
2022-07-06 07:37:15 +08:00
|
|
|
if (hash1 == hash2) {
|
|
|
|
spin_lock(&net->unx.table.locks[hash1]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-24 10:14:30 +08:00
|
|
|
if (hash1 > hash2)
|
|
|
|
swap(hash1, hash2);
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_lock(&net->unx.table.locks[hash1]);
|
|
|
|
spin_lock_nested(&net->unx.table.locks[hash2], SINGLE_DEPTH_NESTING);
|
2021-11-24 10:14:30 +08:00
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
static void unix_table_double_unlock(struct net *net,
|
|
|
|
unsigned int hash1, unsigned int hash2)
|
2021-11-24 10:14:30 +08:00
|
|
|
{
|
2022-07-06 07:37:15 +08:00
|
|
|
if (hash1 == hash2) {
|
|
|
|
spin_unlock(&net->unx.table.locks[hash1]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_unlock(&net->unx.table.locks[hash1]);
|
|
|
|
spin_unlock(&net->unx.table.locks[hash2]);
|
2021-11-24 10:14:30 +08:00
|
|
|
}
|
|
|
|
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
#ifdef CONFIG_SECURITY_NETWORK
|
2006-08-03 05:12:06 +08:00
|
|
|
static void unix_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
{
|
2015-06-10 20:44:59 +08:00
|
|
|
UNIXCB(skb).secid = scm->secid;
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void unix_set_secdata(struct scm_cookie *scm, struct sk_buff *skb)
|
|
|
|
{
|
2015-06-10 20:44:59 +08:00
|
|
|
scm->secid = UNIXCB(skb).secid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool unix_secdata_eq(struct scm_cookie *scm, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
return (scm->secid == UNIXCB(skb).secid);
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
}
|
|
|
|
#else
|
2006-08-03 05:12:06 +08:00
|
|
|
static inline void unix_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
{ }
|
|
|
|
|
|
|
|
static inline void unix_set_secdata(struct scm_cookie *scm, struct sk_buff *skb)
|
|
|
|
{ }
|
2015-06-10 20:44:59 +08:00
|
|
|
|
|
|
|
static inline bool unix_secdata_eq(struct scm_cookie *scm, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
#endif /* CONFIG_SECURITY_NETWORK */
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define unix_peer(sk) (unix_sk(sk)->peer)
|
|
|
|
|
|
|
|
static inline int unix_our_peer(struct sock *sk, struct sock *osk)
|
|
|
|
{
|
|
|
|
return unix_peer(osk) == sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int unix_may_send(struct sock *sk, struct sock *osk)
|
|
|
|
{
|
2008-11-17 14:58:44 +08:00
|
|
|
return unix_peer(osk) == NULL || unix_our_peer(sk, osk);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2020-02-05 02:40:29 +08:00
|
|
|
static inline int unix_recvq_full(const struct sock *sk)
|
2008-06-18 13:28:05 +08:00
|
|
|
{
|
|
|
|
return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog;
|
|
|
|
}
|
|
|
|
|
2020-02-05 02:40:29 +08:00
|
|
|
static inline int unix_recvq_full_lockless(const struct sock *sk)
|
|
|
|
{
|
|
|
|
return skb_queue_len_lockless(&sk->sk_receive_queue) >
|
|
|
|
READ_ONCE(sk->sk_max_ack_backlog);
|
|
|
|
}
|
|
|
|
|
2011-12-15 10:44:03 +08:00
|
|
|
struct sock *unix_peer_get(struct sock *s)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *peer;
|
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(s);
|
2005-04-17 06:20:36 +08:00
|
|
|
peer = unix_peer(s);
|
|
|
|
if (peer)
|
|
|
|
sock_hold(peer);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(s);
|
2005-04-17 06:20:36 +08:00
|
|
|
return peer;
|
|
|
|
}
|
2011-12-15 10:44:03 +08:00
|
|
|
EXPORT_SYMBOL_GPL(unix_peer_get);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:26 +08:00
|
|
|
static struct unix_address *unix_create_addr(struct sockaddr_un *sunaddr,
|
|
|
|
int addr_len)
|
|
|
|
{
|
|
|
|
struct unix_address *addr;
|
|
|
|
|
|
|
|
addr = kmalloc(sizeof(*addr) + addr_len, GFP_KERNEL);
|
|
|
|
if (!addr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
refcount_set(&addr->refcnt, 1);
|
|
|
|
addr->len = addr_len;
|
|
|
|
memcpy(addr->name, sunaddr, addr_len);
|
|
|
|
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline void unix_release_addr(struct unix_address *addr)
|
|
|
|
{
|
2017-06-30 18:08:05 +08:00
|
|
|
if (refcount_dec_and_test(&addr->refcnt))
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check unix socket name:
|
|
|
|
* - should be not zero length.
|
|
|
|
* - if started by not zero, should be NULL terminated (FS object)
|
|
|
|
* - if started by zero, it is abstract name.
|
|
|
|
*/
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2021-11-24 10:14:23 +08:00
|
|
|
static int unix_validate_addr(struct sockaddr_un *sunaddr, int addr_len)
|
|
|
|
{
|
|
|
|
if (addr_len <= offsetof(struct sockaddr_un, sun_path) ||
|
|
|
|
addr_len > sizeof(*sunaddr))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (sunaddr->sun_family != AF_UNIX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-27 03:08:28 +08:00
|
|
|
static int unix_mkname_bsd(struct sockaddr_un *sunaddr, int addr_len)
|
2021-11-24 10:14:24 +08:00
|
|
|
{
|
2023-07-27 03:08:28 +08:00
|
|
|
struct sockaddr_storage *addr = (struct sockaddr_storage *)sunaddr;
|
|
|
|
short offset = offsetof(struct sockaddr_storage, __data);
|
|
|
|
|
|
|
|
BUILD_BUG_ON(offset != offsetof(struct sockaddr_un, sun_path));
|
|
|
|
|
2021-11-24 10:14:24 +08:00
|
|
|
/* This may look like an off by one error but it is a bit more
|
|
|
|
* subtle. 108 is the longest valid AF_UNIX path for a binding.
|
|
|
|
* sun_path[108] doesn't as such exist. However in kernel space
|
|
|
|
* we are guaranteed that it is a valid memory location in our
|
|
|
|
* kernel address buffer because syscall functions always pass
|
|
|
|
* a pointer of struct sockaddr_storage which has a bigger buffer
|
2023-07-27 03:08:28 +08:00
|
|
|
* than 108. Also, we must terminate sun_path for strlen() in
|
|
|
|
* getname_kernel().
|
|
|
|
*/
|
|
|
|
addr->__data[addr_len - offset] = 0;
|
|
|
|
|
|
|
|
/* Don't pass sunaddr->sun_path to strlen(). Otherwise, 108 will
|
|
|
|
* cause panic if CONFIG_FORTIFY_SOURCE=y. Let __fortify_strlen()
|
|
|
|
* know the actual buffer.
|
2021-11-24 10:14:24 +08:00
|
|
|
*/
|
2023-07-27 03:08:28 +08:00
|
|
|
return strlen(addr->__data) + offset + 1;
|
2021-11-24 10:14:24 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void __unix_remove_socket(struct sock *sk)
|
|
|
|
{
|
|
|
|
sk_del_node_init(sk);
|
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
static void __unix_insert_socket(struct net *net, struct sock *sk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2022-06-09 00:04:35 +08:00
|
|
|
DEBUG_NET_WARN_ON_ONCE(!sk_unhashed(sk));
|
2022-06-22 01:19:12 +08:00
|
|
|
sk_add_node(sk, &net->unx.table.buckets[sk->sk_hash]);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
static void __unix_set_addr_hash(struct net *net, struct sock *sk,
|
|
|
|
struct unix_address *addr, unsigned int hash)
|
2021-06-19 11:50:26 +08:00
|
|
|
{
|
|
|
|
__unix_remove_socket(sk);
|
|
|
|
smp_store_release(&unix_sk(sk)->addr, addr);
|
2021-11-24 10:14:29 +08:00
|
|
|
|
|
|
|
sk->sk_hash = hash;
|
2022-06-22 01:19:12 +08:00
|
|
|
__unix_insert_socket(net, sk);
|
2021-06-19 11:50:26 +08:00
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
static void unix_remove_socket(struct net *net, struct sock *sk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_lock(&net->unx.table.locks[sk->sk_hash]);
|
2005-04-17 06:20:36 +08:00
|
|
|
__unix_remove_socket(sk);
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_unlock(&net->unx.table.locks[sk->sk_hash]);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
static void unix_insert_unbound_socket(struct net *net, struct sock *sk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_lock(&net->unx.table.locks[sk->sk_hash]);
|
2022-06-22 01:19:12 +08:00
|
|
|
__unix_insert_socket(net, sk);
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_unlock(&net->unx.table.locks[sk->sk_hash]);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
static void unix_insert_bsd_socket(struct sock *sk)
|
|
|
|
{
|
|
|
|
spin_lock(&bsd_socket_locks[sk->sk_hash]);
|
|
|
|
sk_add_bind_node(sk, &bsd_socket_buckets[sk->sk_hash]);
|
|
|
|
spin_unlock(&bsd_socket_locks[sk->sk_hash]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_remove_bsd_socket(struct sock *sk)
|
|
|
|
{
|
|
|
|
if (!hlist_unhashed(&sk->sk_bind_node)) {
|
|
|
|
spin_lock(&bsd_socket_locks[sk->sk_hash]);
|
|
|
|
__sk_del_bind_node(sk);
|
|
|
|
spin_unlock(&bsd_socket_locks[sk->sk_hash]);
|
|
|
|
|
|
|
|
sk_node_init(&sk->sk_bind_node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-20 14:29:30 +08:00
|
|
|
static struct sock *__unix_find_socket_byname(struct net *net,
|
|
|
|
struct sockaddr_un *sunname,
|
2021-06-19 11:50:33 +08:00
|
|
|
int len, unsigned int hash)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *s;
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
sk_for_each(s, &net->unx.table.buckets[hash]) {
|
2005-04-17 06:20:36 +08:00
|
|
|
struct unix_sock *u = unix_sk(s);
|
|
|
|
|
|
|
|
if (u->addr->len == len &&
|
|
|
|
!memcmp(u->addr->name, sunname, len))
|
2019-10-10 11:43:47 +08:00
|
|
|
return s;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2019-10-10 11:43:47 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-11-20 14:29:30 +08:00
|
|
|
static inline struct sock *unix_find_socket_byname(struct net *net,
|
|
|
|
struct sockaddr_un *sunname,
|
2021-06-19 11:50:33 +08:00
|
|
|
int len, unsigned int hash)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *s;
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_lock(&net->unx.table.locks[hash]);
|
2021-06-19 11:50:33 +08:00
|
|
|
s = __unix_find_socket_byname(net, sunname, len, hash);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (s)
|
|
|
|
sock_hold(s);
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_unlock(&net->unx.table.locks[hash]);
|
2005-04-17 06:20:36 +08:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
static struct sock *unix_find_socket_byinode(struct inode *i)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-11-24 10:14:28 +08:00
|
|
|
unsigned int hash = unix_bsd_hash(i);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sock *s;
|
|
|
|
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
spin_lock(&bsd_socket_locks[hash]);
|
|
|
|
sk_for_each_bound(s, &bsd_socket_buckets[hash]) {
|
2012-03-15 09:54:32 +08:00
|
|
|
struct dentry *dentry = unix_sk(s)->path.dentry;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-12-16 18:02:53 +08:00
|
|
|
if (dentry && d_backing_inode(dentry) == i) {
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_hold(s);
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
spin_unlock(&bsd_socket_locks[hash]);
|
2021-11-24 10:14:30 +08:00
|
|
|
return s;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
spin_unlock(&bsd_socket_locks[hash]);
|
2021-11-24 10:14:30 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
/* Support code for asymmetrically connected dgram sockets
|
|
|
|
*
|
|
|
|
* If a datagram socket is connected to a socket not itself connected
|
|
|
|
* to the first socket (eg, /dev/log), clients may only enqueue more
|
|
|
|
* messages if the present receive queue of the server socket is not
|
|
|
|
* "too large". This means there's a second writeability condition
|
|
|
|
* poll and sendmsg need to test. The dgram recv code will do a wake
|
|
|
|
* up on the peer_wait wait queue of a socket upon reception of a
|
|
|
|
* datagram which needs to be propagated to sleeping would-be writers
|
|
|
|
* since these might not have sent anything so far. This can't be
|
|
|
|
* accomplished via poll_wait because the lifetime of the server
|
|
|
|
* socket might be less than that of its clients if these break their
|
|
|
|
* association with it or if the server socket is closed while clients
|
|
|
|
* are still connected to it and there's no way to inform "a polling
|
|
|
|
* implementation" that it should let go of a certain wait queue
|
|
|
|
*
|
2017-06-20 18:06:13 +08:00
|
|
|
* In order to propagate a wake up, a wait_queue_entry_t of the client
|
2015-11-21 06:07:23 +08:00
|
|
|
* socket is enqueued on the peer_wait queue of the server socket
|
|
|
|
* whose wake function does a wake_up on the ordinary client socket
|
|
|
|
* wait queue. This connection is established whenever a write (or
|
|
|
|
* poll for write) hit the flow control condition and broken when the
|
|
|
|
* association to the server socket is dissolved or after a wake up
|
|
|
|
* was relayed.
|
|
|
|
*/
|
|
|
|
|
2017-06-20 18:06:13 +08:00
|
|
|
static int unix_dgram_peer_wake_relay(wait_queue_entry_t *q, unsigned mode, int flags,
|
2015-11-21 06:07:23 +08:00
|
|
|
void *key)
|
|
|
|
{
|
|
|
|
struct unix_sock *u;
|
|
|
|
wait_queue_head_t *u_sleep;
|
|
|
|
|
|
|
|
u = container_of(q, struct unix_sock, peer_wake);
|
|
|
|
|
|
|
|
__remove_wait_queue(&unix_sk(u->peer_wake.private)->peer_wait,
|
|
|
|
q);
|
|
|
|
u->peer_wake.private = NULL;
|
|
|
|
|
|
|
|
/* relaying can only happen while the wq still exists */
|
|
|
|
u_sleep = sk_sleep(&u->sk);
|
|
|
|
if (u_sleep)
|
2017-07-04 08:14:56 +08:00
|
|
|
wake_up_interruptible_poll(u_sleep, key_to_poll(key));
|
2015-11-21 06:07:23 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_dgram_peer_wake_connect(struct sock *sk, struct sock *other)
|
|
|
|
{
|
|
|
|
struct unix_sock *u, *u_other;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
u = unix_sk(sk);
|
|
|
|
u_other = unix_sk(other);
|
|
|
|
rc = 0;
|
|
|
|
spin_lock(&u_other->peer_wait.lock);
|
|
|
|
|
|
|
|
if (!u->peer_wake.private) {
|
|
|
|
u->peer_wake.private = other;
|
|
|
|
__add_wait_queue(&u_other->peer_wait, &u->peer_wake);
|
|
|
|
|
|
|
|
rc = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock(&u_other->peer_wait.lock);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_dgram_peer_wake_disconnect(struct sock *sk,
|
|
|
|
struct sock *other)
|
|
|
|
{
|
|
|
|
struct unix_sock *u, *u_other;
|
|
|
|
|
|
|
|
u = unix_sk(sk);
|
|
|
|
u_other = unix_sk(other);
|
|
|
|
spin_lock(&u_other->peer_wait.lock);
|
|
|
|
|
|
|
|
if (u->peer_wake.private == other) {
|
|
|
|
__remove_wait_queue(&u_other->peer_wait, &u->peer_wake);
|
|
|
|
u->peer_wake.private = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock(&u_other->peer_wait.lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_dgram_peer_wake_disconnect_wakeup(struct sock *sk,
|
|
|
|
struct sock *other)
|
|
|
|
{
|
|
|
|
unix_dgram_peer_wake_disconnect(sk, other);
|
|
|
|
wake_up_interruptible_poll(sk_sleep(sk),
|
2018-02-12 06:34:03 +08:00
|
|
|
EPOLLOUT |
|
|
|
|
EPOLLWRNORM |
|
|
|
|
EPOLLWRBAND);
|
2015-11-21 06:07:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* preconditions:
|
|
|
|
* - unix_peer(sk) == other
|
|
|
|
* - association is stable
|
|
|
|
*/
|
|
|
|
static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other)
|
|
|
|
{
|
|
|
|
int connected;
|
|
|
|
|
|
|
|
connected = unix_dgram_peer_wake_connect(sk, other);
|
|
|
|
|
2018-08-04 05:24:53 +08:00
|
|
|
/* If other is SOCK_DEAD, we want to make sure we signal
|
|
|
|
* POLLOUT, such that a subsequent write() can get a
|
|
|
|
* -ECONNREFUSED. Otherwise, if we haven't queued any skbs
|
|
|
|
* to other and its full, we will hang waiting for POLLOUT.
|
|
|
|
*/
|
2022-06-06 07:23:25 +08:00
|
|
|
if (unix_recvq_full_lockless(other) && !sock_flag(other, SOCK_DEAD))
|
2015-11-21 06:07:23 +08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (connected)
|
|
|
|
unix_dgram_peer_wake_disconnect(sk, other);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-24 01:59:16 +08:00
|
|
|
static int unix_writable(const struct sock *sk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-10-24 01:59:16 +08:00
|
|
|
return sk->sk_state != TCP_LISTEN &&
|
2017-06-30 18:08:00 +08:00
|
|
|
(refcount_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_write_space(struct sock *sk)
|
|
|
|
{
|
2010-04-29 19:01:49 +08:00
|
|
|
struct socket_wq *wq;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (unix_writable(sk)) {
|
2010-04-29 19:01:49 +08:00
|
|
|
wq = rcu_dereference(sk->sk_wq);
|
2015-11-26 13:55:39 +08:00
|
|
|
if (skwq_has_sleeper(wq))
|
2010-10-30 04:44:44 +08:00
|
|
|
wake_up_interruptible_sync_poll(&wq->wait,
|
2018-02-12 06:34:03 +08:00
|
|
|
EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND);
|
2007-11-26 20:10:50 +08:00
|
|
|
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-04-29 19:01:49 +08:00
|
|
|
rcu_read_unlock();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* When dgram socket disconnects (or changes its peer), we clear its receive
|
|
|
|
* queue of packets arrived from previous peer. First, it allows to do
|
|
|
|
* flow control based only on wmem_alloc; second, sk connected to peer
|
|
|
|
* may receive messages only from that peer. */
|
|
|
|
static void unix_dgram_disconnected(struct sock *sk, struct sock *other)
|
|
|
|
{
|
2005-07-09 05:57:23 +08:00
|
|
|
if (!skb_queue_empty(&sk->sk_receive_queue)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
skb_queue_purge(&sk->sk_receive_queue);
|
|
|
|
wake_up_interruptible_all(&unix_sk(sk)->peer_wait);
|
|
|
|
|
|
|
|
/* If one link of bidirectional dgram pipe is disconnected,
|
|
|
|
* we signal error. Messages are lost. Do not make this,
|
|
|
|
* when peer was not connected to us.
|
|
|
|
*/
|
|
|
|
if (!sock_flag(other, SOCK_DEAD) && unix_peer(other) == sk) {
|
2023-03-16 04:57:46 +08:00
|
|
|
WRITE_ONCE(other->sk_err, ECONNRESET);
|
2021-06-28 06:48:21 +08:00
|
|
|
sk_error_report(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2021-08-31 01:21:37 +08:00
|
|
|
other->sk_state = TCP_CLOSE;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_sock_destructor(struct sock *sk)
|
|
|
|
{
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
|
|
|
|
skb_queue_purge(&sk->sk_receive_queue);
|
|
|
|
|
2022-06-09 00:04:35 +08:00
|
|
|
DEBUG_NET_WARN_ON_ONCE(refcount_read(&sk->sk_wmem_alloc));
|
|
|
|
DEBUG_NET_WARN_ON_ONCE(!sk_unhashed(sk));
|
|
|
|
DEBUG_NET_WARN_ON_ONCE(sk->sk_socket);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!sock_flag(sk, SOCK_DEAD)) {
|
2013-12-06 18:03:36 +08:00
|
|
|
pr_info("Attempt to release alive unix socket: %p\n", sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (u->addr)
|
|
|
|
unix_release_addr(u->addr);
|
|
|
|
|
2010-10-27 05:22:44 +08:00
|
|
|
atomic_long_dec(&unix_nr_socks);
|
2008-11-17 18:38:49 +08:00
|
|
|
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef UNIX_REFCNT_DEBUG
|
2013-12-06 18:03:36 +08:00
|
|
|
pr_debug("UNIX %p is destroyed, %ld are still alive.\n", sk,
|
2010-10-27 05:22:44 +08:00
|
|
|
atomic_long_read(&unix_nr_socks));
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-03-25 11:18:33 +08:00
|
|
|
static void unix_release_sock(struct sock *sk, int embrion)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
struct sock *skpair;
|
|
|
|
struct sk_buff *skb;
|
2022-06-22 01:19:11 +08:00
|
|
|
struct path path;
|
2005-04-17 06:20:36 +08:00
|
|
|
int state;
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_remove_socket(sock_net(sk), sk);
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
unix_remove_bsd_socket(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Clear state */
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_orphan(sk);
|
2023-05-10 08:34:56 +08:00
|
|
|
WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK);
|
2012-03-15 09:54:32 +08:00
|
|
|
path = u->path;
|
|
|
|
u->path.dentry = NULL;
|
|
|
|
u->path.mnt = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
state = sk->sk_state;
|
|
|
|
sk->sk_state = TCP_CLOSE;
|
2021-06-16 22:47:15 +08:00
|
|
|
|
|
|
|
skpair = unix_peer(sk);
|
|
|
|
unix_peer(sk) = NULL;
|
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
af_unix: Fix memory leaks of the whole sk due to OOB skb.
syzbot reported a sequence of memory leaks, and one of them indicated we
failed to free a whole sk:
unreferenced object 0xffff8880126e0000 (size 1088):
comm "syz-executor419", pid 326, jiffies 4294773607 (age 12.609s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 7d 00 00 00 00 00 00 00 ........}.......
01 00 07 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............
backtrace:
[<000000006fefe750>] sk_prot_alloc+0x64/0x2a0 net/core/sock.c:1970
[<0000000074006db5>] sk_alloc+0x3b/0x800 net/core/sock.c:2029
[<00000000728cd434>] unix_create1+0xaf/0x920 net/unix/af_unix.c:928
[<00000000a279a139>] unix_create+0x113/0x1d0 net/unix/af_unix.c:997
[<0000000068259812>] __sock_create+0x2ab/0x550 net/socket.c:1516
[<00000000da1521e1>] sock_create net/socket.c:1566 [inline]
[<00000000da1521e1>] __sys_socketpair+0x1a8/0x550 net/socket.c:1698
[<000000007ab259e1>] __do_sys_socketpair net/socket.c:1751 [inline]
[<000000007ab259e1>] __se_sys_socketpair net/socket.c:1748 [inline]
[<000000007ab259e1>] __x64_sys_socketpair+0x97/0x100 net/socket.c:1748
[<000000007dedddc1>] do_syscall_x64 arch/x86/entry/common.c:50 [inline]
[<000000007dedddc1>] do_syscall_64+0x38/0x90 arch/x86/entry/common.c:80
[<000000009456679f>] entry_SYSCALL_64_after_hwframe+0x63/0xcd
We can reproduce this issue by creating two AF_UNIX SOCK_STREAM sockets,
send()ing an OOB skb to each other, and close()ing them without consuming
the OOB skbs.
int skpair[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, skpair);
send(skpair[0], "x", 1, MSG_OOB);
send(skpair[1], "x", 1, MSG_OOB);
close(skpair[0]);
close(skpair[1]);
Currently, we free an OOB skb in unix_sock_destructor() which is called via
__sk_free(), but it's too late because the receiver's unix_sk(sk)->oob_skb
is accounted against the sender's sk->sk_wmem_alloc and __sk_free() is
called only when sk->sk_wmem_alloc is 0.
In the repro sequences, we do not consume the OOB skb, so both two sk's
sock_put() never reach __sk_free() due to the positive sk->sk_wmem_alloc.
Then, no one can consume the OOB skb nor call __sk_free(), and we finally
leak the two whole sk.
Thus, we must free the unconsumed OOB skb earlier when close()ing the
socket.
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-09-29 23:52:04 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
|
|
|
if (u->oob_skb) {
|
|
|
|
kfree_skb(u->oob_skb);
|
|
|
|
u->oob_skb = NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
wake_up_interruptible_all(&u->peer_wait);
|
|
|
|
|
2008-11-02 12:38:31 +08:00
|
|
|
if (skpair != NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) {
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(skpair);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* No more writes */
|
2023-05-10 08:34:56 +08:00
|
|
|
WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!skb_queue_empty(&sk->sk_receive_queue) || embrion)
|
2023-03-16 04:57:46 +08:00
|
|
|
WRITE_ONCE(skpair->sk_err, ECONNRESET);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(skpair);
|
2005-04-17 06:20:36 +08:00
|
|
|
skpair->sk_state_change(skpair);
|
2007-11-26 20:10:50 +08:00
|
|
|
sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-11-21 06:07:23 +08:00
|
|
|
|
|
|
|
unix_dgram_peer_wake_disconnect(sk, skpair);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(skpair); /* It may now die */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to flush out this socket. Throw out buffers at least */
|
|
|
|
|
|
|
|
while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
|
2008-11-02 12:38:31 +08:00
|
|
|
if (state == TCP_LISTEN)
|
2005-04-17 06:20:36 +08:00
|
|
|
unix_release_sock(skb->sk, 1);
|
|
|
|
/* passed fds are erased in the kfree_skb hook */
|
2015-11-10 23:23:15 +08:00
|
|
|
UNIXCB(skb).consumed = skb->len;
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2012-03-15 09:54:32 +08:00
|
|
|
if (path.dentry)
|
|
|
|
path_put(&path);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sock_put(sk);
|
|
|
|
|
|
|
|
/* ---- Socket is dead now and most probably destroyed ---- */
|
|
|
|
|
|
|
|
/*
|
2012-09-17 08:52:41 +08:00
|
|
|
* Fixme: BSD difference: In BSD all sockets connected to us get
|
2005-04-17 06:20:36 +08:00
|
|
|
* ECONNRESET and we die on the spot. In Linux we behave
|
|
|
|
* like files and pipes do and wait for the last
|
|
|
|
* dereference.
|
|
|
|
*
|
|
|
|
* Can't we simply set sock->err?
|
|
|
|
*
|
|
|
|
* What the above comment does talk about? --ANK(980817)
|
|
|
|
*/
|
|
|
|
|
2023-09-02 08:27:06 +08:00
|
|
|
if (READ_ONCE(unix_tot_inflight))
|
2007-02-09 22:25:23 +08:00
|
|
|
unix_gc(); /* Garbage collect fds */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-13 11:30:14 +08:00
|
|
|
static void init_peercred(struct sock *sk)
|
|
|
|
{
|
2021-09-30 06:57:50 +08:00
|
|
|
const struct cred *old_cred;
|
|
|
|
struct pid *old_pid;
|
|
|
|
|
|
|
|
spin_lock(&sk->sk_peer_lock);
|
|
|
|
old_pid = sk->sk_peer_pid;
|
|
|
|
old_cred = sk->sk_peer_cred;
|
2010-06-13 11:30:14 +08:00
|
|
|
sk->sk_peer_pid = get_pid(task_tgid(current));
|
|
|
|
sk->sk_peer_cred = get_current_cred();
|
2021-09-30 06:57:50 +08:00
|
|
|
spin_unlock(&sk->sk_peer_lock);
|
|
|
|
|
|
|
|
put_pid(old_pid);
|
|
|
|
put_cred(old_cred);
|
2010-06-13 11:30:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void copy_peercred(struct sock *sk, struct sock *peersk)
|
|
|
|
{
|
2021-09-30 06:57:50 +08:00
|
|
|
const struct cred *old_cred;
|
|
|
|
struct pid *old_pid;
|
|
|
|
|
|
|
|
if (sk < peersk) {
|
|
|
|
spin_lock(&sk->sk_peer_lock);
|
|
|
|
spin_lock_nested(&peersk->sk_peer_lock, SINGLE_DEPTH_NESTING);
|
|
|
|
} else {
|
|
|
|
spin_lock(&peersk->sk_peer_lock);
|
|
|
|
spin_lock_nested(&sk->sk_peer_lock, SINGLE_DEPTH_NESTING);
|
|
|
|
}
|
|
|
|
old_pid = sk->sk_peer_pid;
|
|
|
|
old_cred = sk->sk_peer_cred;
|
2010-06-13 11:30:14 +08:00
|
|
|
sk->sk_peer_pid = get_pid(peersk->sk_peer_pid);
|
|
|
|
sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);
|
2021-09-30 06:57:50 +08:00
|
|
|
|
|
|
|
spin_unlock(&sk->sk_peer_lock);
|
|
|
|
spin_unlock(&peersk->sk_peer_lock);
|
|
|
|
|
|
|
|
put_pid(old_pid);
|
|
|
|
put_cred(old_cred);
|
2010-06-13 11:30:14 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_listen(struct socket *sock, int backlog)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
|
|
|
|
err = -EOPNOTSUPP;
|
2008-11-17 14:58:44 +08:00
|
|
|
if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
|
|
|
|
goto out; /* Only stream/seqpacket sockets accept */
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
if (!u->addr)
|
2008-11-17 14:58:44 +08:00
|
|
|
goto out; /* No listens on an unbound socket */
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
|
|
|
|
goto out_unlock;
|
|
|
|
if (backlog > sk->sk_max_ack_backlog)
|
|
|
|
wake_up_interruptible_all(&u->peer_wait);
|
|
|
|
sk->sk_max_ack_backlog = backlog;
|
|
|
|
sk->sk_state = TCP_LISTEN;
|
|
|
|
/* set credentials so connect can copy them */
|
2010-06-13 11:30:14 +08:00
|
|
|
init_peercred(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
err = 0;
|
|
|
|
|
|
|
|
out_unlock:
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_release(struct socket *);
|
|
|
|
static int unix_bind(struct socket *, struct sockaddr *, int);
|
|
|
|
static int unix_stream_connect(struct socket *, struct sockaddr *,
|
|
|
|
int addr_len, int flags);
|
|
|
|
static int unix_socketpair(struct socket *, struct socket *);
|
2017-03-09 16:09:05 +08:00
|
|
|
static int unix_accept(struct socket *, struct socket *, int, bool);
|
2018-02-13 03:00:20 +08:00
|
|
|
static int unix_getname(struct socket *, struct sockaddr *, int);
|
2018-06-29 00:43:44 +08:00
|
|
|
static __poll_t unix_poll(struct file *, struct socket *, poll_table *);
|
|
|
|
static __poll_t unix_dgram_poll(struct file *, struct socket *,
|
|
|
|
poll_table *);
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_ioctl(struct socket *, unsigned int, unsigned long);
|
2019-06-04 04:03:44 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_shutdown(struct socket *, int);
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t);
|
|
|
|
static int unix_stream_recvmsg(struct socket *, struct msghdr *, size_t, int);
|
2015-05-21 23:00:01 +08:00
|
|
|
static ssize_t unix_stream_splice_read(struct socket *, loff_t *ppos,
|
|
|
|
struct pipe_inode_info *, size_t size,
|
|
|
|
unsigned int flags);
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_dgram_sendmsg(struct socket *, struct msghdr *, size_t);
|
|
|
|
static int unix_dgram_recvmsg(struct socket *, struct msghdr *, size_t, int);
|
2022-06-16 00:20:12 +08:00
|
|
|
static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor);
|
|
|
|
static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor);
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_dgram_connect(struct socket *, struct sockaddr *,
|
|
|
|
int, int);
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_seqpacket_sendmsg(struct socket *, struct msghdr *, size_t);
|
|
|
|
static int unix_seqpacket_recvmsg(struct socket *, struct msghdr *, size_t,
|
|
|
|
int);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-12-08 06:26:27 +08:00
|
|
|
static int unix_set_peek_off(struct sock *sk, int val)
|
2012-02-21 15:31:51 +08:00
|
|
|
{
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
if (mutex_lock_interruptible(&u->iolock))
|
2013-12-08 06:26:27 +08:00
|
|
|
return -EINTR;
|
|
|
|
|
2023-07-28 23:03:16 +08:00
|
|
|
WRITE_ONCE(sk->sk_peek_off, val);
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_unlock(&u->iolock);
|
2013-12-08 06:26:27 +08:00
|
|
|
|
|
|
|
return 0;
|
2012-02-21 15:31:51 +08:00
|
|
|
}
|
|
|
|
|
2020-02-28 03:52:35 +08:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2022-08-17 05:51:54 +08:00
|
|
|
static int unix_count_nr_fds(struct sock *sk)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct unix_sock *u;
|
|
|
|
int nr_fds = 0;
|
|
|
|
|
|
|
|
spin_lock(&sk->sk_receive_queue.lock);
|
|
|
|
skb = skb_peek(&sk->sk_receive_queue);
|
|
|
|
while (skb) {
|
|
|
|
u = unix_sk(skb->sk);
|
|
|
|
nr_fds += atomic_read(&u->scm_stat.nr_fds);
|
|
|
|
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
|
|
|
}
|
|
|
|
spin_unlock(&sk->sk_receive_queue.lock);
|
|
|
|
|
|
|
|
return nr_fds;
|
|
|
|
}
|
|
|
|
|
2019-12-09 18:03:46 +08:00
|
|
|
static void unix_show_fdinfo(struct seq_file *m, struct socket *sock)
|
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
2023-01-14 17:35:02 +08:00
|
|
|
unsigned char s_state;
|
2019-12-09 18:03:46 +08:00
|
|
|
struct unix_sock *u;
|
2023-01-14 17:35:02 +08:00
|
|
|
int nr_fds = 0;
|
2019-12-09 18:03:46 +08:00
|
|
|
|
|
|
|
if (sk) {
|
2023-01-14 17:35:02 +08:00
|
|
|
s_state = READ_ONCE(sk->sk_state);
|
2022-08-17 05:51:54 +08:00
|
|
|
u = unix_sk(sk);
|
|
|
|
|
2023-01-14 17:35:02 +08:00
|
|
|
/* SOCK_STREAM and SOCK_SEQPACKET sockets never change their
|
|
|
|
* sk_state after switching to TCP_ESTABLISHED or TCP_LISTEN.
|
|
|
|
* SOCK_DGRAM is ordinary. So, no lock is needed.
|
|
|
|
*/
|
|
|
|
if (sock->type == SOCK_DGRAM || s_state == TCP_ESTABLISHED)
|
2022-08-17 05:51:54 +08:00
|
|
|
nr_fds = atomic_read(&u->scm_stat.nr_fds);
|
2023-01-14 17:35:02 +08:00
|
|
|
else if (s_state == TCP_LISTEN)
|
2022-08-17 05:51:54 +08:00
|
|
|
nr_fds = unix_count_nr_fds(sk);
|
2023-01-14 17:35:02 +08:00
|
|
|
|
2022-08-17 05:51:54 +08:00
|
|
|
seq_printf(m, "scm_fds: %u\n", nr_fds);
|
2019-12-09 18:03:46 +08:00
|
|
|
}
|
|
|
|
}
|
2020-02-27 01:29:53 +08:00
|
|
|
#else
|
|
|
|
#define unix_show_fdinfo NULL
|
|
|
|
#endif
|
2012-02-21 15:31:51 +08:00
|
|
|
|
2005-12-23 04:49:22 +08:00
|
|
|
static const struct proto_ops unix_stream_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = PF_UNIX,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.release = unix_release,
|
|
|
|
.bind = unix_bind,
|
|
|
|
.connect = unix_stream_connect,
|
|
|
|
.socketpair = unix_socketpair,
|
|
|
|
.accept = unix_accept,
|
|
|
|
.getname = unix_getname,
|
2018-06-29 00:43:44 +08:00
|
|
|
.poll = unix_poll,
|
2005-04-17 06:20:36 +08:00
|
|
|
.ioctl = unix_ioctl,
|
2019-06-04 04:03:44 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = unix_compat_ioctl,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
.listen = unix_listen,
|
|
|
|
.shutdown = unix_shutdown,
|
|
|
|
.sendmsg = unix_stream_sendmsg,
|
|
|
|
.recvmsg = unix_stream_recvmsg,
|
2022-06-16 00:20:12 +08:00
|
|
|
.read_skb = unix_stream_read_skb,
|
2005-04-17 06:20:36 +08:00
|
|
|
.mmap = sock_no_mmap,
|
2015-05-21 23:00:01 +08:00
|
|
|
.splice_read = unix_stream_splice_read,
|
2012-02-21 15:32:06 +08:00
|
|
|
.set_peek_off = unix_set_peek_off,
|
2019-12-09 18:03:46 +08:00
|
|
|
.show_fdinfo = unix_show_fdinfo,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2005-12-23 04:49:22 +08:00
|
|
|
static const struct proto_ops unix_dgram_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = PF_UNIX,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.release = unix_release,
|
|
|
|
.bind = unix_bind,
|
|
|
|
.connect = unix_dgram_connect,
|
|
|
|
.socketpair = unix_socketpair,
|
|
|
|
.accept = sock_no_accept,
|
|
|
|
.getname = unix_getname,
|
2018-06-29 00:43:44 +08:00
|
|
|
.poll = unix_dgram_poll,
|
2005-04-17 06:20:36 +08:00
|
|
|
.ioctl = unix_ioctl,
|
2019-06-04 04:03:44 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = unix_compat_ioctl,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
.listen = sock_no_listen,
|
|
|
|
.shutdown = unix_shutdown,
|
|
|
|
.sendmsg = unix_dgram_sendmsg,
|
2022-06-16 00:20:12 +08:00
|
|
|
.read_skb = unix_read_skb,
|
2005-04-17 06:20:36 +08:00
|
|
|
.recvmsg = unix_dgram_recvmsg,
|
|
|
|
.mmap = sock_no_mmap,
|
2012-02-21 15:31:51 +08:00
|
|
|
.set_peek_off = unix_set_peek_off,
|
2019-12-09 18:03:46 +08:00
|
|
|
.show_fdinfo = unix_show_fdinfo,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2005-12-23 04:49:22 +08:00
|
|
|
static const struct proto_ops unix_seqpacket_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = PF_UNIX,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.release = unix_release,
|
|
|
|
.bind = unix_bind,
|
|
|
|
.connect = unix_stream_connect,
|
|
|
|
.socketpair = unix_socketpair,
|
|
|
|
.accept = unix_accept,
|
|
|
|
.getname = unix_getname,
|
2018-06-29 00:43:44 +08:00
|
|
|
.poll = unix_dgram_poll,
|
2005-04-17 06:20:36 +08:00
|
|
|
.ioctl = unix_ioctl,
|
2019-06-04 04:03:44 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = unix_compat_ioctl,
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
.listen = unix_listen,
|
|
|
|
.shutdown = unix_shutdown,
|
|
|
|
.sendmsg = unix_seqpacket_sendmsg,
|
2011-04-24 09:54:57 +08:00
|
|
|
.recvmsg = unix_seqpacket_recvmsg,
|
2005-04-17 06:20:36 +08:00
|
|
|
.mmap = sock_no_mmap,
|
2012-02-21 15:31:51 +08:00
|
|
|
.set_peek_off = unix_set_peek_off,
|
2019-12-09 18:03:46 +08:00
|
|
|
.show_fdinfo = unix_show_fdinfo,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2021-07-05 03:02:46 +08:00
|
|
|
static void unix_close(struct sock *sk, long timeout)
|
|
|
|
{
|
|
|
|
/* Nothing to do here, unix socket does not need a ->close().
|
|
|
|
* This is merely for sockmap.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
static void unix_unhash(struct sock *sk)
|
|
|
|
{
|
|
|
|
/* Nothing to do here, unix socket does not need a ->unhash().
|
|
|
|
* This is merely for sockmap.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2023-06-09 04:26:26 +08:00
|
|
|
static bool unix_bpf_bypass_getsockopt(int level, int optname)
|
|
|
|
{
|
|
|
|
if (level == SOL_SOCKET) {
|
|
|
|
switch (optname) {
|
|
|
|
case SO_PEERPIDFD:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
struct proto unix_dgram_proto = {
|
2021-10-09 05:59:45 +08:00
|
|
|
.name = "UNIX",
|
2008-11-17 16:00:30 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.obj_size = sizeof(struct unix_sock),
|
2021-07-05 03:02:46 +08:00
|
|
|
.close = unix_close,
|
2023-06-09 04:26:26 +08:00
|
|
|
.bpf_bypass_getsockopt = unix_bpf_bypass_getsockopt,
|
2021-07-05 03:02:47 +08:00
|
|
|
#ifdef CONFIG_BPF_SYSCALL
|
2021-08-17 03:03:21 +08:00
|
|
|
.psock_update_sk_prot = unix_dgram_bpf_update_proto,
|
2021-07-05 03:02:47 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
struct proto unix_stream_proto = {
|
|
|
|
.name = "UNIX-STREAM",
|
2008-11-17 16:00:30 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.obj_size = sizeof(struct unix_sock),
|
2021-07-05 03:02:46 +08:00
|
|
|
.close = unix_close,
|
2021-08-17 03:03:21 +08:00
|
|
|
.unhash = unix_unhash,
|
2023-06-09 04:26:26 +08:00
|
|
|
.bpf_bypass_getsockopt = unix_bpf_bypass_getsockopt,
|
2021-07-05 03:02:47 +08:00
|
|
|
#ifdef CONFIG_BPF_SYSCALL
|
2021-08-17 03:03:21 +08:00
|
|
|
.psock_update_sk_prot = unix_stream_bpf_update_proto,
|
2021-07-05 03:02:47 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
static struct sock *unix_create1(struct net *net, struct socket *sock, int kern, int type)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct unix_sock *u;
|
2021-09-28 08:42:27 +08:00
|
|
|
struct sock *sk;
|
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-10-27 05:22:44 +08:00
|
|
|
atomic_long_inc(&unix_nr_socks);
|
2021-09-28 08:42:27 +08:00
|
|
|
if (atomic_long_read(&unix_nr_socks) > 2 * get_max_files()) {
|
|
|
|
err = -ENFILE;
|
|
|
|
goto err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
if (type == SOCK_STREAM)
|
|
|
|
sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_stream_proto, kern);
|
|
|
|
else /*dgram and seqpacket */
|
|
|
|
sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_dgram_proto, kern);
|
|
|
|
|
2021-09-28 08:42:27 +08:00
|
|
|
if (!sk) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-11-17 14:58:44 +08:00
|
|
|
sock_init_data(sock, sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:29 +08:00
|
|
|
sk->sk_hash = unix_unbound_hash(sk);
|
af_unix: charge buffers to kmemcg
Unix sockets can consume a significant amount of system memory, hence
they should be accounted to kmemcg.
Since unix socket buffers are always allocated from process context, all
we need to do to charge them to kmemcg is set __GFP_ACCOUNT in
sock->sk_allocation mask.
Eric asked:
> 1) What happens when a buffer, allocated from socket <A> lands in a
> different socket <B>, maybe owned by another user/process.
>
> Who owns it now, in term of kmemcg accounting ?
We never move memcg charges. E.g. if two processes from different
cgroups are sharing a memory region, each page will be charged to the
process which touched it first. Or if two processes are working with
the same directory tree, inodes and dentries will be charged to the
first user. The same is fair for unix socket buffers - they will be
charged to the sender.
> 2) Has performance impact been evaluated ?
I ran netperf STREAM_STREAM with default options in a kmemcg on a 4 core
x2 HT box. The results are below:
# clients bandwidth (10^6bits/sec)
base patched
1 67643 +- 725 64874 +- 353 - 4.0 %
4 193585 +- 2516 186715 +- 1460 - 3.5 %
8 194820 +- 377 187443 +- 1229 - 3.7 %
So the accounting doesn't come for free - it takes ~4% of performance.
I believe we could optimize it by using per cpu batching not only on
charge, but also on uncharge in memcg core, but that's beyond the scope
of this patch set - I'll take a look at this later.
Anyway, if performance impact is found to be unacceptable, it is always
possible to disable kmem accounting at boot time (cgroup.memory=nokmem)
or not use memory cgroups at runtime at all (thanks to jump labels
there'll be no overhead even if they are compiled in).
Link: http://lkml.kernel.org/r/fcfe6cae27a59fbc5e40145664b3cf085a560c68.1464079538.git.vdavydov@virtuozzo.com
Signed-off-by: Vladimir Davydov <vdavydov@virtuozzo.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Minchan Kim <minchan@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-07-27 06:24:36 +08:00
|
|
|
sk->sk_allocation = GFP_KERNEL_ACCOUNT;
|
2005-04-17 06:20:36 +08:00
|
|
|
sk->sk_write_space = unix_write_space;
|
2007-12-11 20:19:17 +08:00
|
|
|
sk->sk_max_ack_backlog = net->unx.sysctl_max_dgram_qlen;
|
2005-04-17 06:20:36 +08:00
|
|
|
sk->sk_destruct = unix_sock_destructor;
|
|
|
|
u = unix_sk(sk);
|
2012-03-15 09:54:32 +08:00
|
|
|
u->path.dentry = NULL;
|
|
|
|
u->path.mnt = NULL;
|
2006-01-04 06:10:46 +08:00
|
|
|
spin_lock_init(&u->lock);
|
2008-07-26 12:39:17 +08:00
|
|
|
atomic_long_set(&u->inflight, 0);
|
2007-07-12 05:22:39 +08:00
|
|
|
INIT_LIST_HEAD(&u->link);
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_init(&u->iolock); /* single task reading lock */
|
|
|
|
mutex_init(&u->bindlock); /* single task binding lock */
|
2005-04-17 06:20:36 +08:00
|
|
|
init_waitqueue_head(&u->peer_wait);
|
2015-11-21 06:07:23 +08:00
|
|
|
init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay);
|
2019-12-09 18:03:46 +08:00
|
|
|
memset(&u->scm_stat, 0, sizeof(struct scm_stat));
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_insert_unbound_socket(net, sk);
|
2021-09-28 08:42:27 +08:00
|
|
|
|
2022-06-22 01:19:08 +08:00
|
|
|
sock_prot_inuse_add(net, sk->sk_prot, 1);
|
2021-09-28 08:42:27 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return sk;
|
2021-09-28 08:42:27 +08:00
|
|
|
|
|
|
|
err:
|
|
|
|
atomic_long_dec(&unix_nr_socks);
|
|
|
|
return ERR_PTR(err);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-11-06 14:18:14 +08:00
|
|
|
static int unix_create(struct net *net, struct socket *sock, int protocol,
|
|
|
|
int kern)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-09-28 08:42:27 +08:00
|
|
|
struct sock *sk;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (protocol && protocol != PF_UNIX)
|
|
|
|
return -EPROTONOSUPPORT;
|
|
|
|
|
|
|
|
sock->state = SS_UNCONNECTED;
|
|
|
|
|
|
|
|
switch (sock->type) {
|
|
|
|
case SOCK_STREAM:
|
|
|
|
sock->ops = &unix_stream_ops;
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* Believe it or not BSD has AF_UNIX, SOCK_RAW though
|
|
|
|
* nothing uses it.
|
|
|
|
*/
|
|
|
|
case SOCK_RAW:
|
2008-11-02 12:38:31 +08:00
|
|
|
sock->type = SOCK_DGRAM;
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2005-04-17 06:20:36 +08:00
|
|
|
case SOCK_DGRAM:
|
|
|
|
sock->ops = &unix_dgram_ops;
|
|
|
|
break;
|
|
|
|
case SOCK_SEQPACKET:
|
|
|
|
sock->ops = &unix_seqpacket_ops;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ESOCKTNOSUPPORT;
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:42:27 +08:00
|
|
|
sk = unix_create1(net, sock, kern, sock->type);
|
|
|
|
if (IS_ERR(sk))
|
|
|
|
return PTR_ERR(sk);
|
|
|
|
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_release(struct socket *sock)
|
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
|
|
|
|
if (!sk)
|
|
|
|
return 0;
|
|
|
|
|
2021-07-05 03:02:46 +08:00
|
|
|
sk->sk_prot->close(sk, 0);
|
2013-03-25 11:18:33 +08:00
|
|
|
unix_release_sock(sk, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock->sk = NULL;
|
|
|
|
|
2013-03-25 11:18:33 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
|
|
|
|
int type)
|
2021-11-24 10:14:21 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct path path;
|
|
|
|
struct sock *sk;
|
|
|
|
int err;
|
|
|
|
|
2021-11-24 10:14:24 +08:00
|
|
|
unix_mkname_bsd(sunaddr, addr_len);
|
2021-11-24 10:14:21 +08:00
|
|
|
err = kern_path(sunaddr->sun_path, LOOKUP_FOLLOW, &path);
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
err = path_permission(&path, MAY_WRITE);
|
|
|
|
if (err)
|
|
|
|
goto path_put;
|
|
|
|
|
|
|
|
err = -ECONNREFUSED;
|
|
|
|
inode = d_backing_inode(path.dentry);
|
|
|
|
if (!S_ISSOCK(inode->i_mode))
|
|
|
|
goto path_put;
|
|
|
|
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
sk = unix_find_socket_byinode(inode);
|
2021-11-24 10:14:21 +08:00
|
|
|
if (!sk)
|
|
|
|
goto path_put;
|
|
|
|
|
|
|
|
err = -EPROTOTYPE;
|
|
|
|
if (sk->sk_type == type)
|
|
|
|
touch_atime(&path);
|
|
|
|
else
|
|
|
|
goto sock_put;
|
|
|
|
|
|
|
|
path_put(&path);
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
|
|
|
|
sock_put:
|
|
|
|
sock_put(sk);
|
|
|
|
path_put:
|
|
|
|
path_put(&path);
|
|
|
|
fail:
|
2021-11-24 10:14:22 +08:00
|
|
|
return ERR_PTR(err);
|
2021-11-24 10:14:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct sock *unix_find_abstract(struct net *net,
|
|
|
|
struct sockaddr_un *sunaddr,
|
2021-11-24 10:14:24 +08:00
|
|
|
int addr_len, int type)
|
2021-11-24 10:14:21 +08:00
|
|
|
{
|
2021-11-24 10:14:28 +08:00
|
|
|
unsigned int hash = unix_abstract_hash(sunaddr, addr_len, type);
|
2021-11-24 10:14:21 +08:00
|
|
|
struct dentry *dentry;
|
|
|
|
struct sock *sk;
|
|
|
|
|
2021-11-24 10:14:28 +08:00
|
|
|
sk = unix_find_socket_byname(net, sunaddr, addr_len, hash);
|
2021-11-24 10:14:22 +08:00
|
|
|
if (!sk)
|
|
|
|
return ERR_PTR(-ECONNREFUSED);
|
2021-11-24 10:14:21 +08:00
|
|
|
|
|
|
|
dentry = unix_sk(sk)->path.dentry;
|
|
|
|
if (dentry)
|
|
|
|
touch_atime(&unix_sk(sk)->path);
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sock *unix_find_other(struct net *net,
|
|
|
|
struct sockaddr_un *sunaddr,
|
2021-11-24 10:14:24 +08:00
|
|
|
int addr_len, int type)
|
2021-11-24 10:14:21 +08:00
|
|
|
{
|
|
|
|
struct sock *sk;
|
|
|
|
|
|
|
|
if (sunaddr->sun_path[0])
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
sk = unix_find_bsd(sunaddr, addr_len, type);
|
2021-11-24 10:14:21 +08:00
|
|
|
else
|
2021-11-24 10:14:24 +08:00
|
|
|
sk = unix_find_abstract(net, sunaddr, addr_len, type);
|
2021-11-24 10:14:21 +08:00
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
2021-11-24 10:14:20 +08:00
|
|
|
static int unix_autobind(struct sock *sk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-11-24 10:14:30 +08:00
|
|
|
unsigned int new_hash, old_hash = sk->sk_hash;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct unix_sock *u = unix_sk(sk);
|
2022-06-22 01:19:11 +08:00
|
|
|
struct net *net = sock_net(sk);
|
2008-11-17 14:58:44 +08:00
|
|
|
struct unix_address *addr;
|
2021-11-24 10:14:31 +08:00
|
|
|
u32 lastnum, ordernum;
|
2021-11-24 10:14:20 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
err = mutex_lock_interruptible(&u->bindlock);
|
2013-12-13 23:54:22 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (u->addr)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -ENOMEM;
|
2021-11-24 10:14:19 +08:00
|
|
|
addr = kzalloc(sizeof(*addr) +
|
|
|
|
offsetof(struct sockaddr_un, sun_path) + 16, GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!addr)
|
|
|
|
goto out;
|
|
|
|
|
2021-11-24 10:14:31 +08:00
|
|
|
addr->len = offsetof(struct sockaddr_un, sun_path) + 6;
|
2005-04-17 06:20:36 +08:00
|
|
|
addr->name->sun_family = AF_UNIX;
|
2017-06-30 18:08:05 +08:00
|
|
|
refcount_set(&addr->refcnt, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2022-10-05 23:43:22 +08:00
|
|
|
ordernum = get_random_u32();
|
2021-11-24 10:14:31 +08:00
|
|
|
lastnum = ordernum & 0xFFFFF;
|
2005-04-17 06:20:36 +08:00
|
|
|
retry:
|
2021-11-24 10:14:31 +08:00
|
|
|
ordernum = (ordernum + 1) & 0xFFFFF;
|
|
|
|
sprintf(addr->name->sun_path + 1, "%05x", ordernum);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:29 +08:00
|
|
|
new_hash = unix_abstract_hash(addr->name, addr->len, sk->sk_type);
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_lock(net, old_hash, new_hash);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
if (__unix_find_socket_byname(net, addr->name, addr->len, new_hash)) {
|
|
|
|
unix_table_double_unlock(net, old_hash, new_hash);
|
2021-11-24 10:14:30 +08:00
|
|
|
|
2021-11-24 10:14:31 +08:00
|
|
|
/* __unix_find_socket_byname() may take long time if many names
|
2010-09-04 09:34:28 +08:00
|
|
|
* are already in use.
|
|
|
|
*/
|
|
|
|
cond_resched();
|
2021-11-24 10:14:31 +08:00
|
|
|
|
|
|
|
if (ordernum == lastnum) {
|
|
|
|
/* Give up if all names seems to be in use. */
|
2010-09-04 09:34:28 +08:00
|
|
|
err = -ENOSPC;
|
2021-11-24 10:14:31 +08:00
|
|
|
unix_release_addr(addr);
|
2010-09-04 09:34:28 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2021-11-24 10:14:31 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
__unix_set_addr_hash(net, sk, addr, new_hash);
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_unlock(net, old_hash, new_hash);
|
2005-04-17 06:20:36 +08:00
|
|
|
err = 0;
|
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
out: mutex_unlock(&u->bindlock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-11-24 10:14:26 +08:00
|
|
|
static int unix_bind_bsd(struct sock *sk, struct sockaddr_un *sunaddr,
|
|
|
|
int addr_len)
|
2012-07-20 06:37:29 +08:00
|
|
|
{
|
2021-06-19 11:50:30 +08:00
|
|
|
umode_t mode = S_IFSOCK |
|
|
|
|
(SOCK_INODE(sk->sk_socket)->i_mode & ~current_umask());
|
2021-11-24 10:14:30 +08:00
|
|
|
unsigned int new_hash, old_hash = sk->sk_hash;
|
2021-11-24 10:14:26 +08:00
|
|
|
struct unix_sock *u = unix_sk(sk);
|
2022-06-22 01:19:11 +08:00
|
|
|
struct net *net = sock_net(sk);
|
2023-01-13 19:49:10 +08:00
|
|
|
struct mnt_idmap *idmap;
|
2021-11-24 10:14:26 +08:00
|
|
|
struct unix_address *addr;
|
2016-09-02 05:56:49 +08:00
|
|
|
struct dentry *dentry;
|
2021-11-24 10:14:26 +08:00
|
|
|
struct path parent;
|
2021-06-19 11:50:30 +08:00
|
|
|
int err;
|
|
|
|
|
2023-07-27 03:08:28 +08:00
|
|
|
addr_len = unix_mkname_bsd(sunaddr, addr_len);
|
2021-11-24 10:14:26 +08:00
|
|
|
addr = unix_create_addr(sunaddr, addr_len);
|
|
|
|
if (!addr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-09-02 05:56:49 +08:00
|
|
|
/*
|
|
|
|
* Get the parent directory, calculate the hash for last
|
|
|
|
* component.
|
|
|
|
*/
|
2021-06-19 11:50:30 +08:00
|
|
|
dentry = kern_path_create(AT_FDCWD, addr->name->sun_path, &parent, 0);
|
2021-11-24 10:14:26 +08:00
|
|
|
if (IS_ERR(dentry)) {
|
|
|
|
err = PTR_ERR(dentry);
|
|
|
|
goto out;
|
|
|
|
}
|
2012-07-20 06:37:29 +08:00
|
|
|
|
2016-09-02 05:56:49 +08:00
|
|
|
/*
|
|
|
|
* All right, let's create it.
|
|
|
|
*/
|
2023-01-13 19:49:10 +08:00
|
|
|
idmap = mnt_idmap(parent.mnt);
|
2021-06-19 11:50:30 +08:00
|
|
|
err = security_path_mknod(&parent, dentry, mode, 0);
|
2021-06-19 11:50:31 +08:00
|
|
|
if (!err)
|
2023-01-13 19:49:10 +08:00
|
|
|
err = vfs_mknod(idmap, d_inode(parent.dentry), dentry, mode, 0);
|
2021-06-19 11:50:32 +08:00
|
|
|
if (err)
|
2021-11-24 10:14:26 +08:00
|
|
|
goto out_path;
|
2021-06-19 11:50:29 +08:00
|
|
|
err = mutex_lock_interruptible(&u->bindlock);
|
2021-06-19 11:50:32 +08:00
|
|
|
if (err)
|
|
|
|
goto out_unlink;
|
|
|
|
if (u->addr)
|
|
|
|
goto out_unlock;
|
2021-06-19 11:50:29 +08:00
|
|
|
|
2021-11-24 10:14:29 +08:00
|
|
|
new_hash = unix_bsd_hash(d_backing_inode(dentry));
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_lock(net, old_hash, new_hash);
|
2021-06-19 11:50:31 +08:00
|
|
|
u->path.mnt = mntget(parent.mnt);
|
|
|
|
u->path.dentry = dget(dentry);
|
2022-06-22 01:19:12 +08:00
|
|
|
__unix_set_addr_hash(net, sk, addr, new_hash);
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_unlock(net, old_hash, new_hash);
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
unix_insert_bsd_socket(sk);
|
2021-06-19 11:50:29 +08:00
|
|
|
mutex_unlock(&u->bindlock);
|
2021-06-19 11:50:31 +08:00
|
|
|
done_path_create(&parent, dentry);
|
2021-06-19 11:50:29 +08:00
|
|
|
return 0;
|
2021-06-19 11:50:32 +08:00
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
mutex_unlock(&u->bindlock);
|
|
|
|
err = -EINVAL;
|
|
|
|
out_unlink:
|
|
|
|
/* failed after successful mknod? unlink what we'd created... */
|
2023-01-13 19:49:10 +08:00
|
|
|
vfs_unlink(idmap, d_inode(parent.dentry), dentry, NULL);
|
2021-11-24 10:14:26 +08:00
|
|
|
out_path:
|
2021-06-19 11:50:32 +08:00
|
|
|
done_path_create(&parent, dentry);
|
2021-11-24 10:14:26 +08:00
|
|
|
out:
|
|
|
|
unix_release_addr(addr);
|
|
|
|
return err == -EEXIST ? -EADDRINUSE : err;
|
2021-06-19 11:50:29 +08:00
|
|
|
}
|
|
|
|
|
2021-11-24 10:14:26 +08:00
|
|
|
static int unix_bind_abstract(struct sock *sk, struct sockaddr_un *sunaddr,
|
|
|
|
int addr_len)
|
2021-06-19 11:50:29 +08:00
|
|
|
{
|
2021-11-24 10:14:30 +08:00
|
|
|
unsigned int new_hash, old_hash = sk->sk_hash;
|
2021-06-19 11:50:29 +08:00
|
|
|
struct unix_sock *u = unix_sk(sk);
|
2022-06-22 01:19:11 +08:00
|
|
|
struct net *net = sock_net(sk);
|
2021-11-24 10:14:26 +08:00
|
|
|
struct unix_address *addr;
|
2021-06-19 11:50:29 +08:00
|
|
|
int err;
|
|
|
|
|
2021-11-24 10:14:26 +08:00
|
|
|
addr = unix_create_addr(sunaddr, addr_len);
|
|
|
|
if (!addr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2021-06-19 11:50:29 +08:00
|
|
|
err = mutex_lock_interruptible(&u->bindlock);
|
|
|
|
if (err)
|
2021-11-24 10:14:26 +08:00
|
|
|
goto out;
|
2021-06-19 11:50:29 +08:00
|
|
|
|
|
|
|
if (u->addr) {
|
2021-11-24 10:14:26 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_mutex;
|
2021-06-19 11:50:29 +08:00
|
|
|
}
|
|
|
|
|
2021-11-24 10:14:29 +08:00
|
|
|
new_hash = unix_abstract_hash(addr->name, addr->len, sk->sk_type);
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_lock(net, old_hash, new_hash);
|
2021-11-24 10:14:26 +08:00
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
if (__unix_find_socket_byname(net, addr->name, addr->len, new_hash))
|
2021-11-24 10:14:26 +08:00
|
|
|
goto out_spin;
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
__unix_set_addr_hash(net, sk, addr, new_hash);
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_unlock(net, old_hash, new_hash);
|
2021-06-19 11:50:29 +08:00
|
|
|
mutex_unlock(&u->bindlock);
|
|
|
|
return 0;
|
2021-11-24 10:14:26 +08:00
|
|
|
|
|
|
|
out_spin:
|
2022-06-22 01:19:11 +08:00
|
|
|
unix_table_double_unlock(net, old_hash, new_hash);
|
2021-11-24 10:14:26 +08:00
|
|
|
err = -EADDRINUSE;
|
|
|
|
out_mutex:
|
|
|
|
mutex_unlock(&u->bindlock);
|
|
|
|
out:
|
|
|
|
unix_release_addr(addr);
|
|
|
|
return err;
|
2021-06-19 11:50:29 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
|
|
|
|
{
|
2008-11-02 12:38:31 +08:00
|
|
|
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
|
2021-11-24 10:14:25 +08:00
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:23 +08:00
|
|
|
if (addr_len == offsetof(struct sockaddr_un, sun_path) &&
|
|
|
|
sunaddr->sun_family == AF_UNIX)
|
2021-11-24 10:14:20 +08:00
|
|
|
return unix_autobind(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:23 +08:00
|
|
|
err = unix_validate_addr(sunaddr, addr_len);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2021-11-24 10:14:26 +08:00
|
|
|
if (sunaddr->sun_path[0])
|
|
|
|
err = unix_bind_bsd(sk, sunaddr, addr_len);
|
2021-06-19 11:50:29 +08:00
|
|
|
else
|
2021-11-24 10:14:26 +08:00
|
|
|
err = unix_bind_abstract(sk, sunaddr, addr_len);
|
|
|
|
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-06-01 06:19:20 +08:00
|
|
|
static void unix_state_double_lock(struct sock *sk1, struct sock *sk2)
|
|
|
|
{
|
|
|
|
if (unlikely(sk1 == sk2) || !sk2) {
|
|
|
|
unix_state_lock(sk1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sk1 < sk2) {
|
|
|
|
unix_state_lock(sk1);
|
|
|
|
unix_state_lock_nested(sk2);
|
|
|
|
} else {
|
|
|
|
unix_state_lock(sk2);
|
|
|
|
unix_state_lock_nested(sk1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2)
|
|
|
|
{
|
|
|
|
if (unlikely(sk1 == sk2) || !sk2) {
|
|
|
|
unix_state_unlock(sk1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unix_state_unlock(sk1);
|
|
|
|
unix_state_unlock(sk2);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
|
|
|
|
int alen, int flags)
|
|
|
|
{
|
2008-11-02 12:38:31 +08:00
|
|
|
struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr;
|
2022-06-22 01:19:08 +08:00
|
|
|
struct sock *sk = sock->sk;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sock *other;
|
|
|
|
int err;
|
|
|
|
|
2017-06-08 17:13:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
if (alen < offsetofend(struct sockaddr, sa_family))
|
|
|
|
goto out;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (addr->sa_family != AF_UNSPEC) {
|
2021-11-24 10:14:23 +08:00
|
|
|
err = unix_validate_addr(sunaddr, alen);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2023-10-12 02:51:06 +08:00
|
|
|
err = BPF_CGROUP_RUN_PROG_UNIX_CONNECT_LOCK(sk, addr, &alen);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2023-06-09 04:26:25 +08:00
|
|
|
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
|
|
|
|
test_bit(SOCK_PASSPIDFD, &sock->flags)) &&
|
2021-11-24 10:14:20 +08:00
|
|
|
!unix_sk(sk)->addr) {
|
|
|
|
err = unix_autobind(sk);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-06-01 06:19:20 +08:00
|
|
|
restart:
|
2022-06-22 01:19:08 +08:00
|
|
|
other = unix_find_other(sock_net(sk), sunaddr, alen, sock->type);
|
2021-11-24 10:14:22 +08:00
|
|
|
if (IS_ERR(other)) {
|
|
|
|
err = PTR_ERR(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2021-11-24 10:14:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-06-01 06:19:20 +08:00
|
|
|
unix_state_double_lock(sk, other);
|
|
|
|
|
|
|
|
/* Apparently VFS overslept socket death. Retry. */
|
|
|
|
if (sock_flag(other, SOCK_DEAD)) {
|
|
|
|
unix_state_double_unlock(sk, other);
|
|
|
|
sock_put(other);
|
|
|
|
goto restart;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err = -EPERM;
|
|
|
|
if (!unix_may_send(sk, other))
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
|
|
|
|
if (err)
|
|
|
|
goto out_unlock;
|
|
|
|
|
2021-08-31 01:21:37 +08:00
|
|
|
sk->sk_state = other->sk_state = TCP_ESTABLISHED;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* 1003.1g breaking connected state with AF_UNSPEC
|
|
|
|
*/
|
|
|
|
other = NULL;
|
2007-06-01 06:19:20 +08:00
|
|
|
unix_state_double_lock(sk, other);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it was connected, reconnect.
|
|
|
|
*/
|
|
|
|
if (unix_peer(sk)) {
|
|
|
|
struct sock *old_peer = unix_peer(sk);
|
2021-08-31 01:21:37 +08:00
|
|
|
|
2008-11-02 12:38:31 +08:00
|
|
|
unix_peer(sk) = other;
|
2021-08-31 01:21:37 +08:00
|
|
|
if (!other)
|
|
|
|
sk->sk_state = TCP_CLOSE;
|
2015-11-21 06:07:23 +08:00
|
|
|
unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer);
|
|
|
|
|
2007-06-01 06:19:20 +08:00
|
|
|
unix_state_double_unlock(sk, other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (other != old_peer)
|
|
|
|
unix_dgram_disconnected(sk, old_peer);
|
|
|
|
sock_put(old_peer);
|
|
|
|
} else {
|
2008-11-02 12:38:31 +08:00
|
|
|
unix_peer(sk) = other;
|
2007-06-01 06:19:20 +08:00
|
|
|
unix_state_double_unlock(sk, other);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2021-07-05 03:02:45 +08:00
|
|
|
|
2007-02-09 22:25:23 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
out_unlock:
|
2007-06-01 06:19:20 +08:00
|
|
|
unix_state_double_unlock(sk, other);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(other);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long unix_wait_for_peer(struct sock *other, long timeo)
|
2020-02-24 07:16:56 +08:00
|
|
|
__releases(&unix_sk(other)->lock)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct unix_sock *u = unix_sk(other);
|
|
|
|
int sched;
|
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
|
|
|
|
prepare_to_wait_exclusive(&u->peer_wait, &wait, TASK_INTERRUPTIBLE);
|
|
|
|
|
|
|
|
sched = !sock_flag(other, SOCK_DEAD) &&
|
|
|
|
!(other->sk_shutdown & RCV_SHUTDOWN) &&
|
2023-05-10 08:34:55 +08:00
|
|
|
unix_recvq_full_lockless(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (sched)
|
|
|
|
timeo = schedule_timeout(timeo);
|
|
|
|
|
|
|
|
finish_wait(&u->peer_wait, &wait);
|
|
|
|
return timeo;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
|
|
|
|
int addr_len, int flags)
|
|
|
|
{
|
2008-11-02 12:38:31 +08:00
|
|
|
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
|
2022-06-22 01:19:08 +08:00
|
|
|
struct sock *sk = sock->sk, *newsk = NULL, *other = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct unix_sock *u = unix_sk(sk), *newu, *otheru;
|
2022-06-22 01:19:08 +08:00
|
|
|
struct net *net = sock_net(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sk_buff *skb = NULL;
|
|
|
|
long timeo;
|
2022-06-22 01:19:08 +08:00
|
|
|
int err;
|
|
|
|
int st;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-11-24 10:14:23 +08:00
|
|
|
err = unix_validate_addr(sunaddr, addr_len);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2023-10-12 02:51:06 +08:00
|
|
|
err = BPF_CGROUP_RUN_PROG_UNIX_CONNECT_LOCK(sk, uaddr, &addr_len);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2023-06-09 04:26:25 +08:00
|
|
|
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
|
|
|
|
test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) {
|
2021-11-24 10:14:20 +08:00
|
|
|
err = unix_autobind(sk);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
|
|
|
|
|
|
|
|
/* First of all allocate resources.
|
|
|
|
If we will make it after state is locked,
|
|
|
|
we will have to recheck all again in any case.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* create new sock for complete connection */
|
2022-06-22 01:19:08 +08:00
|
|
|
newsk = unix_create1(net, NULL, 0, sock->type);
|
2021-09-28 08:42:27 +08:00
|
|
|
if (IS_ERR(newsk)) {
|
|
|
|
err = PTR_ERR(newsk);
|
|
|
|
newsk = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2021-09-28 08:42:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
err = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Allocate skb for sending to listening sock */
|
|
|
|
skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
|
|
|
|
if (skb == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
restart:
|
|
|
|
/* Find listening sock. */
|
2021-11-24 10:14:24 +08:00
|
|
|
other = unix_find_other(net, sunaddr, addr_len, sk->sk_type);
|
2021-11-24 10:14:22 +08:00
|
|
|
if (IS_ERR(other)) {
|
|
|
|
err = PTR_ERR(other);
|
|
|
|
other = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2021-11-24 10:14:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Latch state of peer */
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Apparently VFS overslept socket death. Retry. */
|
|
|
|
if (sock_flag(other, SOCK_DEAD)) {
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(other);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = -ECONNREFUSED;
|
|
|
|
if (other->sk_state != TCP_LISTEN)
|
|
|
|
goto out_unlock;
|
AF_UNIX: Fix deadlock on connecting to shutdown socket
I found a deadlock bug in UNIX domain socket, which makes able to DoS
attack against the local machine by non-root users.
How to reproduce:
1. Make a listening AF_UNIX/SOCK_STREAM socket with an abstruct
namespace(*), and shutdown(2) it.
2. Repeat connect(2)ing to the listening socket from the other sockets
until the connection backlog is full-filled.
3. connect(2) takes the CPU forever. If every core is taken, the
system hangs.
PoC code: (Run as many times as cores on SMP machines.)
int main(void)
{
int ret;
int csd;
int lsd;
struct sockaddr_un sun;
/* make an abstruct name address (*) */
memset(&sun, 0, sizeof(sun));
sun.sun_family = PF_UNIX;
sprintf(&sun.sun_path[1], "%d", getpid());
/* create the listening socket and shutdown */
lsd = socket(AF_UNIX, SOCK_STREAM, 0);
bind(lsd, (struct sockaddr *)&sun, sizeof(sun));
listen(lsd, 1);
shutdown(lsd, SHUT_RDWR);
/* connect loop */
alarm(15); /* forcely exit the loop after 15 sec */
for (;;) {
csd = socket(AF_UNIX, SOCK_STREAM, 0);
ret = connect(csd, (struct sockaddr *)&sun, sizeof(sun));
if (-1 == ret) {
perror("connect()");
break;
}
puts("Connection OK");
}
return 0;
}
(*) Make sun_path[0] = 0 to use the abstruct namespace.
If a file-based socket is used, the system doesn't deadlock because
of context switches in the file system layer.
Why this happens:
Error checks between unix_socket_connect() and unix_wait_for_peer() are
inconsistent. The former calls the latter to wait until the backlog is
processed. Despite the latter returns without doing anything when the
socket is shutdown, the former doesn't check the shutdown state and
just retries calling the latter forever.
Patch:
The patch below adds shutdown check into unix_socket_connect(), so
connect(2) to the shutdown socket will return -ECONREFUSED.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama.qu@hitachi.com>
Signed-off-by: Masanori Yoshida <masanori.yoshida.tv@hitachi.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-10-19 14:17:37 +08:00
|
|
|
if (other->sk_shutdown & RCV_SHUTDOWN)
|
|
|
|
goto out_unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-06-18 13:28:05 +08:00
|
|
|
if (unix_recvq_full(other)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EAGAIN;
|
|
|
|
if (!timeo)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
timeo = unix_wait_for_peer(other, timeo);
|
|
|
|
|
|
|
|
err = sock_intr_errno(timeo);
|
|
|
|
if (signal_pending(current))
|
|
|
|
goto out;
|
|
|
|
sock_put(other);
|
|
|
|
goto restart;
|
2007-02-09 22:25:23 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Latch our state.
|
|
|
|
|
2011-03-15 06:25:33 +08:00
|
|
|
It is tricky place. We need to grab our state lock and cannot
|
2005-04-17 06:20:36 +08:00
|
|
|
drop lock on peer. It is dangerous because deadlock is
|
|
|
|
possible. Connect to self case and simultaneous
|
|
|
|
attempt to connect are eliminated by checking socket
|
|
|
|
state. other is TCP_LISTEN, if sk is TCP_LISTEN we
|
|
|
|
check this before attempt to grab lock.
|
|
|
|
|
|
|
|
Well, and we have to recheck the state after socket locked.
|
|
|
|
*/
|
|
|
|
st = sk->sk_state;
|
|
|
|
|
|
|
|
switch (st) {
|
|
|
|
case TCP_CLOSE:
|
|
|
|
/* This is ok... continue with connect */
|
|
|
|
break;
|
|
|
|
case TCP_ESTABLISHED:
|
|
|
|
/* Socket is already connected */
|
|
|
|
err = -EISCONN;
|
|
|
|
goto out_unlock;
|
|
|
|
default:
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock_nested(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (sk->sk_state != st) {
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(other);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
2011-01-06 07:38:53 +08:00
|
|
|
err = security_unix_stream_connect(sk, other, newsk);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err) {
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The way is open! Fastly set all the necessary fields... */
|
|
|
|
|
|
|
|
sock_hold(sk);
|
|
|
|
unix_peer(newsk) = sk;
|
|
|
|
newsk->sk_state = TCP_ESTABLISHED;
|
|
|
|
newsk->sk_type = sk->sk_type;
|
2010-06-13 11:30:14 +08:00
|
|
|
init_peercred(newsk);
|
2005-04-17 06:20:36 +08:00
|
|
|
newu = unix_sk(newsk);
|
2011-02-18 11:26:36 +08:00
|
|
|
RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
|
2005-04-17 06:20:36 +08:00
|
|
|
otheru = unix_sk(other);
|
|
|
|
|
2019-02-16 04:09:35 +08:00
|
|
|
/* copy address information from listening to new sock
|
|
|
|
*
|
|
|
|
* The contents of *(otheru->addr) and otheru->path
|
|
|
|
* are seen fully set up here, since we have found
|
2022-06-22 01:19:13 +08:00
|
|
|
* otheru in hash under its lock. Insertion into the
|
|
|
|
* hash chain we'd found it in had been done in an
|
|
|
|
* earlier critical area protected by the chain's lock,
|
2019-02-16 04:09:35 +08:00
|
|
|
* the same one where we'd set *(otheru->addr) contents,
|
|
|
|
* as well as otheru->path and otheru->addr itself.
|
|
|
|
*
|
|
|
|
* Using smp_store_release() here to set newu->addr
|
|
|
|
* is enough to make those stores, as well as stores
|
|
|
|
* to newu->path visible to anyone who gets newu->addr
|
|
|
|
* by smp_load_acquire(). IOW, the same warranties
|
|
|
|
* as for unix_sock instances bound in unix_bind() or
|
|
|
|
* in unix_autobind().
|
|
|
|
*/
|
2012-03-15 09:54:32 +08:00
|
|
|
if (otheru->path.dentry) {
|
|
|
|
path_get(&otheru->path);
|
|
|
|
newu->path = otheru->path;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2019-02-16 04:09:35 +08:00
|
|
|
refcount_inc(&otheru->addr->refcnt);
|
|
|
|
smp_store_release(&newu->addr, otheru->addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Set credentials */
|
2010-06-13 11:30:14 +08:00
|
|
|
copy_peercred(sk, other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sock->state = SS_CONNECTED;
|
|
|
|
sk->sk_state = TCP_ESTABLISHED;
|
2005-12-14 15:22:32 +08:00
|
|
|
sock_hold(newsk);
|
|
|
|
|
2014-03-18 01:06:10 +08:00
|
|
|
smp_mb__after_atomic(); /* sock_hold() does an atomic_inc() */
|
2005-12-14 15:22:32 +08:00
|
|
|
unix_peer(sk) = newsk;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-06-10 11:09:35 +08:00
|
|
|
/* take ten and send info to listening sock */
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock(&other->sk_receive_queue.lock);
|
|
|
|
__skb_queue_tail(&other->sk_receive_queue, skb);
|
|
|
|
spin_unlock(&other->sk_receive_queue.lock);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2014-04-12 04:15:36 +08:00
|
|
|
other->sk_data_ready(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(other);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
if (other)
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
out:
|
2009-02-25 08:32:45 +08:00
|
|
|
kfree_skb(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (newsk)
|
|
|
|
unix_release_sock(newsk, 0);
|
|
|
|
if (other)
|
|
|
|
sock_put(other);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_socketpair(struct socket *socka, struct socket *sockb)
|
|
|
|
{
|
2008-11-02 12:38:31 +08:00
|
|
|
struct sock *ska = socka->sk, *skb = sockb->sk;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Join our sockets back to back */
|
|
|
|
sock_hold(ska);
|
|
|
|
sock_hold(skb);
|
2008-11-02 12:38:31 +08:00
|
|
|
unix_peer(ska) = skb;
|
|
|
|
unix_peer(skb) = ska;
|
2010-06-13 11:30:14 +08:00
|
|
|
init_peercred(ska);
|
|
|
|
init_peercred(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-07-05 03:02:45 +08:00
|
|
|
ska->sk_state = TCP_ESTABLISHED;
|
|
|
|
skb->sk_state = TCP_ESTABLISHED;
|
|
|
|
socka->state = SS_CONNECTED;
|
|
|
|
sockb->state = SS_CONNECTED;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
net: unix: inherit SOCK_PASS{CRED, SEC} flags from socket to fix race
In the case of credentials passing in unix stream sockets (dgram
sockets seem not affected), we get a rather sparse race after
commit 16e5726 ("af_unix: dont send SCM_CREDENTIALS by default").
We have a stream server on receiver side that requests credential
passing from senders (e.g. nc -U). Since we need to set SO_PASSCRED
on each spawned/accepted socket on server side to 1 first (as it's
not inherited), it can happen that in the time between accept() and
setsockopt() we get interrupted, the sender is being scheduled and
continues with passing data to our receiver. At that time SO_PASSCRED
is neither set on sender nor receiver side, hence in cmsg's
SCM_CREDENTIALS we get eventually pid:0, uid:65534, gid:65534
(== overflow{u,g}id) instead of what we actually would like to see.
On the sender side, here nc -U, the tests in maybe_add_creds()
invoked through unix_stream_sendmsg() would fail, as at that exact
time, as mentioned, the sender has neither SO_PASSCRED on his side
nor sees it on the server side, and we have a valid 'other' socket
in place. Thus, sender believes it would just look like a normal
connection, not needing/requesting SO_PASSCRED at that time.
As reverting 16e5726 would not be an option due to the significant
performance regression reported when having creds always passed,
one way/trade-off to prevent that would be to set SO_PASSCRED on
the listener socket and allow inheriting these flags to the spawned
socket on server side in accept(). It seems also logical to do so
if we'd tell the listener socket to pass those flags onwards, and
would fix the race.
Before, strace:
recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"blub\n", 4096}],
msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET,
cmsg_type=SCM_CREDENTIALS{pid=0, uid=65534, gid=65534}},
msg_flags=0}, 0) = 5
After, strace:
recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"blub\n", 4096}],
msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET,
cmsg_type=SCM_CREDENTIALS{pid=11580, uid=1000, gid=1000}},
msg_flags=0}, 0) = 5
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-18 04:51:31 +08:00
|
|
|
static void unix_sock_inherit_flags(const struct socket *old,
|
|
|
|
struct socket *new)
|
|
|
|
{
|
|
|
|
if (test_bit(SOCK_PASSCRED, &old->flags))
|
|
|
|
set_bit(SOCK_PASSCRED, &new->flags);
|
2023-06-09 04:26:25 +08:00
|
|
|
if (test_bit(SOCK_PASSPIDFD, &old->flags))
|
|
|
|
set_bit(SOCK_PASSPIDFD, &new->flags);
|
net: unix: inherit SOCK_PASS{CRED, SEC} flags from socket to fix race
In the case of credentials passing in unix stream sockets (dgram
sockets seem not affected), we get a rather sparse race after
commit 16e5726 ("af_unix: dont send SCM_CREDENTIALS by default").
We have a stream server on receiver side that requests credential
passing from senders (e.g. nc -U). Since we need to set SO_PASSCRED
on each spawned/accepted socket on server side to 1 first (as it's
not inherited), it can happen that in the time between accept() and
setsockopt() we get interrupted, the sender is being scheduled and
continues with passing data to our receiver. At that time SO_PASSCRED
is neither set on sender nor receiver side, hence in cmsg's
SCM_CREDENTIALS we get eventually pid:0, uid:65534, gid:65534
(== overflow{u,g}id) instead of what we actually would like to see.
On the sender side, here nc -U, the tests in maybe_add_creds()
invoked through unix_stream_sendmsg() would fail, as at that exact
time, as mentioned, the sender has neither SO_PASSCRED on his side
nor sees it on the server side, and we have a valid 'other' socket
in place. Thus, sender believes it would just look like a normal
connection, not needing/requesting SO_PASSCRED at that time.
As reverting 16e5726 would not be an option due to the significant
performance regression reported when having creds always passed,
one way/trade-off to prevent that would be to set SO_PASSCRED on
the listener socket and allow inheriting these flags to the spawned
socket on server side in accept(). It seems also logical to do so
if we'd tell the listener socket to pass those flags onwards, and
would fix the race.
Before, strace:
recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"blub\n", 4096}],
msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET,
cmsg_type=SCM_CREDENTIALS{pid=0, uid=65534, gid=65534}},
msg_flags=0}, 0) = 5
After, strace:
recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"blub\n", 4096}],
msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET,
cmsg_type=SCM_CREDENTIALS{pid=11580, uid=1000, gid=1000}},
msg_flags=0}, 0) = 5
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-18 04:51:31 +08:00
|
|
|
if (test_bit(SOCK_PASSSEC, &old->flags))
|
|
|
|
set_bit(SOCK_PASSSEC, &new->flags);
|
|
|
|
}
|
|
|
|
|
2017-03-09 16:09:05 +08:00
|
|
|
static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
|
|
|
|
bool kern)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
struct sock *tsk;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = -EOPNOTSUPP;
|
2008-11-17 14:58:44 +08:00
|
|
|
if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
if (sk->sk_state != TCP_LISTEN)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* If socket state is TCP_LISTEN it cannot change (for now...),
|
|
|
|
* so that no locks are necessary.
|
|
|
|
*/
|
|
|
|
|
2022-04-05 00:30:22 +08:00
|
|
|
skb = skb_recv_datagram(sk, (flags & O_NONBLOCK) ? MSG_DONTWAIT : 0,
|
|
|
|
&err);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!skb) {
|
|
|
|
/* This means receive shutdown. */
|
|
|
|
if (err == 0)
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
tsk = skb->sk;
|
|
|
|
skb_free_datagram(sk, skb);
|
|
|
|
wake_up_interruptible(&unix_sk(sk)->peer_wait);
|
|
|
|
|
|
|
|
/* attach accepted sock to socket */
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(tsk);
|
2005-04-17 06:20:36 +08:00
|
|
|
newsock->state = SS_CONNECTED;
|
net: unix: inherit SOCK_PASS{CRED, SEC} flags from socket to fix race
In the case of credentials passing in unix stream sockets (dgram
sockets seem not affected), we get a rather sparse race after
commit 16e5726 ("af_unix: dont send SCM_CREDENTIALS by default").
We have a stream server on receiver side that requests credential
passing from senders (e.g. nc -U). Since we need to set SO_PASSCRED
on each spawned/accepted socket on server side to 1 first (as it's
not inherited), it can happen that in the time between accept() and
setsockopt() we get interrupted, the sender is being scheduled and
continues with passing data to our receiver. At that time SO_PASSCRED
is neither set on sender nor receiver side, hence in cmsg's
SCM_CREDENTIALS we get eventually pid:0, uid:65534, gid:65534
(== overflow{u,g}id) instead of what we actually would like to see.
On the sender side, here nc -U, the tests in maybe_add_creds()
invoked through unix_stream_sendmsg() would fail, as at that exact
time, as mentioned, the sender has neither SO_PASSCRED on his side
nor sees it on the server side, and we have a valid 'other' socket
in place. Thus, sender believes it would just look like a normal
connection, not needing/requesting SO_PASSCRED at that time.
As reverting 16e5726 would not be an option due to the significant
performance regression reported when having creds always passed,
one way/trade-off to prevent that would be to set SO_PASSCRED on
the listener socket and allow inheriting these flags to the spawned
socket on server side in accept(). It seems also logical to do so
if we'd tell the listener socket to pass those flags onwards, and
would fix the race.
Before, strace:
recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"blub\n", 4096}],
msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET,
cmsg_type=SCM_CREDENTIALS{pid=0, uid=65534, gid=65534}},
msg_flags=0}, 0) = 5
After, strace:
recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"blub\n", 4096}],
msg_controllen=32, {cmsg_len=28, cmsg_level=SOL_SOCKET,
cmsg_type=SCM_CREDENTIALS{pid=11580, uid=1000, gid=1000}},
msg_flags=0}, 0) = 5
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-18 04:51:31 +08:00
|
|
|
unix_sock_inherit_flags(sock, newsock);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_graft(tsk, newsock);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(tsk);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-13 03:00:20 +08:00
|
|
|
static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
2019-02-16 04:09:35 +08:00
|
|
|
struct unix_address *addr;
|
2009-11-08 13:51:19 +08:00
|
|
|
DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, uaddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (peer) {
|
|
|
|
sk = unix_peer_get(sk);
|
|
|
|
|
|
|
|
err = -ENOTCONN;
|
|
|
|
if (!sk)
|
|
|
|
goto out;
|
|
|
|
err = 0;
|
|
|
|
} else {
|
|
|
|
sock_hold(sk);
|
|
|
|
}
|
|
|
|
|
2019-02-16 04:09:35 +08:00
|
|
|
addr = smp_load_acquire(&unix_sk(sk)->addr);
|
|
|
|
if (!addr) {
|
2005-04-17 06:20:36 +08:00
|
|
|
sunaddr->sun_family = AF_UNIX;
|
|
|
|
sunaddr->sun_path[0] = 0;
|
2021-11-24 10:14:19 +08:00
|
|
|
err = offsetof(struct sockaddr_un, sun_path);
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
2018-02-13 03:00:20 +08:00
|
|
|
err = addr->len;
|
|
|
|
memcpy(sunaddr, addr->name, addr->len);
|
2023-10-12 02:51:06 +08:00
|
|
|
|
|
|
|
if (peer)
|
|
|
|
BPF_CGROUP_RUN_SA_PROG(sk, uaddr, &err,
|
|
|
|
CGROUP_UNIX_GETPEERNAME);
|
|
|
|
else
|
|
|
|
BPF_CGROUP_RUN_SA_PROG(sk, uaddr, &err,
|
|
|
|
CGROUP_UNIX_GETSOCKNAME);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
sock_put(sk);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-07-28 20:47:20 +08:00
|
|
|
static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
scm->fp = scm_fp_dup(UNIXCB(skb).fp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Garbage collection of unix sockets starts by selecting a set of
|
|
|
|
* candidate sockets which have reference only from being in flight
|
|
|
|
* (total_refs == inflight_refs). This condition is checked once during
|
|
|
|
* the candidate collection phase, and candidates are marked as such, so
|
|
|
|
* that non-candidates can later be ignored. While inflight_refs is
|
|
|
|
* protected by unix_gc_lock, total_refs (file count) is not, hence this
|
|
|
|
* is an instantaneous decision.
|
|
|
|
*
|
|
|
|
* Once a candidate, however, the socket must not be reinstalled into a
|
|
|
|
* file descriptor while the garbage collection is in progress.
|
|
|
|
*
|
|
|
|
* If the above conditions are met, then the directed graph of
|
|
|
|
* candidates (*) does not change while unix_gc_lock is held.
|
|
|
|
*
|
|
|
|
* Any operations that changes the file count through file descriptors
|
|
|
|
* (dup, close, sendmsg) does not change the graph since candidates are
|
|
|
|
* not installed in fds.
|
|
|
|
*
|
|
|
|
* Dequeing a candidate via recvmsg would install it into an fd, but
|
|
|
|
* that takes unix_gc_lock to decrement the inflight count, so it's
|
|
|
|
* serialized with garbage collection.
|
|
|
|
*
|
|
|
|
* MSG_PEEK is special in that it does not change the inflight count,
|
|
|
|
* yet does install the socket into an fd. The following lock/unlock
|
|
|
|
* pair is to ensure serialization with garbage collection. It must be
|
|
|
|
* done between incrementing the file count and installing the file into
|
|
|
|
* an fd.
|
|
|
|
*
|
|
|
|
* If garbage collection starts after the barrier provided by the
|
|
|
|
* lock/unlock, then it will see the elevated refcount and not mark this
|
|
|
|
* as a candidate. If a garbage collection is already in progress
|
|
|
|
* before the file count was incremented, then the lock/unlock pair will
|
|
|
|
* ensure that garbage collection is finished before progressing to
|
|
|
|
* installing the fd.
|
|
|
|
*
|
|
|
|
* (*) A -> B where B is on the queue of A or B is on the queue of C
|
|
|
|
* which is on the queue of listening socket A.
|
|
|
|
*/
|
|
|
|
spin_lock(&unix_gc_lock);
|
|
|
|
spin_unlock(&unix_gc_lock);
|
|
|
|
}
|
|
|
|
|
2011-09-17 07:34:00 +08:00
|
|
|
static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds)
|
2010-06-13 11:34:33 +08:00
|
|
|
{
|
|
|
|
int err = 0;
|
2011-09-19 13:52:27 +08:00
|
|
|
|
2011-09-17 07:34:00 +08:00
|
|
|
UNIXCB(skb).pid = get_pid(scm->pid);
|
2013-04-04 01:28:16 +08:00
|
|
|
UNIXCB(skb).uid = scm->creds.uid;
|
|
|
|
UNIXCB(skb).gid = scm->creds.gid;
|
2010-06-13 11:34:33 +08:00
|
|
|
UNIXCB(skb).fp = NULL;
|
2015-06-10 20:44:59 +08:00
|
|
|
unix_get_secdata(scm, skb);
|
2010-06-13 11:34:33 +08:00
|
|
|
if (scm->fp && send_fds)
|
|
|
|
err = unix_attach_fds(scm, skb);
|
|
|
|
|
|
|
|
skb->destructor = unix_destruct_scm;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-11-26 19:08:18 +08:00
|
|
|
static bool unix_passcred_enabled(const struct socket *sock,
|
|
|
|
const struct sock *other)
|
|
|
|
{
|
|
|
|
return test_bit(SOCK_PASSCRED, &sock->flags) ||
|
2023-06-09 04:26:25 +08:00
|
|
|
test_bit(SOCK_PASSPIDFD, &sock->flags) ||
|
2015-11-26 19:08:18 +08:00
|
|
|
!other->sk_socket ||
|
2023-06-09 04:26:25 +08:00
|
|
|
test_bit(SOCK_PASSCRED, &other->sk_socket->flags) ||
|
|
|
|
test_bit(SOCK_PASSPIDFD, &other->sk_socket->flags);
|
2015-11-26 19:08:18 +08:00
|
|
|
}
|
|
|
|
|
2011-09-19 13:52:27 +08:00
|
|
|
/*
|
|
|
|
* Some apps rely on write() giving SCM_CREDENTIALS
|
|
|
|
* We include credentials if source or destination socket
|
|
|
|
* asserted SOCK_PASSCRED.
|
|
|
|
*/
|
|
|
|
static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
|
|
|
|
const struct sock *other)
|
|
|
|
{
|
2013-04-04 01:28:16 +08:00
|
|
|
if (UNIXCB(skb).pid)
|
2011-09-19 13:52:27 +08:00
|
|
|
return;
|
2015-11-26 19:08:18 +08:00
|
|
|
if (unix_passcred_enabled(sock, other)) {
|
2011-09-19 13:52:27 +08:00
|
|
|
UNIXCB(skb).pid = get_pid(task_tgid(current));
|
2013-04-23 08:32:51 +08:00
|
|
|
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
|
2011-09-19 13:52:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-26 19:08:18 +08:00
|
|
|
static bool unix_skb_scm_eq(struct sk_buff *skb,
|
|
|
|
struct scm_cookie *scm)
|
|
|
|
{
|
2022-05-11 06:46:26 +08:00
|
|
|
return UNIXCB(skb).pid == scm->pid &&
|
|
|
|
uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
|
|
|
|
gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
|
2015-11-26 19:08:18 +08:00
|
|
|
unix_secdata_eq(scm, skb);
|
|
|
|
}
|
|
|
|
|
2019-12-09 18:03:46 +08:00
|
|
|
static void scm_stat_add(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct scm_fp_list *fp = UNIXCB(skb).fp;
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
|
|
|
|
if (unlikely(fp && fp->count))
|
2020-02-28 21:45:21 +08:00
|
|
|
atomic_add(fp->count, &u->scm_stat.nr_fds);
|
2019-12-09 18:03:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct scm_fp_list *fp = UNIXCB(skb).fp;
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
|
|
|
|
if (unlikely(fp && fp->count))
|
2020-02-28 21:45:21 +08:00
|
|
|
atomic_sub(fp->count, &u->scm_stat.nr_fds);
|
2019-12-09 18:03:46 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Send AF_UNIX data.
|
|
|
|
*/
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
|
|
|
|
size_t len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-01-18 05:53:15 +08:00
|
|
|
DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, msg->msg_name);
|
2022-06-22 01:19:08 +08:00
|
|
|
struct sock *sk = sock->sk, *other = NULL;
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
2015-01-29 01:04:53 +08:00
|
|
|
struct scm_cookie scm;
|
2022-06-22 01:19:08 +08:00
|
|
|
struct sk_buff *skb;
|
2012-04-03 13:28:28 +08:00
|
|
|
int data_len = 0;
|
2015-11-21 06:07:23 +08:00
|
|
|
int sk_locked;
|
2022-06-22 01:19:08 +08:00
|
|
|
long timeo;
|
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-11-27 07:32:27 +08:00
|
|
|
wait_for_unix_gc();
|
2015-01-29 01:04:53 +08:00
|
|
|
err = scm_send(sock, msg, &scm, false);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
if (msg->msg_flags&MSG_OOB)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (msg->msg_namelen) {
|
2021-11-24 10:14:23 +08:00
|
|
|
err = unix_validate_addr(sunaddr, msg->msg_namelen);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2023-10-12 02:51:06 +08:00
|
|
|
|
|
|
|
err = BPF_CGROUP_RUN_PROG_UNIX_SENDMSG_LOCK(sk,
|
|
|
|
msg->msg_name,
|
|
|
|
&msg->msg_namelen,
|
|
|
|
NULL);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
sunaddr = NULL;
|
|
|
|
err = -ENOTCONN;
|
|
|
|
other = unix_peer_get(sk);
|
|
|
|
if (!other)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2023-06-09 04:26:25 +08:00
|
|
|
if ((test_bit(SOCK_PASSCRED, &sock->flags) ||
|
|
|
|
test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) {
|
2021-11-24 10:14:20 +08:00
|
|
|
err = unix_autobind(sk);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err = -EMSGSIZE;
|
|
|
|
if (len > sk->sk_sndbuf - 32)
|
|
|
|
goto out;
|
|
|
|
|
2014-05-15 23:56:28 +08:00
|
|
|
if (len > SKB_MAX_ALLOC) {
|
2012-04-03 13:28:28 +08:00
|
|
|
data_len = min_t(size_t,
|
|
|
|
len - SKB_MAX_ALLOC,
|
|
|
|
MAX_SKB_FRAGS * PAGE_SIZE);
|
2014-05-15 23:56:28 +08:00
|
|
|
data_len = PAGE_ALIGN(data_len);
|
|
|
|
|
|
|
|
BUILD_BUG_ON(SKB_MAX_ALLOC < PAGE_SIZE);
|
|
|
|
}
|
2012-04-03 13:28:28 +08:00
|
|
|
|
|
|
|
skb = sock_alloc_send_pskb(sk, len - data_len, data_len,
|
2013-08-09 05:38:47 +08:00
|
|
|
msg->msg_flags & MSG_DONTWAIT, &err,
|
|
|
|
PAGE_ALLOC_COSTLY_ORDER);
|
2008-11-02 12:38:31 +08:00
|
|
|
if (skb == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
|
2015-01-29 01:04:53 +08:00
|
|
|
err = unix_scm_to_skb(&scm, skb, true);
|
2010-11-25 12:11:39 +08:00
|
|
|
if (err < 0)
|
2010-06-13 11:34:33 +08:00
|
|
|
goto out_free;
|
[AF_UNIX]: Datagram getpeersec
This patch implements an API whereby an application can determine the
label of its peer's Unix datagram sockets via the auxiliary data mechanism of
recvmsg.
Patch purpose:
This patch enables a security-aware application to retrieve the
security context of the peer of a Unix datagram socket. The application
can then use this security context to determine the security context for
processing on behalf of the peer who sent the packet.
Patch design and implementation:
The design and implementation is very similar to the UDP case for INET
sockets. Basically we build upon the existing Unix domain socket API for
retrieving user credentials. Linux offers the API for obtaining user
credentials via ancillary messages (i.e., out of band/control messages
that are bundled together with a normal message). To retrieve the security
context, the application first indicates to the kernel such desire by
setting the SO_PASSSEC option via getsockopt. Then the application
retrieves the security context using the auxiliary data mechanism.
An example server application for Unix datagram socket should look like this:
toggle = 1;
toggle_len = sizeof(toggle);
setsockopt(sockfd, SOL_SOCKET, SO_PASSSEC, &toggle, &toggle_len);
recvmsg(sockfd, &msg_hdr, 0);
if (msg_hdr.msg_controllen > sizeof(struct cmsghdr)) {
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg_hdr->cmsg_len <= CMSG_LEN(sizeof(scontext)) &&
cmsg_hdr->cmsg_level == SOL_SOCKET &&
cmsg_hdr->cmsg_type == SCM_SECURITY) {
memcpy(&scontext, CMSG_DATA(cmsg_hdr), sizeof(scontext));
}
}
sock_setsockopt is enhanced with a new socket option SOCK_PASSSEC to allow
a server socket to receive security context of the peer.
Testing:
We have tested the patch by setting up Unix datagram client and server
applications. We verified that the server can retrieve the security context
using the auxiliary data mechanism of recvmsg.
Signed-off-by: Catherine Zhang <cxzhang@watson.ibm.com>
Acked-by: Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-06-30 03:27:47 +08:00
|
|
|
|
2012-04-03 13:28:28 +08:00
|
|
|
skb_put(skb, len - data_len);
|
|
|
|
skb->data_len = data_len;
|
|
|
|
skb->len = len;
|
2014-11-24 23:42:55 +08:00
|
|
|
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err)
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
|
|
|
|
|
|
|
|
restart:
|
|
|
|
if (!other) {
|
|
|
|
err = -ECONNRESET;
|
|
|
|
if (sunaddr == NULL)
|
|
|
|
goto out_free;
|
|
|
|
|
2022-06-22 01:19:08 +08:00
|
|
|
other = unix_find_other(sock_net(sk), sunaddr, msg->msg_namelen,
|
2021-11-24 10:14:24 +08:00
|
|
|
sk->sk_type);
|
2021-11-24 10:14:22 +08:00
|
|
|
if (IS_ERR(other)) {
|
|
|
|
err = PTR_ERR(other);
|
|
|
|
other = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_free;
|
2021-11-24 10:14:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
af_unix: implement socket filter
Linux Socket Filters can already be successfully attached and detached on unix
sockets with setsockopt(sockfd, SOL_SOCKET, SO_{ATTACH,DETACH}_FILTER, ...).
See: Documentation/networking/filter.txt
But the filter was never used in the unix socket code so it did not work. This
patch uses sk_filter() to filter buffers before delivery.
This short program demonstrates the problem on SOCK_DGRAM.
int main(void) {
int i, j, ret;
int sv[2];
struct pollfd fds[2];
char *message = "Hello world!";
char buffer[64];
struct sock_filter ins[32] = {{0,},};
struct sock_fprog filter;
socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
for (i = 0 ; i < 2 ; i++) {
fds[i].fd = sv[i];
fds[i].events = POLLIN;
fds[i].revents = 0;
}
for(j = 1 ; j < 13 ; j++) {
/* Set a socket filter to truncate the message */
memset(ins, 0, sizeof(ins));
ins[0].code = BPF_RET|BPF_K;
ins[0].k = j;
filter.len = 1;
filter.filter = ins;
setsockopt(sv[1], SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
/* send a message */
send(sv[0], message, strlen(message) + 1, 0);
/* The filter should let the message pass but truncated. */
poll(fds, 2, 0);
/* Receive the truncated message*/
ret = recv(sv[1], buffer, 64, 0);
printf("received %d bytes, expected %d\n", ret, j);
}
for (i = 0 ; i < 2 ; i++)
close(sv[i]);
return 0;
}
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
Reviewed-by: Ian Molton <ian.molton@collabora.co.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-01-18 14:39:15 +08:00
|
|
|
if (sk_filter(other, skb) < 0) {
|
|
|
|
/* Toss the packet but do not return any error to the sender */
|
|
|
|
err = len;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
sk_locked = 0;
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(other);
|
2015-11-21 06:07:23 +08:00
|
|
|
restart_locked:
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EPERM;
|
|
|
|
if (!unix_may_send(sk, other))
|
|
|
|
goto out_unlock;
|
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
if (unlikely(sock_flag(other, SOCK_DEAD))) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Check with 1003.1g - what should
|
|
|
|
* datagram error
|
|
|
|
*/
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(other);
|
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
if (!sk_locked)
|
|
|
|
unix_state_lock(sk);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = 0;
|
unix: Fix race in SOCK_SEQPACKET's unix_dgram_sendmsg()
There is a race resulting in alive SOCK_SEQPACKET socket
may change its state from TCP_ESTABLISHED to TCP_CLOSE:
unix_release_sock(peer) unix_dgram_sendmsg(sk)
sock_orphan(peer)
sock_set_flag(peer, SOCK_DEAD)
sock_alloc_send_pskb()
if !(sk->sk_shutdown & SEND_SHUTDOWN)
OK
if sock_flag(peer, SOCK_DEAD)
sk->sk_state = TCP_CLOSE
sk->sk_shutdown = SHUTDOWN_MASK
After that socket sk remains almost normal: it is able to connect, listen, accept
and recvmsg, while it can't sendmsg.
Since this is the only possibility for alive SOCK_SEQPACKET to change
the state in such way, we should better fix this strange and potentially
danger corner case.
Note, that we will return EPIPE here like this is normally done in sock_alloc_send_pskb().
Originally used ECONNREFUSED looks strange, since it's strange to return
a specific retval in dependence of race in kernel, when user can't affect on this.
Also, move TCP_CLOSE assignment for SOCK_DGRAM sockets under state lock
to fix race with unix_dgram_connect():
unix_dgram_connect(other) unix_dgram_sendmsg(sk)
unix_peer(sk) = NULL
unix_state_unlock(sk)
unix_state_double_lock(sk, other)
sk->sk_state = TCP_ESTABLISHED
unix_peer(sk) = other
unix_state_double_unlock(sk, other)
sk->sk_state = TCP_CLOSED
This patch fixes both of these races.
Fixes: 83301b5367a9 ("af_unix: Set TCP_ESTABLISHED for datagram sockets too")
Signed-off-by: Kirill Tkhai <tkhai@ya.ru>
Link: https://lore.kernel.org/r/135fda25-22d5-837a-782b-ceee50e19844@ya.ru
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-12-13 05:05:53 +08:00
|
|
|
if (sk->sk_type == SOCK_SEQPACKET) {
|
|
|
|
/* We are here only when racing with unix_release_sock()
|
|
|
|
* is clearing @other. Never change state to TCP_CLOSE
|
|
|
|
* unlike SOCK_DGRAM wants.
|
|
|
|
*/
|
|
|
|
unix_state_unlock(sk);
|
|
|
|
err = -EPIPE;
|
|
|
|
} else if (unix_peer(sk) == other) {
|
2008-11-02 12:38:31 +08:00
|
|
|
unix_peer(sk) = NULL;
|
2015-11-21 06:07:23 +08:00
|
|
|
unix_dgram_peer_wake_disconnect_wakeup(sk, other);
|
|
|
|
|
unix: Fix race in SOCK_SEQPACKET's unix_dgram_sendmsg()
There is a race resulting in alive SOCK_SEQPACKET socket
may change its state from TCP_ESTABLISHED to TCP_CLOSE:
unix_release_sock(peer) unix_dgram_sendmsg(sk)
sock_orphan(peer)
sock_set_flag(peer, SOCK_DEAD)
sock_alloc_send_pskb()
if !(sk->sk_shutdown & SEND_SHUTDOWN)
OK
if sock_flag(peer, SOCK_DEAD)
sk->sk_state = TCP_CLOSE
sk->sk_shutdown = SHUTDOWN_MASK
After that socket sk remains almost normal: it is able to connect, listen, accept
and recvmsg, while it can't sendmsg.
Since this is the only possibility for alive SOCK_SEQPACKET to change
the state in such way, we should better fix this strange and potentially
danger corner case.
Note, that we will return EPIPE here like this is normally done in sock_alloc_send_pskb().
Originally used ECONNREFUSED looks strange, since it's strange to return
a specific retval in dependence of race in kernel, when user can't affect on this.
Also, move TCP_CLOSE assignment for SOCK_DGRAM sockets under state lock
to fix race with unix_dgram_connect():
unix_dgram_connect(other) unix_dgram_sendmsg(sk)
unix_peer(sk) = NULL
unix_state_unlock(sk)
unix_state_double_lock(sk, other)
sk->sk_state = TCP_ESTABLISHED
unix_peer(sk) = other
unix_state_double_unlock(sk, other)
sk->sk_state = TCP_CLOSED
This patch fixes both of these races.
Fixes: 83301b5367a9 ("af_unix: Set TCP_ESTABLISHED for datagram sockets too")
Signed-off-by: Kirill Tkhai <tkhai@ya.ru>
Link: https://lore.kernel.org/r/135fda25-22d5-837a-782b-ceee50e19844@ya.ru
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-12-13 05:05:53 +08:00
|
|
|
sk->sk_state = TCP_CLOSE;
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
unix_dgram_disconnected(sk, other);
|
|
|
|
sock_put(other);
|
|
|
|
err = -ECONNREFUSED;
|
|
|
|
} else {
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
other = NULL;
|
|
|
|
if (err)
|
|
|
|
goto out_free;
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = -EPIPE;
|
|
|
|
if (other->sk_shutdown & RCV_SHUTDOWN)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
if (sk->sk_type != SOCK_SEQPACKET) {
|
|
|
|
err = security_unix_may_send(sk->sk_socket, other->sk_socket);
|
|
|
|
if (err)
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2016-02-12 03:37:27 +08:00
|
|
|
/* other == sk && unix_peer(other) != sk if
|
|
|
|
* - unix_peer(sk) == NULL, destination address bound to sk
|
|
|
|
* - unix_peer(sk) == sk by time of get but disconnected before lock
|
|
|
|
*/
|
|
|
|
if (other != sk &&
|
2020-02-05 02:40:29 +08:00
|
|
|
unlikely(unix_peer(other) != sk &&
|
|
|
|
unix_recvq_full_lockless(other))) {
|
2015-11-21 06:07:23 +08:00
|
|
|
if (timeo) {
|
|
|
|
timeo = unix_wait_for_peer(other, timeo);
|
|
|
|
|
|
|
|
err = sock_intr_errno(timeo);
|
|
|
|
if (signal_pending(current))
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
goto restart;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
if (!sk_locked) {
|
|
|
|
unix_state_unlock(other);
|
|
|
|
unix_state_double_lock(sk, other);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
if (unix_peer(sk) != other ||
|
|
|
|
unix_dgram_peer_wake_me(sk, other)) {
|
|
|
|
err = -EAGAIN;
|
|
|
|
sk_locked = 1;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
if (!sk_locked) {
|
|
|
|
sk_locked = 1;
|
|
|
|
goto restart_locked;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-11-21 06:07:23 +08:00
|
|
|
if (unlikely(sk_locked))
|
|
|
|
unix_state_unlock(sk);
|
|
|
|
|
2010-10-04 16:48:28 +08:00
|
|
|
if (sock_flag(other, SOCK_RCVTSTAMP))
|
|
|
|
__net_timestamp(skb);
|
2011-09-19 13:52:27 +08:00
|
|
|
maybe_add_creds(skb, sock, other);
|
2019-12-09 18:03:46 +08:00
|
|
|
scm_stat_add(other, skb);
|
2020-02-28 21:45:21 +08:00
|
|
|
skb_queue_tail(&other->sk_receive_queue, skb);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2014-04-12 04:15:36 +08:00
|
|
|
other->sk_data_ready(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
sock_put(other);
|
2015-01-29 01:04:53 +08:00
|
|
|
scm_destroy(&scm);
|
2005-04-17 06:20:36 +08:00
|
|
|
return len;
|
|
|
|
|
|
|
|
out_unlock:
|
2015-11-21 06:07:23 +08:00
|
|
|
if (sk_locked)
|
|
|
|
unix_state_unlock(sk);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
out_free:
|
|
|
|
kfree_skb(skb);
|
|
|
|
out:
|
|
|
|
if (other)
|
|
|
|
sock_put(other);
|
2015-01-29 01:04:53 +08:00
|
|
|
scm_destroy(&scm);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-08-09 05:37:32 +08:00
|
|
|
/* We use paged skbs for stream sockets, and limit occupancy to 32768
|
2018-02-13 18:11:30 +08:00
|
|
|
* bytes, and a minimum of a full page.
|
2013-08-09 05:37:32 +08:00
|
|
|
*/
|
|
|
|
#define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768))
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2022-03-17 11:23:08 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
2023-03-08 00:45:30 +08:00
|
|
|
static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other,
|
|
|
|
struct scm_cookie *scm, bool fds_sent)
|
2021-08-01 15:57:07 +08:00
|
|
|
{
|
|
|
|
struct unix_sock *ousk = unix_sk(other);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
skb = sock_alloc_send_skb(sock->sk, 1, msg->msg_flags & MSG_DONTWAIT, &err);
|
|
|
|
|
|
|
|
if (!skb)
|
|
|
|
return err;
|
|
|
|
|
2023-03-08 00:45:30 +08:00
|
|
|
err = unix_scm_to_skb(scm, skb, !fds_sent);
|
|
|
|
if (err < 0) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return err;
|
|
|
|
}
|
2021-08-01 15:57:07 +08:00
|
|
|
skb_put(skb, 1);
|
|
|
|
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, 1);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
unix_state_lock(other);
|
2021-08-14 02:19:34 +08:00
|
|
|
|
|
|
|
if (sock_flag(other, SOCK_DEAD) ||
|
|
|
|
(other->sk_shutdown & RCV_SHUTDOWN)) {
|
|
|
|
unix_state_unlock(other);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EPIPE;
|
|
|
|
}
|
|
|
|
|
2021-08-01 15:57:07 +08:00
|
|
|
maybe_add_creds(skb, sock, other);
|
|
|
|
skb_get(skb);
|
|
|
|
|
|
|
|
if (ousk->oob_skb)
|
2021-08-14 02:19:34 +08:00
|
|
|
consume_skb(ousk->oob_skb);
|
2021-08-01 15:57:07 +08:00
|
|
|
|
af_unix: Fix some data-races around unix_sk(sk)->oob_skb.
Out-of-band data automatically places a "mark" showing wherein the
sequence the out-of-band data would have been. If the out-of-band data
implies cancelling everything sent so far, the "mark" is helpful to flush
them. When the socket's read pointer reaches the "mark", the ioctl() below
sets a non zero value to the arg `atmark`:
The out-of-band data is queued in sk->sk_receive_queue as well as ordinary
data and also saved in unix_sk(sk)->oob_skb. It can be used to test if the
head of the receive queue is the out-of-band data meaning the socket is at
the "mark".
While testing that, unix_ioctl() reads unix_sk(sk)->oob_skb locklessly.
Thus, all accesses to oob_skb need some basic protection to avoid
load/store tearing which KCSAN detects when these are called concurrently:
- ioctl(fd_a, SIOCATMARK, &atmark, sizeof(atmark))
- send(fd_b_connected_to_a, buf, sizeof(buf), MSG_OOB)
BUG: KCSAN: data-race in unix_ioctl / unix_stream_sendmsg
write to 0xffff888003d9cff0 of 8 bytes by task 175 on cpu 1:
unix_stream_sendmsg (net/unix/af_unix.c:2087 net/unix/af_unix.c:2191)
sock_sendmsg (net/socket.c:705 net/socket.c:725)
__sys_sendto (net/socket.c:2040)
__x64_sys_sendto (net/socket.c:2048)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
read to 0xffff888003d9cff0 of 8 bytes by task 176 on cpu 0:
unix_ioctl (net/unix/af_unix.c:3101 (discriminator 1))
sock_do_ioctl (net/socket.c:1128)
sock_ioctl (net/socket.c:1242)
__x64_sys_ioctl (fs/ioctl.c:52 fs/ioctl.c:874 fs/ioctl.c:860 fs/ioctl.c:860)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
value changed: 0xffff888003da0c00 -> 0xffff888003da0d00
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 176 Comm: unix_race_oob_i Not tainted 5.17.0-rc5-59529-g83dc4c2af682 #12
Hardware name: Red Hat KVM, BIOS 1.11.0-2.amzn2 04/01/2014
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-03-17 11:08:08 +08:00
|
|
|
WRITE_ONCE(ousk->oob_skb, skb);
|
2021-08-01 15:57:07 +08:00
|
|
|
|
|
|
|
scm_stat_add(other, skb);
|
|
|
|
skb_queue_tail(&other->sk_receive_queue, skb);
|
|
|
|
sk_send_sigurg(other);
|
|
|
|
unix_state_unlock(other);
|
|
|
|
other->sk_data_ready(other);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
|
|
|
size_t len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
struct sock *other = NULL;
|
2008-11-17 14:58:44 +08:00
|
|
|
int err, size;
|
2011-09-17 07:34:00 +08:00
|
|
|
struct sk_buff *skb;
|
2008-11-02 12:38:31 +08:00
|
|
|
int sent = 0;
|
2015-01-29 01:04:53 +08:00
|
|
|
struct scm_cookie scm;
|
net: unix: fix sending fds in multiple buffers
Kalle Olavi Niemitalo reported that:
"..., when one process calls sendmsg once to send 43804 bytes of
data and one file descriptor, and another process then calls recvmsg
three times to receive the 16032+16032+11740 bytes, each of those
recvmsg calls returns the file descriptor in the ancillary data. I
confirmed this with strace. The behaviour differs from Linux
2.6.26, where reportedly only one of those recvmsg calls (I think
the first one) returned the file descriptor."
This bug was introduced by a patch from me titled "net: unix: fix inflight
counting bug in garbage collector", commit 6209344f5.
And the reason is, quoting Kalle:
"Before your patch, unix_attach_fds() would set scm->fp = NULL, so
that if the loop in unix_stream_sendmsg() ran multiple iterations,
it could not call unix_attach_fds() again. But now,
unix_attach_fds() leaves scm->fp unchanged, and I think this causes
it to be called multiple times and duplicate the same file
descriptors to each struct sk_buff."
Fix this by introducing a flag that is cleared at the start and set
when the fds attached to the first buffer. The resulting code should
work equivalently to the one on 2.6.26.
Reported-by: Kalle Olavi Niemitalo <kon@iki.fi>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-09-12 02:31:45 +08:00
|
|
|
bool fds_sent = false;
|
2013-08-09 05:37:32 +08:00
|
|
|
int data_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-11-27 07:32:27 +08:00
|
|
|
wait_for_unix_gc();
|
2015-01-29 01:04:53 +08:00
|
|
|
err = scm_send(sock, msg, &scm, false);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = -EOPNOTSUPP;
|
2021-08-01 15:57:07 +08:00
|
|
|
if (msg->msg_flags & MSG_OOB) {
|
2022-03-17 11:23:08 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
2021-08-01 15:57:07 +08:00
|
|
|
if (len)
|
|
|
|
len--;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
goto out_err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (msg->msg_namelen) {
|
|
|
|
err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
|
|
|
} else {
|
|
|
|
err = -ENOTCONN;
|
2005-12-14 15:22:32 +08:00
|
|
|
other = unix_peer(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!other)
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sk->sk_shutdown & SEND_SHUTDOWN)
|
|
|
|
goto pipe_err;
|
|
|
|
|
2008-11-17 14:58:44 +08:00
|
|
|
while (sent < len) {
|
2013-08-09 05:37:32 +08:00
|
|
|
size = len - sent;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-05-22 20:11:24 +08:00
|
|
|
if (unlikely(msg->msg_flags & MSG_SPLICE_PAGES)) {
|
|
|
|
skb = sock_alloc_send_pskb(sk, 0, 0,
|
|
|
|
msg->msg_flags & MSG_DONTWAIT,
|
|
|
|
&err, 0);
|
|
|
|
} else {
|
|
|
|
/* Keep two messages in the pipe so it schedules better */
|
|
|
|
size = min_t(int, size, (sk->sk_sndbuf >> 1) - 64);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-05-22 20:11:24 +08:00
|
|
|
/* allow fallback to order-0 allocations */
|
|
|
|
size = min_t(int, size, SKB_MAX_HEAD(0) + UNIX_SKB_FRAGS_SZ);
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2023-05-22 20:11:24 +08:00
|
|
|
data_len = max_t(int, 0, size - SKB_MAX_HEAD(0));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-05-22 20:11:24 +08:00
|
|
|
data_len = min_t(size_t, size, PAGE_ALIGN(data_len));
|
2014-05-15 23:56:28 +08:00
|
|
|
|
2023-05-22 20:11:24 +08:00
|
|
|
skb = sock_alloc_send_pskb(sk, size - data_len, data_len,
|
|
|
|
msg->msg_flags & MSG_DONTWAIT, &err,
|
|
|
|
get_order(UNIX_SKB_FRAGS_SZ));
|
|
|
|
}
|
2013-08-09 05:37:32 +08:00
|
|
|
if (!skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_err;
|
|
|
|
|
2011-09-17 07:34:00 +08:00
|
|
|
/* Only send the fds in the first buffer */
|
2015-01-29 01:04:53 +08:00
|
|
|
err = unix_scm_to_skb(&scm, skb, !fds_sent);
|
2010-11-25 12:11:39 +08:00
|
|
|
if (err < 0) {
|
2010-06-13 11:34:33 +08:00
|
|
|
kfree_skb(skb);
|
2011-09-17 07:34:00 +08:00
|
|
|
goto out_err;
|
2008-11-09 22:23:57 +08:00
|
|
|
}
|
2010-06-13 11:34:33 +08:00
|
|
|
fds_sent = true;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-05-22 20:11:24 +08:00
|
|
|
if (unlikely(msg->msg_flags & MSG_SPLICE_PAGES)) {
|
|
|
|
err = skb_splice_from_iter(skb, &msg->msg_iter, size,
|
|
|
|
sk->sk_allocation);
|
|
|
|
if (err < 0) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
size = err;
|
|
|
|
refcount_add(size, &sk->sk_wmem_alloc);
|
|
|
|
} else {
|
|
|
|
skb_put(skb, size - data_len);
|
|
|
|
skb->data_len = data_len;
|
|
|
|
skb->len = size;
|
|
|
|
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
|
|
|
|
if (err) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
goto out_err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (sock_flag(other, SOCK_DEAD) ||
|
|
|
|
(other->sk_shutdown & RCV_SHUTDOWN))
|
|
|
|
goto pipe_err_free;
|
|
|
|
|
2011-09-19 13:52:27 +08:00
|
|
|
maybe_add_creds(skb, sock, other);
|
2019-12-09 18:03:46 +08:00
|
|
|
scm_stat_add(other, skb);
|
2020-02-28 21:45:21 +08:00
|
|
|
skb_queue_tail(&other->sk_receive_queue, skb);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2014-04-12 04:15:36 +08:00
|
|
|
other->sk_data_ready(other);
|
2008-11-02 12:38:31 +08:00
|
|
|
sent += size;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2022-03-17 11:23:08 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
2021-08-01 15:57:07 +08:00
|
|
|
if (msg->msg_flags & MSG_OOB) {
|
2023-03-08 00:45:30 +08:00
|
|
|
err = queue_oob(sock, msg, other, &scm, fds_sent);
|
2021-08-01 15:57:07 +08:00
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
sent++;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-01-29 01:04:53 +08:00
|
|
|
scm_destroy(&scm);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return sent;
|
|
|
|
|
|
|
|
pipe_err_free:
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(other);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
pipe_err:
|
2008-11-17 14:58:44 +08:00
|
|
|
if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
|
|
|
|
send_sig(SIGPIPE, current, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EPIPE;
|
|
|
|
out_err:
|
2015-01-29 01:04:53 +08:00
|
|
|
scm_destroy(&scm);
|
2005-04-17 06:20:36 +08:00
|
|
|
return sent ? : err;
|
|
|
|
}
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_seqpacket_sendmsg(struct socket *sock, struct msghdr *msg,
|
|
|
|
size_t len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct sock *sk = sock->sk;
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = sock_error(sk);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (sk->sk_state != TCP_ESTABLISHED)
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
|
|
|
if (msg->msg_namelen)
|
|
|
|
msg->msg_namelen = 0;
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
return unix_dgram_sendmsg(sock, msg, len);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg,
|
|
|
|
size_t size, int flags)
|
2011-04-24 09:54:57 +08:00
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
|
|
|
|
if (sk->sk_state != TCP_ESTABLISHED)
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2015-03-02 15:37:48 +08:00
|
|
|
return unix_dgram_recvmsg(sock, msg, size, flags);
|
2011-04-24 09:54:57 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void unix_copy_addr(struct msghdr *msg, struct sock *sk)
|
|
|
|
{
|
2019-02-16 04:09:35 +08:00
|
|
|
struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-02-16 04:09:35 +08:00
|
|
|
if (addr) {
|
|
|
|
msg->msg_namelen = addr->len;
|
|
|
|
memcpy(msg->msg_name, addr->name, addr->len);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-05 03:02:48 +08:00
|
|
|
int __unix_dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t size,
|
|
|
|
int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-01-29 01:04:53 +08:00
|
|
|
struct scm_cookie scm;
|
2021-07-05 03:02:48 +08:00
|
|
|
struct socket *sock = sk->sk_socket;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct unix_sock *u = unix_sk(sk);
|
2015-12-07 05:11:38 +08:00
|
|
|
struct sk_buff *skb, *last;
|
|
|
|
long timeo;
|
2019-04-08 16:15:59 +08:00
|
|
|
int skip;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
if (flags&MSG_OOB)
|
|
|
|
goto out;
|
|
|
|
|
2015-12-07 05:11:38 +08:00
|
|
|
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-12-07 05:11:38 +08:00
|
|
|
do {
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_lock(&u->iolock);
|
2012-02-21 15:31:51 +08:00
|
|
|
|
2015-12-07 05:11:38 +08:00
|
|
|
skip = sk_peek_offset(sk, flags);
|
2019-11-25 21:48:57 +08:00
|
|
|
skb = __skb_try_recv_datagram(sk, &sk->sk_receive_queue, flags,
|
2020-02-28 21:45:22 +08:00
|
|
|
&skip, &err, &last);
|
|
|
|
if (skb) {
|
|
|
|
if (!(flags & MSG_PEEK))
|
|
|
|
scm_stat_del(sk, skb);
|
2015-12-07 05:11:38 +08:00
|
|
|
break;
|
2020-02-28 21:45:22 +08:00
|
|
|
}
|
2015-12-07 05:11:38 +08:00
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_unlock(&u->iolock);
|
2015-12-07 05:11:38 +08:00
|
|
|
|
|
|
|
if (err != -EAGAIN)
|
|
|
|
break;
|
|
|
|
} while (timeo &&
|
2019-11-25 21:48:57 +08:00
|
|
|
!__skb_wait_for_more_packets(sk, &sk->sk_receive_queue,
|
|
|
|
&err, &timeo, last));
|
2015-12-07 05:11:38 +08:00
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
if (!skb) { /* implies iolock unlocked */
|
[UNIX]: EOF on non-blocking SOCK_SEQPACKET
I am not absolutely sure whether this actually is a bug (as in: I've got
no clue what the standards say or what other implementations do), but at
least I was pretty surprised when I noticed that a recv() on a
non-blocking unix domain socket of type SOCK_SEQPACKET (which is connection
oriented, after all) where the remote end has closed the connection
returned -1 (EAGAIN) rather than 0 to indicate end of file.
This is a test case:
| #include <sys/types.h>
| #include <unistd.h>
| #include <sys/socket.h>
| #include <sys/un.h>
| #include <fcntl.h>
| #include <string.h>
| #include <stdlib.h>
|
| int main(){
| int sock;
| struct sockaddr_un addr;
| char buf[4096];
| int pfds[2];
|
| pipe(pfds);
| sock=socket(PF_UNIX,SOCK_SEQPACKET,0);
| addr.sun_family=AF_UNIX;
| strcpy(addr.sun_path,"/tmp/foobar_testsock");
| bind(sock,(struct sockaddr *)&addr,sizeof(addr));
| listen(sock,1);
| if(fork()){
| close(sock);
| sock=socket(PF_UNIX,SOCK_SEQPACKET,0);
| connect(sock,(struct sockaddr *)&addr,sizeof(addr));
| fcntl(sock,F_SETFL,fcntl(sock,F_GETFL)|O_NONBLOCK);
| close(pfds[1]);
| read(pfds[0],buf,sizeof(buf));
| recv(sock,buf,sizeof(buf),0); // <-- this one
| }else accept(sock,NULL,NULL);
| exit(0);
| }
If you try it, make sure /tmp/foobar_testsock doesn't exist.
The marked recv() returns -1 (EAGAIN) on 2.6.23.9. Below you find a
patch that fixes that.
Signed-off-by: Florian Zumbiehl <florz@florz.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2007-11-29 20:19:23 +08:00
|
|
|
unix_state_lock(sk);
|
|
|
|
/* Signal EOF on disconnected non-blocking SEQPACKET socket. */
|
|
|
|
if (sk->sk_type == SOCK_SEQPACKET && err == -EAGAIN &&
|
|
|
|
(sk->sk_shutdown & RCV_SHUTDOWN))
|
|
|
|
err = 0;
|
|
|
|
unix_state_unlock(sk);
|
2015-12-07 05:11:38 +08:00
|
|
|
goto out;
|
[UNIX]: EOF on non-blocking SOCK_SEQPACKET
I am not absolutely sure whether this actually is a bug (as in: I've got
no clue what the standards say or what other implementations do), but at
least I was pretty surprised when I noticed that a recv() on a
non-blocking unix domain socket of type SOCK_SEQPACKET (which is connection
oriented, after all) where the remote end has closed the connection
returned -1 (EAGAIN) rather than 0 to indicate end of file.
This is a test case:
| #include <sys/types.h>
| #include <unistd.h>
| #include <sys/socket.h>
| #include <sys/un.h>
| #include <fcntl.h>
| #include <string.h>
| #include <stdlib.h>
|
| int main(){
| int sock;
| struct sockaddr_un addr;
| char buf[4096];
| int pfds[2];
|
| pipe(pfds);
| sock=socket(PF_UNIX,SOCK_SEQPACKET,0);
| addr.sun_family=AF_UNIX;
| strcpy(addr.sun_path,"/tmp/foobar_testsock");
| bind(sock,(struct sockaddr *)&addr,sizeof(addr));
| listen(sock,1);
| if(fork()){
| close(sock);
| sock=socket(PF_UNIX,SOCK_SEQPACKET,0);
| connect(sock,(struct sockaddr *)&addr,sizeof(addr));
| fcntl(sock,F_SETFL,fcntl(sock,F_GETFL)|O_NONBLOCK);
| close(pfds[1]);
| read(pfds[0],buf,sizeof(buf));
| recv(sock,buf,sizeof(buf),0); // <-- this one
| }else accept(sock,NULL,NULL);
| exit(0);
| }
If you try it, make sure /tmp/foobar_testsock doesn't exist.
The marked recv() returns -1 (EAGAIN) on 2.6.23.9. Below you find a
patch that fixes that.
Signed-off-by: Florian Zumbiehl <florz@florz.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2007-11-29 20:19:23 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-11-27 03:23:15 +08:00
|
|
|
if (wq_has_sleeper(&u->peer_wait))
|
|
|
|
wake_up_interruptible_sync_poll(&u->peer_wait,
|
2018-02-12 06:34:03 +08:00
|
|
|
EPOLLOUT | EPOLLWRNORM |
|
|
|
|
EPOLLWRBAND);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-10-12 02:51:06 +08:00
|
|
|
if (msg->msg_name) {
|
2005-04-17 06:20:36 +08:00
|
|
|
unix_copy_addr(msg, skb->sk);
|
|
|
|
|
2023-10-12 02:51:06 +08:00
|
|
|
BPF_CGROUP_RUN_PROG_UNIX_RECVMSG_LOCK(sk,
|
|
|
|
msg->msg_name,
|
|
|
|
&msg->msg_namelen);
|
|
|
|
}
|
|
|
|
|
2012-02-21 15:31:51 +08:00
|
|
|
if (size > skb->len - skip)
|
|
|
|
size = skb->len - skip;
|
|
|
|
else if (size < skb->len - skip)
|
2005-04-17 06:20:36 +08:00
|
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
|
|
|
2014-11-06 05:46:40 +08:00
|
|
|
err = skb_copy_datagram_msg(skb, skip, msg, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err)
|
|
|
|
goto out_free;
|
|
|
|
|
2010-10-04 16:48:28 +08:00
|
|
|
if (sock_flag(sk, SOCK_RCVTSTAMP))
|
|
|
|
__sock_recv_timestamp(msg, sk, skb);
|
|
|
|
|
2015-01-29 01:04:53 +08:00
|
|
|
memset(&scm, 0, sizeof(scm));
|
|
|
|
|
|
|
|
scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
|
|
|
|
unix_set_secdata(&scm, skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-11-17 14:58:44 +08:00
|
|
|
if (!(flags & MSG_PEEK)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (UNIXCB(skb).fp)
|
2015-01-29 01:04:53 +08:00
|
|
|
unix_detach_fds(&scm, skb);
|
2012-02-21 15:31:51 +08:00
|
|
|
|
|
|
|
sk_peek_offset_bwd(sk, skb->len);
|
2008-11-17 14:58:44 +08:00
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* It is questionable: on PEEK we could:
|
|
|
|
- do not return fds - good, but too simple 8)
|
|
|
|
- return fds, and do not return them on read (old strategy,
|
|
|
|
apparently wrong)
|
|
|
|
- clone fds (I chose it for now, it is the most universal
|
|
|
|
solution)
|
2007-02-09 22:25:23 +08:00
|
|
|
|
|
|
|
POSIX 1003.1g does not actually define this clearly
|
|
|
|
at all. POSIX 1003.1g doesn't define a lot of things
|
|
|
|
clearly however!
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2012-02-21 15:31:51 +08:00
|
|
|
|
|
|
|
sk_peek_offset_fwd(sk, size);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (UNIXCB(skb).fp)
|
2021-07-28 20:47:20 +08:00
|
|
|
unix_peek_fds(&scm, skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-02-22 07:24:55 +08:00
|
|
|
err = (flags & MSG_TRUNC) ? skb->len - skip : size;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-06-28 01:43:14 +08:00
|
|
|
scm_recv_unix(sock, msg, &scm, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
out_free:
|
2008-11-17 14:58:44 +08:00
|
|
|
skb_free_datagram(sk, skb);
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_unlock(&u->iolock);
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
2021-07-05 03:02:44 +08:00
|
|
|
|
2021-07-05 03:02:48 +08:00
|
|
|
static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
|
|
|
int flags)
|
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
|
|
|
|
#ifdef CONFIG_BPF_SYSCALL
|
2021-08-17 03:03:21 +08:00
|
|
|
const struct proto *prot = READ_ONCE(sk->sk_prot);
|
|
|
|
|
|
|
|
if (prot != &unix_dgram_proto)
|
net: remove noblock parameter from recvmsg() entities
The internal recvmsg() functions have two parameters 'flags' and 'noblock'
that were merged inside skb_recv_datagram(). As a follow up patch to commit
f4b41f062c42 ("net: remove noblock parameter from skb_recv_datagram()")
this patch removes the separate 'noblock' parameter for recvmsg().
Analogue to the referenced patch for skb_recv_datagram() the 'flags' and
'noblock' parameters are unnecessarily split up with e.g.
err = sk->sk_prot->recvmsg(sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
or in
err = INDIRECT_CALL_2(sk->sk_prot->recvmsg, tcp_recvmsg, udp_recvmsg,
sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
instead of simply using only flags all the time and check for MSG_DONTWAIT
where needed (to preserve for the formerly separated no(n)block condition).
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/r/20220411124955.154876-1-socketcan@hartkopp.net
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-04-11 20:49:55 +08:00
|
|
|
return prot->recvmsg(sk, msg, size, flags, NULL);
|
2021-07-05 03:02:48 +08:00
|
|
|
#endif
|
|
|
|
return __unix_dgram_recvmsg(sk, msg, size, flags);
|
|
|
|
}
|
|
|
|
|
2022-06-16 00:20:12 +08:00
|
|
|
static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
|
2021-07-05 03:02:44 +08:00
|
|
|
{
|
2022-09-23 12:59:26 +08:00
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
struct sk_buff *skb;
|
2023-05-23 10:56:05 +08:00
|
|
|
int err;
|
2021-07-05 03:02:44 +08:00
|
|
|
|
2022-09-23 12:59:26 +08:00
|
|
|
mutex_lock(&u->iolock);
|
|
|
|
skb = skb_recv_datagram(sk, MSG_DONTWAIT, &err);
|
|
|
|
mutex_unlock(&u->iolock);
|
|
|
|
if (!skb)
|
|
|
|
return err;
|
2021-07-05 03:02:44 +08:00
|
|
|
|
2023-05-23 10:56:05 +08:00
|
|
|
return recv_actor(sk, skb);
|
2021-07-05 03:02:44 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
2013-04-29 19:42:14 +08:00
|
|
|
* Sleep until more data has arrived. But check for races..
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2013-04-29 19:42:14 +08:00
|
|
|
static long unix_stream_data_wait(struct sock *sk, long timeo,
|
2016-11-18 07:55:26 +08:00
|
|
|
struct sk_buff *last, unsigned int last_len,
|
|
|
|
bool freezable)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
freezer,sched: Rewrite core freezer logic
Rewrite the core freezer to behave better wrt thawing and be simpler
in general.
By replacing PF_FROZEN with TASK_FROZEN, a special block state, it is
ensured frozen tasks stay frozen until thawed and don't randomly wake
up early, as is currently possible.
As such, it does away with PF_FROZEN and PF_FREEZER_SKIP, freeing up
two PF_flags (yay!).
Specifically; the current scheme works a little like:
freezer_do_not_count();
schedule();
freezer_count();
And either the task is blocked, or it lands in try_to_freezer()
through freezer_count(). Now, when it is blocked, the freezer
considers it frozen and continues.
However, on thawing, once pm_freezing is cleared, freezer_count()
stops working, and any random/spurious wakeup will let a task run
before its time.
That is, thawing tries to thaw things in explicit order; kernel
threads and workqueues before doing bringing SMP back before userspace
etc.. However due to the above mentioned races it is entirely possible
for userspace tasks to thaw (by accident) before SMP is back.
This can be a fatal problem in asymmetric ISA architectures (eg ARMv9)
where the userspace task requires a special CPU to run.
As said; replace this with a special task state TASK_FROZEN and add
the following state transitions:
TASK_FREEZABLE -> TASK_FROZEN
__TASK_STOPPED -> TASK_FROZEN
__TASK_TRACED -> TASK_FROZEN
The new TASK_FREEZABLE can be set on any state part of TASK_NORMAL
(IOW. TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE) -- any such state
is already required to deal with spurious wakeups and the freezer
causes one such when thawing the task (since the original state is
lost).
The special __TASK_{STOPPED,TRACED} states *can* be restored since
their canonical state is in ->jobctl.
With this, frozen tasks need an explicit TASK_FROZEN wakeup and are
free of undue (early / spurious) wakeups.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20220822114649.055452969@infradead.org
2022-08-22 19:18:22 +08:00
|
|
|
unsigned int state = TASK_INTERRUPTIBLE | freezable * TASK_FREEZABLE;
|
2015-05-21 23:00:01 +08:00
|
|
|
struct sk_buff *tail;
|
2005-04-17 06:20:36 +08:00
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for (;;) {
|
freezer,sched: Rewrite core freezer logic
Rewrite the core freezer to behave better wrt thawing and be simpler
in general.
By replacing PF_FROZEN with TASK_FROZEN, a special block state, it is
ensured frozen tasks stay frozen until thawed and don't randomly wake
up early, as is currently possible.
As such, it does away with PF_FROZEN and PF_FREEZER_SKIP, freeing up
two PF_flags (yay!).
Specifically; the current scheme works a little like:
freezer_do_not_count();
schedule();
freezer_count();
And either the task is blocked, or it lands in try_to_freezer()
through freezer_count(). Now, when it is blocked, the freezer
considers it frozen and continues.
However, on thawing, once pm_freezing is cleared, freezer_count()
stops working, and any random/spurious wakeup will let a task run
before its time.
That is, thawing tries to thaw things in explicit order; kernel
threads and workqueues before doing bringing SMP back before userspace
etc.. However due to the above mentioned races it is entirely possible
for userspace tasks to thaw (by accident) before SMP is back.
This can be a fatal problem in asymmetric ISA architectures (eg ARMv9)
where the userspace task requires a special CPU to run.
As said; replace this with a special task state TASK_FROZEN and add
the following state transitions:
TASK_FREEZABLE -> TASK_FROZEN
__TASK_STOPPED -> TASK_FROZEN
__TASK_TRACED -> TASK_FROZEN
The new TASK_FREEZABLE can be set on any state part of TASK_NORMAL
(IOW. TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE) -- any such state
is already required to deal with spurious wakeups and the freezer
causes one such when thawing the task (since the original state is
lost).
The special __TASK_{STOPPED,TRACED} states *can* be restored since
their canonical state is in ->jobctl.
With this, frozen tasks need an explicit TASK_FROZEN wakeup and are
free of undue (early / spurious) wakeups.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20220822114649.055452969@infradead.org
2022-08-22 19:18:22 +08:00
|
|
|
prepare_to_wait(sk_sleep(sk), &wait, state);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
tail = skb_peek_tail(&sk->sk_receive_queue);
|
|
|
|
if (tail != last ||
|
|
|
|
(tail && tail->len != last_len) ||
|
2005-04-17 06:20:36 +08:00
|
|
|
sk->sk_err ||
|
|
|
|
(sk->sk_shutdown & RCV_SHUTDOWN) ||
|
|
|
|
signal_pending(current) ||
|
|
|
|
!timeo)
|
|
|
|
break;
|
|
|
|
|
2015-11-30 12:03:10 +08:00
|
|
|
sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
freezer,sched: Rewrite core freezer logic
Rewrite the core freezer to behave better wrt thawing and be simpler
in general.
By replacing PF_FROZEN with TASK_FROZEN, a special block state, it is
ensured frozen tasks stay frozen until thawed and don't randomly wake
up early, as is currently possible.
As such, it does away with PF_FROZEN and PF_FREEZER_SKIP, freeing up
two PF_flags (yay!).
Specifically; the current scheme works a little like:
freezer_do_not_count();
schedule();
freezer_count();
And either the task is blocked, or it lands in try_to_freezer()
through freezer_count(). Now, when it is blocked, the freezer
considers it frozen and continues.
However, on thawing, once pm_freezing is cleared, freezer_count()
stops working, and any random/spurious wakeup will let a task run
before its time.
That is, thawing tries to thaw things in explicit order; kernel
threads and workqueues before doing bringing SMP back before userspace
etc.. However due to the above mentioned races it is entirely possible
for userspace tasks to thaw (by accident) before SMP is back.
This can be a fatal problem in asymmetric ISA architectures (eg ARMv9)
where the userspace task requires a special CPU to run.
As said; replace this with a special task state TASK_FROZEN and add
the following state transitions:
TASK_FREEZABLE -> TASK_FROZEN
__TASK_STOPPED -> TASK_FROZEN
__TASK_TRACED -> TASK_FROZEN
The new TASK_FREEZABLE can be set on any state part of TASK_NORMAL
(IOW. TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE) -- any such state
is already required to deal with spurious wakeups and the freezer
causes one such when thawing the task (since the original state is
lost).
The special __TASK_{STOPPED,TRACED} states *can* be restored since
their canonical state is in ->jobctl.
With this, frozen tasks need an explicit TASK_FROZEN wakeup and are
free of undue (early / spurious) wakeups.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://lore.kernel.org/r/20220822114649.055452969@infradead.org
2022-08-22 19:18:22 +08:00
|
|
|
timeo = schedule_timeout(timeo);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(sk);
|
2015-05-26 23:22:19 +08:00
|
|
|
|
|
|
|
if (sock_flag(sk, SOCK_DEAD))
|
|
|
|
break;
|
|
|
|
|
2015-11-30 12:03:10 +08:00
|
|
|
sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-04-20 21:03:51 +08:00
|
|
|
finish_wait(sk_sleep(sk), &wait);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
return timeo;
|
|
|
|
}
|
|
|
|
|
2013-08-09 05:37:32 +08:00
|
|
|
static unsigned int unix_skb_len(const struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
return skb->len - UNIXCB(skb).consumed;
|
|
|
|
}
|
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
struct unix_stream_read_state {
|
|
|
|
int (*recv_actor)(struct sk_buff *, int, int,
|
|
|
|
struct unix_stream_read_state *);
|
|
|
|
struct socket *socket;
|
|
|
|
struct msghdr *msg;
|
|
|
|
struct pipe_inode_info *pipe;
|
|
|
|
size_t size;
|
|
|
|
int flags;
|
|
|
|
unsigned int splice_flags;
|
|
|
|
};
|
|
|
|
|
2021-08-01 15:57:07 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
|
|
|
static int unix_stream_recv_urg(struct unix_stream_read_state *state)
|
|
|
|
{
|
|
|
|
struct socket *sock = state->socket;
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
int chunk = 1;
|
2021-08-12 06:06:52 +08:00
|
|
|
struct sk_buff *oob_skb;
|
2021-08-01 15:57:07 +08:00
|
|
|
|
2021-08-12 06:06:52 +08:00
|
|
|
mutex_lock(&u->iolock);
|
|
|
|
unix_state_lock(sk);
|
|
|
|
|
|
|
|
if (sock_flag(sk, SOCK_URGINLINE) || !u->oob_skb) {
|
|
|
|
unix_state_unlock(sk);
|
|
|
|
mutex_unlock(&u->iolock);
|
2021-08-01 15:57:07 +08:00
|
|
|
return -EINVAL;
|
2021-08-12 06:06:52 +08:00
|
|
|
}
|
2021-08-01 15:57:07 +08:00
|
|
|
|
2021-08-12 06:06:52 +08:00
|
|
|
oob_skb = u->oob_skb;
|
2021-08-01 15:57:07 +08:00
|
|
|
|
af_unix: Fix some data-races around unix_sk(sk)->oob_skb.
Out-of-band data automatically places a "mark" showing wherein the
sequence the out-of-band data would have been. If the out-of-band data
implies cancelling everything sent so far, the "mark" is helpful to flush
them. When the socket's read pointer reaches the "mark", the ioctl() below
sets a non zero value to the arg `atmark`:
The out-of-band data is queued in sk->sk_receive_queue as well as ordinary
data and also saved in unix_sk(sk)->oob_skb. It can be used to test if the
head of the receive queue is the out-of-band data meaning the socket is at
the "mark".
While testing that, unix_ioctl() reads unix_sk(sk)->oob_skb locklessly.
Thus, all accesses to oob_skb need some basic protection to avoid
load/store tearing which KCSAN detects when these are called concurrently:
- ioctl(fd_a, SIOCATMARK, &atmark, sizeof(atmark))
- send(fd_b_connected_to_a, buf, sizeof(buf), MSG_OOB)
BUG: KCSAN: data-race in unix_ioctl / unix_stream_sendmsg
write to 0xffff888003d9cff0 of 8 bytes by task 175 on cpu 1:
unix_stream_sendmsg (net/unix/af_unix.c:2087 net/unix/af_unix.c:2191)
sock_sendmsg (net/socket.c:705 net/socket.c:725)
__sys_sendto (net/socket.c:2040)
__x64_sys_sendto (net/socket.c:2048)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
read to 0xffff888003d9cff0 of 8 bytes by task 176 on cpu 0:
unix_ioctl (net/unix/af_unix.c:3101 (discriminator 1))
sock_do_ioctl (net/socket.c:1128)
sock_ioctl (net/socket.c:1242)
__x64_sys_ioctl (fs/ioctl.c:52 fs/ioctl.c:874 fs/ioctl.c:860 fs/ioctl.c:860)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
value changed: 0xffff888003da0c00 -> 0xffff888003da0d00
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 176 Comm: unix_race_oob_i Not tainted 5.17.0-rc5-59529-g83dc4c2af682 #12
Hardware name: Red Hat KVM, BIOS 1.11.0-2.amzn2 04/01/2014
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-03-17 11:08:08 +08:00
|
|
|
if (!(state->flags & MSG_PEEK))
|
|
|
|
WRITE_ONCE(u->oob_skb, NULL);
|
2021-08-12 06:06:52 +08:00
|
|
|
|
|
|
|
unix_state_unlock(sk);
|
|
|
|
|
|
|
|
chunk = state->recv_actor(oob_skb, 0, chunk, state);
|
|
|
|
|
|
|
|
if (!(state->flags & MSG_PEEK)) {
|
|
|
|
UNIXCB(oob_skb).consumed += 1;
|
|
|
|
kfree_skb(oob_skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&u->iolock);
|
|
|
|
|
|
|
|
if (chunk < 0)
|
|
|
|
return -EFAULT;
|
|
|
|
|
2021-08-01 15:57:07 +08:00
|
|
|
state->msg->msg_flags |= MSG_OOB;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
|
|
|
|
int flags, int copied)
|
|
|
|
{
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
|
|
|
|
if (!unix_skb_len(skb) && !(flags & MSG_PEEK)) {
|
|
|
|
skb_unlink(skb, &sk->sk_receive_queue);
|
|
|
|
consume_skb(skb);
|
|
|
|
skb = NULL;
|
|
|
|
} else {
|
|
|
|
if (skb == u->oob_skb) {
|
|
|
|
if (copied) {
|
|
|
|
skb = NULL;
|
|
|
|
} else if (sock_flag(sk, SOCK_URGINLINE)) {
|
|
|
|
if (!(flags & MSG_PEEK)) {
|
af_unix: Fix some data-races around unix_sk(sk)->oob_skb.
Out-of-band data automatically places a "mark" showing wherein the
sequence the out-of-band data would have been. If the out-of-band data
implies cancelling everything sent so far, the "mark" is helpful to flush
them. When the socket's read pointer reaches the "mark", the ioctl() below
sets a non zero value to the arg `atmark`:
The out-of-band data is queued in sk->sk_receive_queue as well as ordinary
data and also saved in unix_sk(sk)->oob_skb. It can be used to test if the
head of the receive queue is the out-of-band data meaning the socket is at
the "mark".
While testing that, unix_ioctl() reads unix_sk(sk)->oob_skb locklessly.
Thus, all accesses to oob_skb need some basic protection to avoid
load/store tearing which KCSAN detects when these are called concurrently:
- ioctl(fd_a, SIOCATMARK, &atmark, sizeof(atmark))
- send(fd_b_connected_to_a, buf, sizeof(buf), MSG_OOB)
BUG: KCSAN: data-race in unix_ioctl / unix_stream_sendmsg
write to 0xffff888003d9cff0 of 8 bytes by task 175 on cpu 1:
unix_stream_sendmsg (net/unix/af_unix.c:2087 net/unix/af_unix.c:2191)
sock_sendmsg (net/socket.c:705 net/socket.c:725)
__sys_sendto (net/socket.c:2040)
__x64_sys_sendto (net/socket.c:2048)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
read to 0xffff888003d9cff0 of 8 bytes by task 176 on cpu 0:
unix_ioctl (net/unix/af_unix.c:3101 (discriminator 1))
sock_do_ioctl (net/socket.c:1128)
sock_ioctl (net/socket.c:1242)
__x64_sys_ioctl (fs/ioctl.c:52 fs/ioctl.c:874 fs/ioctl.c:860 fs/ioctl.c:860)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
value changed: 0xffff888003da0c00 -> 0xffff888003da0d00
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 176 Comm: unix_race_oob_i Not tainted 5.17.0-rc5-59529-g83dc4c2af682 #12
Hardware name: Red Hat KVM, BIOS 1.11.0-2.amzn2 04/01/2014
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-03-17 11:08:08 +08:00
|
|
|
WRITE_ONCE(u->oob_skb, NULL);
|
2021-08-01 15:57:07 +08:00
|
|
|
consume_skb(skb);
|
|
|
|
}
|
|
|
|
} else if (!(flags & MSG_PEEK)) {
|
|
|
|
skb_unlink(skb, &sk->sk_receive_queue);
|
|
|
|
consume_skb(skb);
|
|
|
|
skb = skb_peek(&sk->sk_receive_queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-06-16 00:20:12 +08:00
|
|
|
static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
|
2021-08-17 03:03:20 +08:00
|
|
|
{
|
|
|
|
if (unlikely(sk->sk_state != TCP_ESTABLISHED))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2022-06-16 00:20:12 +08:00
|
|
|
return unix_read_skb(sk, recv_actor);
|
2021-08-17 03:03:20 +08:00
|
|
|
}
|
|
|
|
|
2016-11-18 07:55:26 +08:00
|
|
|
static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
|
|
|
bool freezable)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-01-29 01:04:53 +08:00
|
|
|
struct scm_cookie scm;
|
2015-05-21 23:00:01 +08:00
|
|
|
struct socket *sock = state->socket;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
struct unix_sock *u = unix_sk(sk);
|
|
|
|
int copied = 0;
|
2015-05-21 23:00:01 +08:00
|
|
|
int flags = state->flags;
|
2014-03-26 09:42:27 +08:00
|
|
|
int noblock = flags & MSG_DONTWAIT;
|
2015-05-21 23:00:01 +08:00
|
|
|
bool check_creds = false;
|
2005-04-17 06:20:36 +08:00
|
|
|
int target;
|
|
|
|
int err = 0;
|
|
|
|
long timeo;
|
2012-02-21 15:32:06 +08:00
|
|
|
int skip;
|
2015-05-21 23:00:01 +08:00
|
|
|
size_t size = state->size;
|
|
|
|
unsigned int last_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-02-09 02:47:19 +08:00
|
|
|
if (unlikely(sk->sk_state != TCP_ESTABLISHED)) {
|
|
|
|
err = -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2016-02-09 02:47:19 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-02-09 02:47:19 +08:00
|
|
|
if (unlikely(flags & MSG_OOB)) {
|
|
|
|
err = -EOPNOTSUPP;
|
2021-08-01 15:57:07 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
|
|
|
err = unix_stream_recv_urg(state);
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2016-02-09 02:47:19 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
|
2014-03-26 09:42:27 +08:00
|
|
|
timeo = sock_rcvtimeo(sk, noblock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
memset(&scm, 0, sizeof(scm));
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Lock the socket to prevent queue disordering
|
|
|
|
* while sleeps in memcpy_tomsg
|
|
|
|
*/
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_lock(&u->iolock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
datagram: When peeking datagrams with offset < 0 don't skip empty skbs
Due to commit e6afc8ace6dd5cef5e812f26c72579da8806f5ac ("udp: remove
headers from UDP packets before queueing"), when udp packets are being
peeked the requested extra offset is always 0 as there is no need to skip
the udp header. However, when the offset is 0 and the next skb is
of length 0, it is only returned once. The behaviour can be seen with
the following python script:
from socket import *;
f=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
g=socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, 0);
f.bind(('::', 0));
addr=('::1', f.getsockname()[1]);
g.sendto(b'', addr)
g.sendto(b'b', addr)
print(f.recvfrom(10, MSG_PEEK));
print(f.recvfrom(10, MSG_PEEK));
Where the expected output should be the empty string twice.
Instead, make sk_peek_offset return negative values, and pass those values
to __skb_try_recv_datagram/__skb_try_recv_from_queue. If the passed offset
to __skb_try_recv_from_queue is negative, the checked skb is never skipped.
__skb_try_recv_from_queue will then ensure the offset is reset back to 0
if a peek is requested without an offset, unless no packets are found.
Also simplify the if condition in __skb_try_recv_from_queue. If _off is
greater then 0, and off is greater then or equal to skb->len, then
(_off || skb->len) must always be true assuming skb->len >= 0 is always
true.
Also remove a redundant check around a call to sk_peek_offset in af_unix.c,
as it double checked if MSG_PEEK was set in the flags.
V2:
- Moved the negative fixup into __skb_try_recv_from_queue, and remove now
redundant checks
- Fix peeking in udp{,v6}_recvmsg to report the right value when the
offset is 0
V3:
- Marked new branch in __skb_try_recv_from_queue as unlikely.
Signed-off-by: Matthew Dawson <matthew@mjdsystems.ca>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-08-19 03:04:54 +08:00
|
|
|
skip = max(sk_peek_offset(sk, flags), 0);
|
2015-10-02 05:05:36 +08:00
|
|
|
|
2008-11-17 14:58:44 +08:00
|
|
|
do {
|
2005-04-17 06:20:36 +08:00
|
|
|
int chunk;
|
2015-11-10 23:23:15 +08:00
|
|
|
bool drop_skb;
|
2013-04-29 19:42:14 +08:00
|
|
|
struct sk_buff *skb, *last;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-02-18 20:39:46 +08:00
|
|
|
redo:
|
2007-06-06 04:10:29 +08:00
|
|
|
unix_state_lock(sk);
|
2015-05-26 23:22:19 +08:00
|
|
|
if (sock_flag(sk, SOCK_DEAD)) {
|
|
|
|
err = -ECONNRESET;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2013-04-29 19:42:14 +08:00
|
|
|
last = skb = skb_peek(&sk->sk_receive_queue);
|
2015-05-21 23:00:01 +08:00
|
|
|
last_len = last ? last->len : 0;
|
2021-08-01 15:57:07 +08:00
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
|
|
|
if (skb) {
|
|
|
|
skb = manage_oob(skb, sk, flags, copied);
|
|
|
|
if (!skb) {
|
|
|
|
unix_state_unlock(sk);
|
|
|
|
if (copied)
|
|
|
|
break;
|
|
|
|
goto redo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2012-02-21 15:32:06 +08:00
|
|
|
again:
|
2008-11-17 14:58:44 +08:00
|
|
|
if (skb == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copied >= target)
|
2007-06-06 04:10:29 +08:00
|
|
|
goto unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* POSIX 1003.1g mandates this order.
|
|
|
|
*/
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2008-11-17 14:58:44 +08:00
|
|
|
err = sock_error(sk);
|
|
|
|
if (err)
|
2007-06-06 04:10:29 +08:00
|
|
|
goto unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
2007-06-06 04:10:29 +08:00
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
unix_state_unlock(sk);
|
2016-02-09 02:47:19 +08:00
|
|
|
if (!timeo) {
|
|
|
|
err = -EAGAIN;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2016-02-09 02:47:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_unlock(&u->iolock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
timeo = unix_stream_data_wait(sk, timeo, last,
|
2016-11-18 07:55:26 +08:00
|
|
|
last_len, freezable);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-12-17 04:09:25 +08:00
|
|
|
if (signal_pending(current)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
err = sock_intr_errno(timeo);
|
2016-01-25 05:53:50 +08:00
|
|
|
scm_destroy(&scm);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2011-02-28 12:50:55 +08:00
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_lock(&u->iolock);
|
2016-02-18 20:39:46 +08:00
|
|
|
goto redo;
|
2015-05-21 23:00:01 +08:00
|
|
|
unlock:
|
2007-06-06 04:10:29 +08:00
|
|
|
unix_state_unlock(sk);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-02-21 15:32:06 +08:00
|
|
|
|
2013-08-09 05:37:32 +08:00
|
|
|
while (skip >= unix_skb_len(skb)) {
|
|
|
|
skip -= unix_skb_len(skb);
|
2013-04-29 19:42:14 +08:00
|
|
|
last = skb;
|
2015-05-21 23:00:01 +08:00
|
|
|
last_len = skb->len;
|
2012-02-21 15:32:06 +08:00
|
|
|
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
2013-04-29 19:42:14 +08:00
|
|
|
if (!skb)
|
|
|
|
goto again;
|
2012-02-21 15:32:06 +08:00
|
|
|
}
|
|
|
|
|
2007-06-06 04:10:29 +08:00
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (check_creds) {
|
|
|
|
/* Never glue messages from different writers */
|
2015-11-26 19:08:18 +08:00
|
|
|
if (!unix_skb_scm_eq(skb, &scm))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2023-06-09 04:26:25 +08:00
|
|
|
} else if (test_bit(SOCK_PASSCRED, &sock->flags) ||
|
|
|
|
test_bit(SOCK_PASSPIDFD, &sock->flags)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Copy credentials */
|
2015-01-29 01:04:53 +08:00
|
|
|
scm_set_cred(&scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
|
2015-06-10 20:44:59 +08:00
|
|
|
unix_set_secdata(&scm, skb);
|
2015-05-21 23:00:01 +08:00
|
|
|
check_creds = true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy address just once */
|
2015-05-21 23:00:01 +08:00
|
|
|
if (state->msg && state->msg->msg_name) {
|
|
|
|
DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr,
|
|
|
|
state->msg->msg_name);
|
|
|
|
unix_copy_addr(state->msg, skb->sk);
|
2023-10-12 02:51:06 +08:00
|
|
|
|
|
|
|
BPF_CGROUP_RUN_PROG_UNIX_RECVMSG_LOCK(sk,
|
|
|
|
state->msg->msg_name,
|
|
|
|
&state->msg->msg_namelen);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
sunaddr = NULL;
|
|
|
|
}
|
|
|
|
|
2013-08-09 05:37:32 +08:00
|
|
|
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
|
2015-11-10 23:23:15 +08:00
|
|
|
skb_get(skb);
|
2015-05-21 23:00:01 +08:00
|
|
|
chunk = state->recv_actor(skb, skip, chunk, state);
|
2015-11-10 23:23:15 +08:00
|
|
|
drop_skb = !unix_skb_len(skb);
|
|
|
|
/* skb is only safe to use if !drop_skb */
|
|
|
|
consume_skb(skb);
|
2015-05-21 23:00:01 +08:00
|
|
|
if (chunk < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copied == 0)
|
|
|
|
copied = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
copied += chunk;
|
|
|
|
size -= chunk;
|
|
|
|
|
2015-11-10 23:23:15 +08:00
|
|
|
if (drop_skb) {
|
|
|
|
/* the skb was touched by a concurrent reader;
|
|
|
|
* we should not expect anything from this skb
|
|
|
|
* anymore and assume it invalid - we can be
|
|
|
|
* sure it was dropped from the socket queue
|
|
|
|
*
|
|
|
|
* let's report a short read
|
|
|
|
*/
|
|
|
|
err = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Mark read part of skb as used */
|
2008-11-17 14:58:44 +08:00
|
|
|
if (!(flags & MSG_PEEK)) {
|
2013-08-09 05:37:32 +08:00
|
|
|
UNIXCB(skb).consumed += chunk;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-21 15:32:06 +08:00
|
|
|
sk_peek_offset_bwd(sk, chunk);
|
|
|
|
|
2019-12-09 18:03:46 +08:00
|
|
|
if (UNIXCB(skb).fp) {
|
|
|
|
scm_stat_del(sk, skb);
|
2015-01-29 01:04:53 +08:00
|
|
|
unix_detach_fds(&scm, skb);
|
2019-12-09 18:03:46 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-08-09 05:37:32 +08:00
|
|
|
if (unix_skb_len(skb))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
2012-01-29 00:11:03 +08:00
|
|
|
skb_unlink(skb, &sk->sk_receive_queue);
|
2010-07-20 14:45:56 +08:00
|
|
|
consume_skb(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-01-29 01:04:53 +08:00
|
|
|
if (scm.fp)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-11-17 14:58:44 +08:00
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* It is questionable, see note in unix_dgram_recvmsg.
|
|
|
|
*/
|
|
|
|
if (UNIXCB(skb).fp)
|
2021-07-28 20:47:20 +08:00
|
|
|
unix_peek_fds(&scm, skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-10-02 05:05:36 +08:00
|
|
|
sk_peek_offset_fwd(sk, chunk);
|
2012-02-21 15:32:06 +08:00
|
|
|
|
2015-09-27 06:50:43 +08:00
|
|
|
if (UNIXCB(skb).fp)
|
|
|
|
break;
|
|
|
|
|
2015-10-02 05:05:36 +08:00
|
|
|
skip = 0;
|
2015-09-27 06:50:43 +08:00
|
|
|
last = skb;
|
|
|
|
last_len = skb->len;
|
|
|
|
unix_state_lock(sk);
|
|
|
|
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
|
|
|
if (skb)
|
|
|
|
goto again;
|
|
|
|
unix_state_unlock(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (size);
|
|
|
|
|
2016-09-02 05:43:53 +08:00
|
|
|
mutex_unlock(&u->iolock);
|
2023-06-27 04:58:37 +08:00
|
|
|
if (state->msg)
|
2023-06-28 01:43:14 +08:00
|
|
|
scm_recv_unix(sock, state->msg, &scm, flags);
|
2015-05-21 23:00:01 +08:00
|
|
|
else
|
|
|
|
scm_destroy(&scm);
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
return copied ? : err;
|
|
|
|
}
|
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
static int unix_stream_read_actor(struct sk_buff *skb,
|
|
|
|
int skip, int chunk,
|
|
|
|
struct unix_stream_read_state *state)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = skb_copy_datagram_msg(skb, UNIXCB(skb).consumed + skip,
|
|
|
|
state->msg, chunk);
|
|
|
|
return ret ?: chunk;
|
|
|
|
}
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
int __unix_stream_recvmsg(struct sock *sk, struct msghdr *msg,
|
|
|
|
size_t size, int flags)
|
|
|
|
{
|
|
|
|
struct unix_stream_read_state state = {
|
|
|
|
.recv_actor = unix_stream_read_actor,
|
|
|
|
.socket = sk->sk_socket,
|
|
|
|
.msg = msg,
|
|
|
|
.size = size,
|
|
|
|
.flags = flags
|
|
|
|
};
|
|
|
|
|
|
|
|
return unix_stream_read_generic(&state, true);
|
|
|
|
}
|
|
|
|
|
2015-05-21 23:00:01 +08:00
|
|
|
static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg,
|
|
|
|
size_t size, int flags)
|
|
|
|
{
|
|
|
|
struct unix_stream_read_state state = {
|
|
|
|
.recv_actor = unix_stream_read_actor,
|
|
|
|
.socket = sock,
|
|
|
|
.msg = msg,
|
|
|
|
.size = size,
|
|
|
|
.flags = flags
|
|
|
|
};
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
#ifdef CONFIG_BPF_SYSCALL
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
const struct proto *prot = READ_ONCE(sk->sk_prot);
|
|
|
|
|
|
|
|
if (prot != &unix_stream_proto)
|
net: remove noblock parameter from recvmsg() entities
The internal recvmsg() functions have two parameters 'flags' and 'noblock'
that were merged inside skb_recv_datagram(). As a follow up patch to commit
f4b41f062c42 ("net: remove noblock parameter from skb_recv_datagram()")
this patch removes the separate 'noblock' parameter for recvmsg().
Analogue to the referenced patch for skb_recv_datagram() the 'flags' and
'noblock' parameters are unnecessarily split up with e.g.
err = sk->sk_prot->recvmsg(sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
or in
err = INDIRECT_CALL_2(sk->sk_prot->recvmsg, tcp_recvmsg, udp_recvmsg,
sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
instead of simply using only flags all the time and check for MSG_DONTWAIT
where needed (to preserve for the formerly separated no(n)block condition).
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/r/20220411124955.154876-1-socketcan@hartkopp.net
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-04-11 20:49:55 +08:00
|
|
|
return prot->recvmsg(sk, msg, size, flags, NULL);
|
2021-08-17 03:03:21 +08:00
|
|
|
#endif
|
2016-11-18 07:55:26 +08:00
|
|
|
return unix_stream_read_generic(&state, true);
|
2015-05-21 23:00:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_stream_splice_actor(struct sk_buff *skb,
|
|
|
|
int skip, int chunk,
|
|
|
|
struct unix_stream_read_state *state)
|
|
|
|
{
|
|
|
|
return skb_splice_bits(skb, state->socket->sk,
|
|
|
|
UNIXCB(skb).consumed + skip,
|
2016-09-18 09:02:10 +08:00
|
|
|
state->pipe, chunk, state->splice_flags);
|
2015-05-21 23:00:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t unix_stream_splice_read(struct socket *sock, loff_t *ppos,
|
|
|
|
struct pipe_inode_info *pipe,
|
|
|
|
size_t size, unsigned int flags)
|
|
|
|
{
|
|
|
|
struct unix_stream_read_state state = {
|
|
|
|
.recv_actor = unix_stream_splice_actor,
|
|
|
|
.socket = sock,
|
|
|
|
.pipe = pipe,
|
|
|
|
.size = size,
|
|
|
|
.splice_flags = flags,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (unlikely(*ppos))
|
|
|
|
return -ESPIPE;
|
|
|
|
|
|
|
|
if (sock->file->f_flags & O_NONBLOCK ||
|
|
|
|
flags & SPLICE_F_NONBLOCK)
|
|
|
|
state.flags = MSG_DONTWAIT;
|
|
|
|
|
2016-11-18 07:55:26 +08:00
|
|
|
return unix_stream_read_generic(&state, false);
|
2015-05-21 23:00:01 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_shutdown(struct socket *sock, int mode)
|
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
|
|
|
struct sock *other;
|
|
|
|
|
2012-08-27 00:47:13 +08:00
|
|
|
if (mode < SHUT_RD || mode > SHUT_RDWR)
|
|
|
|
return -EINVAL;
|
|
|
|
/* This maps:
|
|
|
|
* SHUT_RD (0) -> RCV_SHUTDOWN (1)
|
|
|
|
* SHUT_WR (1) -> SEND_SHUTDOWN (2)
|
|
|
|
* SHUT_RDWR (2) -> SHUTDOWN_MASK (3)
|
|
|
|
*/
|
|
|
|
++mode;
|
2011-01-19 12:56:36 +08:00
|
|
|
|
|
|
|
unix_state_lock(sk);
|
2023-05-10 08:34:56 +08:00
|
|
|
WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | mode);
|
2011-01-19 12:56:36 +08:00
|
|
|
other = unix_peer(sk);
|
|
|
|
if (other)
|
|
|
|
sock_hold(other);
|
|
|
|
unix_state_unlock(sk);
|
|
|
|
sk->sk_state_change(sk);
|
|
|
|
|
|
|
|
if (other &&
|
|
|
|
(sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET)) {
|
|
|
|
|
|
|
|
int peer_mode = 0;
|
2021-08-17 03:03:21 +08:00
|
|
|
const struct proto *prot = READ_ONCE(other->sk_prot);
|
2011-01-19 12:56:36 +08:00
|
|
|
|
2021-08-22 02:07:36 +08:00
|
|
|
if (prot->unhash)
|
|
|
|
prot->unhash(other);
|
2011-01-19 12:56:36 +08:00
|
|
|
if (mode&RCV_SHUTDOWN)
|
|
|
|
peer_mode |= SEND_SHUTDOWN;
|
|
|
|
if (mode&SEND_SHUTDOWN)
|
|
|
|
peer_mode |= RCV_SHUTDOWN;
|
|
|
|
unix_state_lock(other);
|
2023-05-10 08:34:56 +08:00
|
|
|
WRITE_ONCE(other->sk_shutdown, other->sk_shutdown | peer_mode);
|
2011-01-19 12:56:36 +08:00
|
|
|
unix_state_unlock(other);
|
|
|
|
other->sk_state_change(other);
|
2021-10-05 07:25:28 +08:00
|
|
|
if (peer_mode == SHUTDOWN_MASK)
|
2011-01-19 12:56:36 +08:00
|
|
|
sk_wake_async(other, SOCK_WAKE_WAITD, POLL_HUP);
|
2021-10-05 07:25:28 +08:00
|
|
|
else if (peer_mode & RCV_SHUTDOWN)
|
2011-01-19 12:56:36 +08:00
|
|
|
sk_wake_async(other, SOCK_WAKE_WAITD, POLL_IN);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-01-19 12:56:36 +08:00
|
|
|
if (other)
|
|
|
|
sock_put(other);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-30 08:54:11 +08:00
|
|
|
long unix_inq_len(struct sock *sk)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
long amount = 0;
|
|
|
|
|
|
|
|
if (sk->sk_state == TCP_LISTEN)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
spin_lock(&sk->sk_receive_queue.lock);
|
|
|
|
if (sk->sk_type == SOCK_STREAM ||
|
|
|
|
sk->sk_type == SOCK_SEQPACKET) {
|
|
|
|
skb_queue_walk(&sk->sk_receive_queue, skb)
|
2013-08-09 05:37:32 +08:00
|
|
|
amount += unix_skb_len(skb);
|
2011-12-30 08:54:11 +08:00
|
|
|
} else {
|
|
|
|
skb = skb_peek(&sk->sk_receive_queue);
|
|
|
|
if (skb)
|
|
|
|
amount = skb->len;
|
|
|
|
}
|
|
|
|
spin_unlock(&sk->sk_receive_queue.lock);
|
|
|
|
|
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(unix_inq_len);
|
|
|
|
|
|
|
|
long unix_outq_len(struct sock *sk)
|
|
|
|
{
|
|
|
|
return sk_wmem_alloc_get(sk);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(unix_outq_len);
|
|
|
|
|
unix: add ioctl to open a unix socket file with O_PATH
This ioctl opens a file to which a socket is bound and
returns a file descriptor. The caller has to have CAP_NET_ADMIN
in the socket network namespace.
Currently it is impossible to get a path and a mount point
for a socket file. socket_diag reports address, device ID and inode
number for unix sockets. An address can contain a relative path or
a file may be moved somewhere. And these properties say nothing about
a mount namespace and a mount point of a socket file.
With the introduced ioctl, we can get a path by reading
/proc/self/fd/X and get mnt_id from /proc/self/fdinfo/X.
In CRIU we are going to use this ioctl to dump and restore unix socket.
Here is an example how it can be used:
$ strace -e socket,bind,ioctl ./test /tmp/test_sock
socket(AF_UNIX, SOCK_STREAM, 0) = 3
bind(3, {sa_family=AF_UNIX, sun_path="test_sock"}, 11) = 0
ioctl(3, SIOCUNIXFILE, 0) = 4
^Z
$ ss -a | grep test_sock
u_str LISTEN 0 1 test_sock 17798 * 0
$ ls -l /proc/760/fd/{3,4}
lrwx------ 1 root root 64 Feb 1 09:41 3 -> 'socket:[17798]'
l--------- 1 root root 64 Feb 1 09:41 4 -> /tmp/test_sock
$ cat /proc/760/fdinfo/4
pos: 0
flags: 012000000
mnt_id: 40
$ cat /proc/self/mountinfo | grep "^40\s"
40 19 0:37 / /tmp rw shared:23 - tmpfs tmpfs rw
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-02 03:00:45 +08:00
|
|
|
static int unix_open_file(struct sock *sk)
|
|
|
|
{
|
|
|
|
struct path path;
|
|
|
|
struct file *f;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2019-02-16 04:09:35 +08:00
|
|
|
if (!smp_load_acquire(&unix_sk(sk)->addr))
|
|
|
|
return -ENOENT;
|
|
|
|
|
unix: add ioctl to open a unix socket file with O_PATH
This ioctl opens a file to which a socket is bound and
returns a file descriptor. The caller has to have CAP_NET_ADMIN
in the socket network namespace.
Currently it is impossible to get a path and a mount point
for a socket file. socket_diag reports address, device ID and inode
number for unix sockets. An address can contain a relative path or
a file may be moved somewhere. And these properties say nothing about
a mount namespace and a mount point of a socket file.
With the introduced ioctl, we can get a path by reading
/proc/self/fd/X and get mnt_id from /proc/self/fdinfo/X.
In CRIU we are going to use this ioctl to dump and restore unix socket.
Here is an example how it can be used:
$ strace -e socket,bind,ioctl ./test /tmp/test_sock
socket(AF_UNIX, SOCK_STREAM, 0) = 3
bind(3, {sa_family=AF_UNIX, sun_path="test_sock"}, 11) = 0
ioctl(3, SIOCUNIXFILE, 0) = 4
^Z
$ ss -a | grep test_sock
u_str LISTEN 0 1 test_sock 17798 * 0
$ ls -l /proc/760/fd/{3,4}
lrwx------ 1 root root 64 Feb 1 09:41 3 -> 'socket:[17798]'
l--------- 1 root root 64 Feb 1 09:41 4 -> /tmp/test_sock
$ cat /proc/760/fdinfo/4
pos: 0
flags: 012000000
mnt_id: 40
$ cat /proc/self/mountinfo | grep "^40\s"
40 19 0:37 / /tmp rw shared:23 - tmpfs tmpfs rw
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-02 03:00:45 +08:00
|
|
|
path = unix_sk(sk)->path;
|
2019-02-16 04:09:35 +08:00
|
|
|
if (!path.dentry)
|
unix: add ioctl to open a unix socket file with O_PATH
This ioctl opens a file to which a socket is bound and
returns a file descriptor. The caller has to have CAP_NET_ADMIN
in the socket network namespace.
Currently it is impossible to get a path and a mount point
for a socket file. socket_diag reports address, device ID and inode
number for unix sockets. An address can contain a relative path or
a file may be moved somewhere. And these properties say nothing about
a mount namespace and a mount point of a socket file.
With the introduced ioctl, we can get a path by reading
/proc/self/fd/X and get mnt_id from /proc/self/fdinfo/X.
In CRIU we are going to use this ioctl to dump and restore unix socket.
Here is an example how it can be used:
$ strace -e socket,bind,ioctl ./test /tmp/test_sock
socket(AF_UNIX, SOCK_STREAM, 0) = 3
bind(3, {sa_family=AF_UNIX, sun_path="test_sock"}, 11) = 0
ioctl(3, SIOCUNIXFILE, 0) = 4
^Z
$ ss -a | grep test_sock
u_str LISTEN 0 1 test_sock 17798 * 0
$ ls -l /proc/760/fd/{3,4}
lrwx------ 1 root root 64 Feb 1 09:41 3 -> 'socket:[17798]'
l--------- 1 root root 64 Feb 1 09:41 4 -> /tmp/test_sock
$ cat /proc/760/fdinfo/4
pos: 0
flags: 012000000
mnt_id: 40
$ cat /proc/self/mountinfo | grep "^40\s"
40 19 0:37 / /tmp rw shared:23 - tmpfs tmpfs rw
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-02 03:00:45 +08:00
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
path_get(&path);
|
|
|
|
|
|
|
|
fd = get_unused_fd_flags(O_CLOEXEC);
|
|
|
|
if (fd < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
f = dentry_open(&path, O_PATH, current_cred());
|
|
|
|
if (IS_ERR(f)) {
|
|
|
|
put_unused_fd(fd);
|
|
|
|
fd = PTR_ERR(f);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd_install(fd, f);
|
|
|
|
out:
|
|
|
|
path_put(&path);
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
2008-11-02 12:38:31 +08:00
|
|
|
long amount = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
2008-11-17 14:58:44 +08:00
|
|
|
switch (cmd) {
|
|
|
|
case SIOCOUTQ:
|
2011-12-30 08:54:11 +08:00
|
|
|
amount = unix_outq_len(sk);
|
2008-11-17 14:58:44 +08:00
|
|
|
err = put_user(amount, (int __user *)arg);
|
|
|
|
break;
|
|
|
|
case SIOCINQ:
|
2011-12-30 08:54:11 +08:00
|
|
|
amount = unix_inq_len(sk);
|
|
|
|
if (amount < 0)
|
|
|
|
err = amount;
|
|
|
|
else
|
2005-04-17 06:20:36 +08:00
|
|
|
err = put_user(amount, (int __user *)arg);
|
2011-12-30 08:54:11 +08:00
|
|
|
break;
|
unix: add ioctl to open a unix socket file with O_PATH
This ioctl opens a file to which a socket is bound and
returns a file descriptor. The caller has to have CAP_NET_ADMIN
in the socket network namespace.
Currently it is impossible to get a path and a mount point
for a socket file. socket_diag reports address, device ID and inode
number for unix sockets. An address can contain a relative path or
a file may be moved somewhere. And these properties say nothing about
a mount namespace and a mount point of a socket file.
With the introduced ioctl, we can get a path by reading
/proc/self/fd/X and get mnt_id from /proc/self/fdinfo/X.
In CRIU we are going to use this ioctl to dump and restore unix socket.
Here is an example how it can be used:
$ strace -e socket,bind,ioctl ./test /tmp/test_sock
socket(AF_UNIX, SOCK_STREAM, 0) = 3
bind(3, {sa_family=AF_UNIX, sun_path="test_sock"}, 11) = 0
ioctl(3, SIOCUNIXFILE, 0) = 4
^Z
$ ss -a | grep test_sock
u_str LISTEN 0 1 test_sock 17798 * 0
$ ls -l /proc/760/fd/{3,4}
lrwx------ 1 root root 64 Feb 1 09:41 3 -> 'socket:[17798]'
l--------- 1 root root 64 Feb 1 09:41 4 -> /tmp/test_sock
$ cat /proc/760/fdinfo/4
pos: 0
flags: 012000000
mnt_id: 40
$ cat /proc/self/mountinfo | grep "^40\s"
40 19 0:37 / /tmp rw shared:23 - tmpfs tmpfs rw
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-02 03:00:45 +08:00
|
|
|
case SIOCUNIXFILE:
|
|
|
|
err = unix_open_file(sk);
|
|
|
|
break;
|
2021-08-01 15:57:07 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
|
|
|
case SIOCATMARK:
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int answ = 0;
|
|
|
|
|
|
|
|
skb = skb_peek(&sk->sk_receive_queue);
|
af_unix: Fix some data-races around unix_sk(sk)->oob_skb.
Out-of-band data automatically places a "mark" showing wherein the
sequence the out-of-band data would have been. If the out-of-band data
implies cancelling everything sent so far, the "mark" is helpful to flush
them. When the socket's read pointer reaches the "mark", the ioctl() below
sets a non zero value to the arg `atmark`:
The out-of-band data is queued in sk->sk_receive_queue as well as ordinary
data and also saved in unix_sk(sk)->oob_skb. It can be used to test if the
head of the receive queue is the out-of-band data meaning the socket is at
the "mark".
While testing that, unix_ioctl() reads unix_sk(sk)->oob_skb locklessly.
Thus, all accesses to oob_skb need some basic protection to avoid
load/store tearing which KCSAN detects when these are called concurrently:
- ioctl(fd_a, SIOCATMARK, &atmark, sizeof(atmark))
- send(fd_b_connected_to_a, buf, sizeof(buf), MSG_OOB)
BUG: KCSAN: data-race in unix_ioctl / unix_stream_sendmsg
write to 0xffff888003d9cff0 of 8 bytes by task 175 on cpu 1:
unix_stream_sendmsg (net/unix/af_unix.c:2087 net/unix/af_unix.c:2191)
sock_sendmsg (net/socket.c:705 net/socket.c:725)
__sys_sendto (net/socket.c:2040)
__x64_sys_sendto (net/socket.c:2048)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
read to 0xffff888003d9cff0 of 8 bytes by task 176 on cpu 0:
unix_ioctl (net/unix/af_unix.c:3101 (discriminator 1))
sock_do_ioctl (net/socket.c:1128)
sock_ioctl (net/socket.c:1242)
__x64_sys_ioctl (fs/ioctl.c:52 fs/ioctl.c:874 fs/ioctl.c:860 fs/ioctl.c:860)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:113)
value changed: 0xffff888003da0c00 -> 0xffff888003da0d00
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 176 Comm: unix_race_oob_i Not tainted 5.17.0-rc5-59529-g83dc4c2af682 #12
Hardware name: Red Hat KVM, BIOS 1.11.0-2.amzn2 04/01/2014
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
2022-03-17 11:08:08 +08:00
|
|
|
if (skb && skb == READ_ONCE(unix_sk(sk)->oob_skb))
|
2021-08-01 15:57:07 +08:00
|
|
|
answ = 1;
|
|
|
|
err = put_user(answ, (int __user *)arg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
2008-11-17 14:58:44 +08:00
|
|
|
default:
|
|
|
|
err = -ENOIOCTLCMD;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-06-04 04:03:44 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
return unix_ioctl(sock, cmd, (unsigned long)compat_ptr(arg));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-06-29 00:43:44 +08:00
|
|
|
static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sock *sk = sock->sk;
|
2018-06-29 00:43:44 +08:00
|
|
|
__poll_t mask;
|
2023-05-10 08:34:56 +08:00
|
|
|
u8 shutdown;
|
2018-06-29 00:43:44 +08:00
|
|
|
|
2018-10-23 19:40:39 +08:00
|
|
|
sock_poll_wait(file, sock, wait);
|
2018-06-29 00:43:44 +08:00
|
|
|
mask = 0;
|
2023-05-10 08:34:56 +08:00
|
|
|
shutdown = READ_ONCE(sk->sk_shutdown);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* exceptional events? */
|
2023-03-16 04:57:46 +08:00
|
|
|
if (READ_ONCE(sk->sk_err))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLERR;
|
2023-05-10 08:34:56 +08:00
|
|
|
if (shutdown == SHUTDOWN_MASK)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLHUP;
|
2023-05-10 08:34:56 +08:00
|
|
|
if (shutdown & RCV_SHUTDOWN)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* readable? */
|
2019-10-24 13:44:50 +08:00
|
|
|
if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2021-10-09 04:33:05 +08:00
|
|
|
if (sk_is_readable(sk))
|
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2022-03-17 11:08:09 +08:00
|
|
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
|
|
|
if (READ_ONCE(unix_sk(sk)->oob_skb))
|
|
|
|
mask |= EPOLLPRI;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Connection-based need to check for termination and startup */
|
2008-11-17 14:58:44 +08:00
|
|
|
if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) &&
|
|
|
|
sk->sk_state == TCP_CLOSE)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLHUP;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we set writable also when the other side has shut down the
|
|
|
|
* connection. This prevents stuck sockets.
|
|
|
|
*/
|
|
|
|
if (unix_writable(sk))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2018-06-29 00:43:44 +08:00
|
|
|
static __poll_t unix_dgram_poll(struct file *file, struct socket *sock,
|
|
|
|
poll_table *wait)
|
2008-06-18 13:28:05 +08:00
|
|
|
{
|
af_unix: fix 'poll for write'/connected DGRAM sockets
For n:1 'datagram connections' (eg /dev/log), the unix_dgram_sendmsg
routine implements a form of receiver-imposed flow control by
comparing the length of the receive queue of the 'peer socket' with
the max_ack_backlog value stored in the corresponding sock structure,
either blocking the thread which caused the send-routine to be called
or returning EAGAIN. This routine is used by both SOCK_DGRAM and
SOCK_SEQPACKET sockets. The poll-implementation for these socket types
is datagram_poll from core/datagram.c. A socket is deemed to be
writeable by this routine when the memory presently consumed by
datagrams owned by it is less than the configured socket send buffer
size. This is always wrong for PF_UNIX non-stream sockets connected to
server sockets dealing with (potentially) multiple clients if the
abovementioned receive queue is currently considered to be full.
'poll' will then return, indicating that the socket is writeable, but
a subsequent write result in EAGAIN, effectively causing an (usual)
application to 'poll for writeability by repeated send request with
O_NONBLOCK set' until it has consumed its time quantum.
The change below uses a suitably modified variant of the datagram_poll
routines for both type of PF_UNIX sockets, which tests if the
recv-queue of the peer a socket is connected to is presently
considered to be 'full' as part of the 'is this socket
writeable'-checking code. The socket being polled is additionally
put onto the peer_wait wait queue associated with its peer, because the
unix_dgram_recvmsg routine does a wake up on this queue after a
datagram was received and the 'other wakeup call' is done implicitly
as part of skb destruction, meaning, a process blocked in poll
because of a full peer receive queue could otherwise sleep forever
if no datagram owned by its socket was already sitting on this queue.
Among this change is a small (inline) helper routine named
'unix_recvq_full', which consolidates the actual testing code (in three
different places) into a single location.
Signed-off-by: Rainer Weikusat <rweikusat@mssgmbh.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-06-28 10:34:18 +08:00
|
|
|
struct sock *sk = sock->sk, *other;
|
2018-06-29 00:43:44 +08:00
|
|
|
unsigned int writable;
|
|
|
|
__poll_t mask;
|
2023-05-10 08:34:56 +08:00
|
|
|
u8 shutdown;
|
2018-06-29 00:43:44 +08:00
|
|
|
|
2018-10-23 19:40:39 +08:00
|
|
|
sock_poll_wait(file, sock, wait);
|
2018-06-29 00:43:44 +08:00
|
|
|
mask = 0;
|
2023-05-10 08:34:56 +08:00
|
|
|
shutdown = READ_ONCE(sk->sk_shutdown);
|
2008-06-18 13:28:05 +08:00
|
|
|
|
|
|
|
/* exceptional events? */
|
2023-03-16 04:57:46 +08:00
|
|
|
if (READ_ONCE(sk->sk_err) ||
|
|
|
|
!skb_queue_empty_lockless(&sk->sk_error_queue))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLERR |
|
|
|
|
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
|
2013-03-28 19:19:25 +08:00
|
|
|
|
2023-05-10 08:34:56 +08:00
|
|
|
if (shutdown & RCV_SHUTDOWN)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
|
2023-05-10 08:34:56 +08:00
|
|
|
if (shutdown == SHUTDOWN_MASK)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLHUP;
|
2008-06-18 13:28:05 +08:00
|
|
|
|
|
|
|
/* readable? */
|
2019-10-24 13:44:50 +08:00
|
|
|
if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2021-10-09 04:33:05 +08:00
|
|
|
if (sk_is_readable(sk))
|
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2008-06-18 13:28:05 +08:00
|
|
|
|
|
|
|
/* Connection-based need to check for termination and startup */
|
|
|
|
if (sk->sk_type == SOCK_SEQPACKET) {
|
|
|
|
if (sk->sk_state == TCP_CLOSE)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLHUP;
|
2008-06-18 13:28:05 +08:00
|
|
|
/* connection hasn't started yet? */
|
|
|
|
if (sk->sk_state == TCP_SYN_SENT)
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2010-10-31 13:38:25 +08:00
|
|
|
/* No write status requested, avoid expensive OUT tests. */
|
2018-06-29 00:43:44 +08:00
|
|
|
if (!(poll_requested_events(wait) & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT)))
|
2010-10-31 13:38:25 +08:00
|
|
|
return mask;
|
|
|
|
|
af_unix: fix 'poll for write'/connected DGRAM sockets
For n:1 'datagram connections' (eg /dev/log), the unix_dgram_sendmsg
routine implements a form of receiver-imposed flow control by
comparing the length of the receive queue of the 'peer socket' with
the max_ack_backlog value stored in the corresponding sock structure,
either blocking the thread which caused the send-routine to be called
or returning EAGAIN. This routine is used by both SOCK_DGRAM and
SOCK_SEQPACKET sockets. The poll-implementation for these socket types
is datagram_poll from core/datagram.c. A socket is deemed to be
writeable by this routine when the memory presently consumed by
datagrams owned by it is less than the configured socket send buffer
size. This is always wrong for PF_UNIX non-stream sockets connected to
server sockets dealing with (potentially) multiple clients if the
abovementioned receive queue is currently considered to be full.
'poll' will then return, indicating that the socket is writeable, but
a subsequent write result in EAGAIN, effectively causing an (usual)
application to 'poll for writeability by repeated send request with
O_NONBLOCK set' until it has consumed its time quantum.
The change below uses a suitably modified variant of the datagram_poll
routines for both type of PF_UNIX sockets, which tests if the
recv-queue of the peer a socket is connected to is presently
considered to be 'full' as part of the 'is this socket
writeable'-checking code. The socket being polled is additionally
put onto the peer_wait wait queue associated with its peer, because the
unix_dgram_recvmsg routine does a wake up on this queue after a
datagram was received and the 'other wakeup call' is done implicitly
as part of skb destruction, meaning, a process blocked in poll
because of a full peer receive queue could otherwise sleep forever
if no datagram owned by its socket was already sitting on this queue.
Among this change is a small (inline) helper routine named
'unix_recvq_full', which consolidates the actual testing code (in three
different places) into a single location.
Signed-off-by: Rainer Weikusat <rweikusat@mssgmbh.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-06-28 10:34:18 +08:00
|
|
|
writable = unix_writable(sk);
|
2015-11-21 06:07:23 +08:00
|
|
|
if (writable) {
|
|
|
|
unix_state_lock(sk);
|
|
|
|
|
|
|
|
other = unix_peer(sk);
|
|
|
|
if (other && unix_peer(other) != sk &&
|
2021-09-09 08:00:29 +08:00
|
|
|
unix_recvq_full_lockless(other) &&
|
2015-11-21 06:07:23 +08:00
|
|
|
unix_dgram_peer_wake_me(sk, other))
|
|
|
|
writable = 0;
|
|
|
|
|
|
|
|
unix_state_unlock(sk);
|
af_unix: fix 'poll for write'/connected DGRAM sockets
For n:1 'datagram connections' (eg /dev/log), the unix_dgram_sendmsg
routine implements a form of receiver-imposed flow control by
comparing the length of the receive queue of the 'peer socket' with
the max_ack_backlog value stored in the corresponding sock structure,
either blocking the thread which caused the send-routine to be called
or returning EAGAIN. This routine is used by both SOCK_DGRAM and
SOCK_SEQPACKET sockets. The poll-implementation for these socket types
is datagram_poll from core/datagram.c. A socket is deemed to be
writeable by this routine when the memory presently consumed by
datagrams owned by it is less than the configured socket send buffer
size. This is always wrong for PF_UNIX non-stream sockets connected to
server sockets dealing with (potentially) multiple clients if the
abovementioned receive queue is currently considered to be full.
'poll' will then return, indicating that the socket is writeable, but
a subsequent write result in EAGAIN, effectively causing an (usual)
application to 'poll for writeability by repeated send request with
O_NONBLOCK set' until it has consumed its time quantum.
The change below uses a suitably modified variant of the datagram_poll
routines for both type of PF_UNIX sockets, which tests if the
recv-queue of the peer a socket is connected to is presently
considered to be 'full' as part of the 'is this socket
writeable'-checking code. The socket being polled is additionally
put onto the peer_wait wait queue associated with its peer, because the
unix_dgram_recvmsg routine does a wake up on this queue after a
datagram was received and the 'other wakeup call' is done implicitly
as part of skb destruction, meaning, a process blocked in poll
because of a full peer receive queue could otherwise sleep forever
if no datagram owned by its socket was already sitting on this queue.
Among this change is a small (inline) helper routine named
'unix_recvq_full', which consolidates the actual testing code (in three
different places) into a single location.
Signed-off-by: Rainer Weikusat <rweikusat@mssgmbh.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-06-28 10:34:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (writable)
|
2018-02-12 06:34:03 +08:00
|
|
|
mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
|
2008-06-18 13:28:05 +08:00
|
|
|
else
|
2015-11-30 12:03:10 +08:00
|
|
|
sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
|
2008-06-18 13:28:05 +08:00
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2007-11-23 20:30:01 +08:00
|
|
|
|
2012-06-08 13:03:21 +08:00
|
|
|
#define BUCKET_SPACE (BITS_PER_LONG - (UNIX_HASH_BITS + 1) - 1)
|
|
|
|
|
|
|
|
#define get_bucket(x) ((x) >> BUCKET_SPACE)
|
2021-11-24 10:14:30 +08:00
|
|
|
#define get_offset(x) ((x) & ((1UL << BUCKET_SPACE) - 1))
|
2012-06-08 13:03:21 +08:00
|
|
|
#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
|
2007-11-23 20:30:01 +08:00
|
|
|
|
2012-06-08 13:03:21 +08:00
|
|
|
static struct sock *unix_from_bucket(struct seq_file *seq, loff_t *pos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-06-08 13:03:21 +08:00
|
|
|
unsigned long offset = get_offset(*pos);
|
|
|
|
unsigned long bucket = get_bucket(*pos);
|
|
|
|
unsigned long count = 0;
|
2022-06-22 01:19:12 +08:00
|
|
|
struct sock *sk;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
for (sk = sk_head(&seq_file_net(seq)->unx.table.buckets[bucket]);
|
|
|
|
sk; sk = sk_next(sk)) {
|
2012-06-08 13:03:21 +08:00
|
|
|
if (++count == offset)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
2022-01-13 08:28:45 +08:00
|
|
|
static struct sock *unix_get_first(struct seq_file *seq, loff_t *pos)
|
2012-06-08 13:03:21 +08:00
|
|
|
{
|
2021-11-24 10:14:30 +08:00
|
|
|
unsigned long bucket = get_bucket(*pos);
|
2022-06-22 01:19:11 +08:00
|
|
|
struct net *net = seq_file_net(seq);
|
2022-01-13 08:28:45 +08:00
|
|
|
struct sock *sk;
|
2012-06-08 13:03:21 +08:00
|
|
|
|
2022-06-22 01:19:09 +08:00
|
|
|
while (bucket < UNIX_HASH_SIZE) {
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_lock(&net->unx.table.locks[bucket]);
|
2022-01-13 08:28:45 +08:00
|
|
|
|
2012-06-08 13:03:21 +08:00
|
|
|
sk = unix_from_bucket(seq, pos);
|
|
|
|
if (sk)
|
|
|
|
return sk;
|
|
|
|
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_unlock(&net->unx.table.locks[bucket]);
|
2022-01-13 08:28:45 +08:00
|
|
|
|
|
|
|
*pos = set_bucket_offset(++bucket, 1);
|
|
|
|
}
|
2012-06-08 13:03:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-01-13 08:28:45 +08:00
|
|
|
static struct sock *unix_get_next(struct seq_file *seq, struct sock *sk,
|
|
|
|
loff_t *pos)
|
|
|
|
{
|
|
|
|
unsigned long bucket = get_bucket(*pos);
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
sk = sk_next(sk);
|
|
|
|
if (sk)
|
|
|
|
return sk;
|
|
|
|
|
2022-01-13 08:28:45 +08:00
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
spin_unlock(&seq_file_net(seq)->unx.table.locks[bucket]);
|
2022-01-13 08:28:45 +08:00
|
|
|
|
|
|
|
*pos = set_bucket_offset(++bucket, 1);
|
|
|
|
|
|
|
|
return unix_get_first(seq, pos);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void *unix_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
{
|
2012-06-08 13:03:21 +08:00
|
|
|
if (!*pos)
|
|
|
|
return SEQ_START_TOKEN;
|
|
|
|
|
2022-01-13 08:28:45 +08:00
|
|
|
return unix_get_first(seq, pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *unix_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
{
|
|
|
|
++*pos;
|
2022-01-13 08:28:45 +08:00
|
|
|
|
|
|
|
if (v == SEQ_START_TOKEN)
|
|
|
|
return unix_get_first(seq, pos);
|
|
|
|
|
|
|
|
return unix_get_next(seq, v, pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unix_seq_stop(struct seq_file *seq, void *v)
|
|
|
|
{
|
2021-11-24 10:14:30 +08:00
|
|
|
struct sock *sk = v;
|
|
|
|
|
2022-06-22 01:19:13 +08:00
|
|
|
if (sk)
|
2022-06-22 01:19:11 +08:00
|
|
|
spin_unlock(&seq_file_net(seq)->unx.table.locks[sk->sk_hash]);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int unix_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
2007-02-09 22:25:23 +08:00
|
|
|
|
2008-04-13 10:04:38 +08:00
|
|
|
if (v == SEQ_START_TOKEN)
|
2005-04-17 06:20:36 +08:00
|
|
|
seq_puts(seq, "Num RefCount Protocol Flags Type St "
|
|
|
|
"Inode Path\n");
|
|
|
|
else {
|
|
|
|
struct sock *s = v;
|
|
|
|
struct unix_sock *u = unix_sk(s);
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_lock(s);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
net: convert %p usage to %pK
The %pK format specifier is designed to hide exposed kernel pointers,
specifically via /proc interfaces. Exposing these pointers provides an
easy target for kernel write vulnerabilities, since they reveal the
locations of writable structures containing easily triggerable function
pointers. The behavior of %pK depends on the kptr_restrict sysctl.
If kptr_restrict is set to 0, no deviation from the standard %p behavior
occurs. If kptr_restrict is set to 1, the default, if the current user
(intended to be a reader via seq_printf(), etc.) does not have CAP_SYSLOG
(currently in the LSM tree), kernel pointers using %pK are printed as 0's.
If kptr_restrict is set to 2, kernel pointers using %pK are printed as
0's regardless of privileges. Replacing with 0's was chosen over the
default "(null)", which cannot be parsed by userland %p, which expects
"(nil)".
The supporting code for kptr_restrict and %pK are currently in the -mm
tree. This patch converts users of %p in net/ to %pK. Cases of printing
pointers to the syslog are not covered, since this would eliminate useful
information for postmortem debugging and the reading of the syslog is
already optionally protected by the dmesg_restrict sysctl.
Signed-off-by: Dan Rosenberg <drosenberg@vsecurity.com>
Cc: James Morris <jmorris@namei.org>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Thomas Graf <tgraf@infradead.org>
Cc: Eugene Teo <eugeneteo@kernel.org>
Cc: Kees Cook <kees.cook@canonical.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: David S. Miller <davem@davemloft.net>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Eric Paris <eparis@parisplace.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-05-23 20:17:35 +08:00
|
|
|
seq_printf(seq, "%pK: %08X %08X %08X %04X %02X %5lu",
|
2005-04-17 06:20:36 +08:00
|
|
|
s,
|
2017-06-30 18:08:01 +08:00
|
|
|
refcount_read(&s->sk_refcnt),
|
2005-04-17 06:20:36 +08:00
|
|
|
0,
|
|
|
|
s->sk_state == TCP_LISTEN ? __SO_ACCEPTCON : 0,
|
|
|
|
s->sk_type,
|
|
|
|
s->sk_socket ?
|
|
|
|
(s->sk_state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
|
|
|
|
(s->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
|
|
|
|
sock_i_ino(s));
|
|
|
|
|
2022-06-22 01:19:13 +08:00
|
|
|
if (u->addr) { // under a hash table lock here
|
2005-04-17 06:20:36 +08:00
|
|
|
int i, len;
|
|
|
|
seq_putc(seq, ' ');
|
|
|
|
|
|
|
|
i = 0;
|
2021-11-24 10:14:19 +08:00
|
|
|
len = u->addr->len -
|
|
|
|
offsetof(struct sockaddr_un, sun_path);
|
2021-11-24 10:14:27 +08:00
|
|
|
if (u->addr->name->sun_path[0]) {
|
2005-04-17 06:20:36 +08:00
|
|
|
len--;
|
2021-11-24 10:14:27 +08:00
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
seq_putc(seq, '@');
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
for ( ; i < len; i++)
|
2016-11-01 08:41:35 +08:00
|
|
|
seq_putc(seq, u->addr->name->sun_path[i] ?:
|
|
|
|
'@');
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-06-01 04:24:26 +08:00
|
|
|
unix_state_unlock(s);
|
2005-04-17 06:20:36 +08:00
|
|
|
seq_putc(seq, '\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-11 14:07:31 +08:00
|
|
|
static const struct seq_operations unix_seq_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.start = unix_seq_start,
|
|
|
|
.next = unix_seq_next,
|
|
|
|
.stop = unix_seq_stop,
|
|
|
|
.show = unix_seq_show,
|
|
|
|
};
|
2021-08-14 09:57:15 +08:00
|
|
|
|
2023-10-27 05:23:05 +08:00
|
|
|
#ifdef CONFIG_BPF_SYSCALL
|
2022-01-13 08:28:46 +08:00
|
|
|
struct bpf_unix_iter_state {
|
|
|
|
struct seq_net_private p;
|
|
|
|
unsigned int cur_sk;
|
|
|
|
unsigned int end_sk;
|
|
|
|
unsigned int max_sk;
|
|
|
|
struct sock **batch;
|
|
|
|
bool st_bucket_done;
|
|
|
|
};
|
|
|
|
|
2021-08-14 09:57:15 +08:00
|
|
|
struct bpf_iter__unix {
|
|
|
|
__bpf_md_ptr(struct bpf_iter_meta *, meta);
|
|
|
|
__bpf_md_ptr(struct unix_sock *, unix_sk);
|
|
|
|
uid_t uid __aligned(8);
|
|
|
|
};
|
|
|
|
|
|
|
|
static int unix_prog_seq_show(struct bpf_prog *prog, struct bpf_iter_meta *meta,
|
|
|
|
struct unix_sock *unix_sk, uid_t uid)
|
|
|
|
{
|
|
|
|
struct bpf_iter__unix ctx;
|
|
|
|
|
|
|
|
meta->seq_num--; /* skip SEQ_START_TOKEN */
|
|
|
|
ctx.meta = meta;
|
|
|
|
ctx.unix_sk = unix_sk;
|
|
|
|
ctx.uid = uid;
|
|
|
|
return bpf_iter_run_prog(prog, &ctx);
|
|
|
|
}
|
|
|
|
|
2022-01-13 08:28:46 +08:00
|
|
|
static int bpf_iter_unix_hold_batch(struct seq_file *seq, struct sock *start_sk)
|
|
|
|
|
|
|
|
{
|
|
|
|
struct bpf_unix_iter_state *iter = seq->private;
|
|
|
|
unsigned int expected = 1;
|
|
|
|
struct sock *sk;
|
|
|
|
|
|
|
|
sock_hold(start_sk);
|
|
|
|
iter->batch[iter->end_sk++] = start_sk;
|
|
|
|
|
|
|
|
for (sk = sk_next(start_sk); sk; sk = sk_next(sk)) {
|
|
|
|
if (iter->end_sk < iter->max_sk) {
|
|
|
|
sock_hold(sk);
|
|
|
|
iter->batch[iter->end_sk++] = sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
expected++;
|
|
|
|
}
|
|
|
|
|
2022-06-22 01:19:12 +08:00
|
|
|
spin_unlock(&seq_file_net(seq)->unx.table.locks[start_sk->sk_hash]);
|
2022-01-13 08:28:46 +08:00
|
|
|
|
|
|
|
return expected;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bpf_iter_unix_put_batch(struct bpf_unix_iter_state *iter)
|
|
|
|
{
|
|
|
|
while (iter->cur_sk < iter->end_sk)
|
|
|
|
sock_put(iter->batch[iter->cur_sk++]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bpf_iter_unix_realloc_batch(struct bpf_unix_iter_state *iter,
|
|
|
|
unsigned int new_batch_sz)
|
|
|
|
{
|
|
|
|
struct sock **new_batch;
|
|
|
|
|
|
|
|
new_batch = kvmalloc(sizeof(*new_batch) * new_batch_sz,
|
|
|
|
GFP_USER | __GFP_NOWARN);
|
|
|
|
if (!new_batch)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
bpf_iter_unix_put_batch(iter);
|
|
|
|
kvfree(iter->batch);
|
|
|
|
iter->batch = new_batch;
|
|
|
|
iter->max_sk = new_batch_sz;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sock *bpf_iter_unix_batch(struct seq_file *seq,
|
|
|
|
loff_t *pos)
|
|
|
|
{
|
|
|
|
struct bpf_unix_iter_state *iter = seq->private;
|
|
|
|
unsigned int expected;
|
|
|
|
bool resized = false;
|
|
|
|
struct sock *sk;
|
|
|
|
|
|
|
|
if (iter->st_bucket_done)
|
|
|
|
*pos = set_bucket_offset(get_bucket(*pos) + 1, 1);
|
|
|
|
|
|
|
|
again:
|
|
|
|
/* Get a new batch */
|
|
|
|
iter->cur_sk = 0;
|
|
|
|
iter->end_sk = 0;
|
|
|
|
|
|
|
|
sk = unix_get_first(seq, pos);
|
|
|
|
if (!sk)
|
|
|
|
return NULL; /* Done */
|
|
|
|
|
|
|
|
expected = bpf_iter_unix_hold_batch(seq, sk);
|
|
|
|
|
|
|
|
if (iter->end_sk == expected) {
|
|
|
|
iter->st_bucket_done = true;
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!resized && !bpf_iter_unix_realloc_batch(iter, expected * 3 / 2)) {
|
|
|
|
resized = true;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *bpf_iter_unix_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
{
|
|
|
|
if (!*pos)
|
|
|
|
return SEQ_START_TOKEN;
|
|
|
|
|
|
|
|
/* bpf iter does not support lseek, so it always
|
|
|
|
* continue from where it was stop()-ped.
|
|
|
|
*/
|
|
|
|
return bpf_iter_unix_batch(seq, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *bpf_iter_unix_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
{
|
|
|
|
struct bpf_unix_iter_state *iter = seq->private;
|
|
|
|
struct sock *sk;
|
|
|
|
|
|
|
|
/* Whenever seq_next() is called, the iter->cur_sk is
|
|
|
|
* done with seq_show(), so advance to the next sk in
|
|
|
|
* the batch.
|
|
|
|
*/
|
|
|
|
if (iter->cur_sk < iter->end_sk)
|
|
|
|
sock_put(iter->batch[iter->cur_sk++]);
|
|
|
|
|
|
|
|
++*pos;
|
|
|
|
|
|
|
|
if (iter->cur_sk < iter->end_sk)
|
|
|
|
sk = iter->batch[iter->cur_sk];
|
|
|
|
else
|
|
|
|
sk = bpf_iter_unix_batch(seq, pos);
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
2021-08-14 09:57:15 +08:00
|
|
|
static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
|
|
|
struct bpf_iter_meta meta;
|
|
|
|
struct bpf_prog *prog;
|
|
|
|
struct sock *sk = v;
|
|
|
|
uid_t uid;
|
2022-01-13 08:28:46 +08:00
|
|
|
bool slow;
|
|
|
|
int ret;
|
2021-08-14 09:57:15 +08:00
|
|
|
|
|
|
|
if (v == SEQ_START_TOKEN)
|
|
|
|
return 0;
|
|
|
|
|
2022-01-13 08:28:46 +08:00
|
|
|
slow = lock_sock_fast(sk);
|
|
|
|
|
|
|
|
if (unlikely(sk_unhashed(sk))) {
|
|
|
|
ret = SEQ_SKIP;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2021-08-14 09:57:15 +08:00
|
|
|
uid = from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk));
|
|
|
|
meta.seq = seq;
|
|
|
|
prog = bpf_iter_get_info(&meta, false);
|
2022-01-13 08:28:46 +08:00
|
|
|
ret = unix_prog_seq_show(prog, &meta, v, uid);
|
|
|
|
unlock:
|
|
|
|
unlock_sock_fast(sk, slow);
|
|
|
|
return ret;
|
2021-08-14 09:57:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bpf_iter_unix_seq_stop(struct seq_file *seq, void *v)
|
|
|
|
{
|
2022-01-13 08:28:46 +08:00
|
|
|
struct bpf_unix_iter_state *iter = seq->private;
|
2021-08-14 09:57:15 +08:00
|
|
|
struct bpf_iter_meta meta;
|
|
|
|
struct bpf_prog *prog;
|
|
|
|
|
|
|
|
if (!v) {
|
|
|
|
meta.seq = seq;
|
|
|
|
prog = bpf_iter_get_info(&meta, true);
|
|
|
|
if (prog)
|
|
|
|
(void)unix_prog_seq_show(prog, &meta, v, 0);
|
|
|
|
}
|
|
|
|
|
2022-01-13 08:28:46 +08:00
|
|
|
if (iter->cur_sk < iter->end_sk)
|
|
|
|
bpf_iter_unix_put_batch(iter);
|
2021-08-14 09:57:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct seq_operations bpf_iter_unix_seq_ops = {
|
2022-01-13 08:28:46 +08:00
|
|
|
.start = bpf_iter_unix_seq_start,
|
|
|
|
.next = bpf_iter_unix_seq_next,
|
2021-08-14 09:57:15 +08:00
|
|
|
.stop = bpf_iter_unix_seq_stop,
|
|
|
|
.show = bpf_iter_unix_seq_show,
|
|
|
|
};
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
2009-10-05 13:58:39 +08:00
|
|
|
static const struct net_proto_family unix_family_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = PF_UNIX,
|
|
|
|
.create = unix_create,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
};
|
|
|
|
|
2007-11-20 14:29:30 +08:00
|
|
|
|
2010-01-17 11:35:32 +08:00
|
|
|
static int __net_init unix_net_init(struct net *net)
|
2007-11-20 14:29:30 +08:00
|
|
|
{
|
2022-06-22 01:19:10 +08:00
|
|
|
int i;
|
2007-11-20 14:29:30 +08:00
|
|
|
|
2007-12-11 20:19:17 +08:00
|
|
|
net->unx.sysctl_max_dgram_qlen = 10;
|
2007-12-01 20:51:01 +08:00
|
|
|
if (unix_sysctl_register(net))
|
|
|
|
goto out;
|
2007-12-01 20:44:15 +08:00
|
|
|
|
2007-11-20 14:29:30 +08:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2018-04-11 01:42:55 +08:00
|
|
|
if (!proc_create_net("unix", 0, net->proc_net, &unix_seq_ops,
|
2022-06-22 01:19:10 +08:00
|
|
|
sizeof(struct seq_net_private)))
|
|
|
|
goto err_sysctl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
net->unx.table.locks = kvmalloc_array(UNIX_HASH_SIZE,
|
|
|
|
sizeof(spinlock_t), GFP_KERNEL);
|
|
|
|
if (!net->unx.table.locks)
|
|
|
|
goto err_proc;
|
|
|
|
|
|
|
|
net->unx.table.buckets = kvmalloc_array(UNIX_HASH_SIZE,
|
|
|
|
sizeof(struct hlist_head),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!net->unx.table.buckets)
|
|
|
|
goto free_locks;
|
|
|
|
|
|
|
|
for (i = 0; i < UNIX_HASH_SIZE; i++) {
|
|
|
|
spin_lock_init(&net->unx.table.locks[i]);
|
|
|
|
INIT_HLIST_HEAD(&net->unx.table.buckets[i]);
|
2007-12-01 20:51:01 +08:00
|
|
|
}
|
2022-06-22 01:19:10 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_locks:
|
|
|
|
kvfree(net->unx.table.locks);
|
|
|
|
err_proc:
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
remove_proc_entry("unix", net->proc_net);
|
|
|
|
err_sysctl:
|
2007-11-20 14:29:30 +08:00
|
|
|
#endif
|
2022-06-22 01:19:10 +08:00
|
|
|
unix_sysctl_unregister(net);
|
2007-11-20 14:29:30 +08:00
|
|
|
out:
|
2022-06-22 01:19:10 +08:00
|
|
|
return -ENOMEM;
|
2007-11-20 14:29:30 +08:00
|
|
|
}
|
|
|
|
|
2010-01-17 11:35:32 +08:00
|
|
|
static void __net_exit unix_net_exit(struct net *net)
|
2007-11-20 14:29:30 +08:00
|
|
|
{
|
2022-06-22 01:19:10 +08:00
|
|
|
kvfree(net->unx.table.buckets);
|
|
|
|
kvfree(net->unx.table.locks);
|
2007-12-01 20:51:01 +08:00
|
|
|
unix_sysctl_unregister(net);
|
2013-02-18 09:34:56 +08:00
|
|
|
remove_proc_entry("unix", net->proc_net);
|
2007-11-20 14:29:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations unix_net_ops = {
|
|
|
|
.init = unix_net_init,
|
|
|
|
.exit = unix_net_exit,
|
|
|
|
};
|
|
|
|
|
2023-10-27 05:23:05 +08:00
|
|
|
#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
|
2021-08-14 09:57:15 +08:00
|
|
|
DEFINE_BPF_ITER_FUNC(unix, struct bpf_iter_meta *meta,
|
|
|
|
struct unix_sock *unix_sk, uid_t uid)
|
|
|
|
|
2022-01-13 08:28:46 +08:00
|
|
|
#define INIT_BATCH_SZ 16
|
|
|
|
|
|
|
|
static int bpf_iter_init_unix(void *priv_data, struct bpf_iter_aux_info *aux)
|
|
|
|
{
|
|
|
|
struct bpf_unix_iter_state *iter = priv_data;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = bpf_iter_init_seq_net(priv_data, aux);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = bpf_iter_unix_realloc_batch(iter, INIT_BATCH_SZ);
|
|
|
|
if (err) {
|
|
|
|
bpf_iter_fini_seq_net(priv_data);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bpf_iter_fini_unix(void *priv_data)
|
|
|
|
{
|
|
|
|
struct bpf_unix_iter_state *iter = priv_data;
|
|
|
|
|
|
|
|
bpf_iter_fini_seq_net(priv_data);
|
|
|
|
kvfree(iter->batch);
|
|
|
|
}
|
|
|
|
|
2021-08-14 09:57:15 +08:00
|
|
|
static const struct bpf_iter_seq_info unix_seq_info = {
|
|
|
|
.seq_ops = &bpf_iter_unix_seq_ops,
|
2022-01-13 08:28:46 +08:00
|
|
|
.init_seq_private = bpf_iter_init_unix,
|
|
|
|
.fini_seq_private = bpf_iter_fini_unix,
|
|
|
|
.seq_priv_size = sizeof(struct bpf_unix_iter_state),
|
2021-08-14 09:57:15 +08:00
|
|
|
};
|
|
|
|
|
2022-01-13 08:28:47 +08:00
|
|
|
static const struct bpf_func_proto *
|
|
|
|
bpf_iter_unix_get_func_proto(enum bpf_func_id func_id,
|
|
|
|
const struct bpf_prog *prog)
|
|
|
|
{
|
|
|
|
switch (func_id) {
|
|
|
|
case BPF_FUNC_setsockopt:
|
|
|
|
return &bpf_sk_setsockopt_proto;
|
|
|
|
case BPF_FUNC_getsockopt:
|
|
|
|
return &bpf_sk_getsockopt_proto;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 09:57:15 +08:00
|
|
|
static struct bpf_iter_reg unix_reg_info = {
|
|
|
|
.target = "unix",
|
|
|
|
.ctx_arg_info_size = 1,
|
|
|
|
.ctx_arg_info = {
|
|
|
|
{ offsetof(struct bpf_iter__unix, unix_sk),
|
|
|
|
PTR_TO_BTF_ID_OR_NULL },
|
|
|
|
},
|
2022-01-13 08:28:47 +08:00
|
|
|
.get_func_proto = bpf_iter_unix_get_func_proto,
|
2021-08-14 09:57:15 +08:00
|
|
|
.seq_info = &unix_seq_info,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void __init bpf_iter_register(void)
|
|
|
|
{
|
|
|
|
unix_reg_info.ctx_arg_info[0].btf_id = btf_sock_ids[BTF_SOCK_TYPE_UNIX];
|
|
|
|
if (bpf_iter_reg_target(&unix_reg_info))
|
|
|
|
pr_warn("Warning: could not register bpf iterator unix\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int __init af_unix_init(void)
|
|
|
|
{
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
int i, rc = -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-12-10 02:31:43 +08:00
|
|
|
BUILD_BUG_ON(sizeof(struct unix_skb_parms) > sizeof_field(struct sk_buff, cb));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
af_unix: Put pathname sockets in the global hash table.
Commit cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
accidentally broke user API for pathname sockets. A socket was able to
connect() to a pathname socket whose file was visible even if they were in
different network namespaces.
The commit puts all sockets into a per-netns hash table. As a result,
connect() to a pathname socket in a different netns fails to find it in the
caller's per-netns hash table and returns -ECONNREFUSED even when the task
can view the peer socket file.
We can reproduce this issue by:
Console A:
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.bind('test')
>>> s.listen(32)
Console B:
# ip netns add test
# ip netns exec test sh
# python3
>>> from socket import *
>>> s = socket(AF_UNIX, SOCK_STREAM, 0)
>>> s.connect('test')
Note when dumping sockets by sock_diag, procfs, and bpf_iter, they are
filtered only by netns. In other words, even if they are visible and
connect()able, all sockets in different netns are skipped while iterating
sockets. Thus, we need a fix only for finding a peer pathname socket.
This patch adds a global hash table for pathname sockets, links them with
sk_bind_node, and uses it in unix_find_socket_byinode(). By doing so, we
can keep sockets in per-netns hash tables and dump them easily.
Thanks to Sachin Sant and Leonard Crestez for reports, logs and a reproducer.
Fixes: cf2f225e2653 ("af_unix: Put a socket into a per-netns hash table.")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Leonard Crestez <cdleonard@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.ibm.com>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Tested-by: Leonard Crestez <cdleonard@gmail.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2022-07-02 23:48:17 +08:00
|
|
|
for (i = 0; i < UNIX_HASH_SIZE / 2; i++) {
|
|
|
|
spin_lock_init(&bsd_socket_locks[i]);
|
|
|
|
INIT_HLIST_HEAD(&bsd_socket_buckets[i]);
|
|
|
|
}
|
|
|
|
|
2021-08-17 03:03:21 +08:00
|
|
|
rc = proto_register(&unix_dgram_proto, 1);
|
|
|
|
if (rc != 0) {
|
|
|
|
pr_crit("%s: Cannot create unix_sock SLAB cache!\n", __func__);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = proto_register(&unix_stream_proto, 1);
|
2007-02-09 22:25:23 +08:00
|
|
|
if (rc != 0) {
|
2013-12-06 18:03:36 +08:00
|
|
|
pr_crit("%s: Cannot create unix_sock SLAB cache!\n", __func__);
|
2022-12-08 23:01:58 +08:00
|
|
|
proto_unregister(&unix_dgram_proto);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
sock_register(&unix_family_ops);
|
2007-11-20 14:29:30 +08:00
|
|
|
register_pernet_subsys(&unix_net_ops);
|
2021-07-05 03:02:47 +08:00
|
|
|
unix_bpf_build_proto();
|
2021-08-14 09:57:15 +08:00
|
|
|
|
2023-10-27 05:23:05 +08:00
|
|
|
#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
|
2021-08-14 09:57:15 +08:00
|
|
|
bpf_iter_register();
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2023-10-27 05:23:05 +08:00
|
|
|
/* Later than subsys_initcall() because we depend on stuff initialised there */
|
2008-04-24 15:59:25 +08:00
|
|
|
fs_initcall(af_unix_init);
|