cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

diag.c (7736B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2#include <linux/types.h>
      3#include <linux/spinlock.h>
      4#include <linux/sock_diag.h>
      5#include <linux/unix_diag.h>
      6#include <linux/skbuff.h>
      7#include <linux/module.h>
      8#include <linux/uidgid.h>
      9#include <net/netlink.h>
     10#include <net/af_unix.h>
     11#include <net/tcp_states.h>
     12#include <net/sock.h>
     13
     14static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb)
     15{
     16	/* might or might not have unix_table_locks */
     17	struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr);
     18
     19	if (!addr)
     20		return 0;
     21
     22	return nla_put(nlskb, UNIX_DIAG_NAME,
     23		       addr->len - offsetof(struct sockaddr_un, sun_path),
     24		       addr->name->sun_path);
     25}
     26
     27static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
     28{
     29	struct dentry *dentry = unix_sk(sk)->path.dentry;
     30
     31	if (dentry) {
     32		struct unix_diag_vfs uv = {
     33			.udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
     34			.udiag_vfs_dev = dentry->d_sb->s_dev,
     35		};
     36
     37		return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
     38	}
     39
     40	return 0;
     41}
     42
     43static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb)
     44{
     45	struct sock *peer;
     46	int ino;
     47
     48	peer = unix_peer_get(sk);
     49	if (peer) {
     50		unix_state_lock(peer);
     51		ino = sock_i_ino(peer);
     52		unix_state_unlock(peer);
     53		sock_put(peer);
     54
     55		return nla_put_u32(nlskb, UNIX_DIAG_PEER, ino);
     56	}
     57
     58	return 0;
     59}
     60
     61static int sk_diag_dump_icons(struct sock *sk, struct sk_buff *nlskb)
     62{
     63	struct sk_buff *skb;
     64	struct nlattr *attr;
     65	u32 *buf;
     66	int i;
     67
     68	if (sk->sk_state == TCP_LISTEN) {
     69		spin_lock(&sk->sk_receive_queue.lock);
     70
     71		attr = nla_reserve(nlskb, UNIX_DIAG_ICONS,
     72				   sk->sk_receive_queue.qlen * sizeof(u32));
     73		if (!attr)
     74			goto errout;
     75
     76		buf = nla_data(attr);
     77		i = 0;
     78		skb_queue_walk(&sk->sk_receive_queue, skb) {
     79			struct sock *req, *peer;
     80
     81			req = skb->sk;
     82			/*
     83			 * The state lock is outer for the same sk's
     84			 * queue lock. With the other's queue locked it's
     85			 * OK to lock the state.
     86			 */
     87			unix_state_lock_nested(req);
     88			peer = unix_sk(req)->peer;
     89			buf[i++] = (peer ? sock_i_ino(peer) : 0);
     90			unix_state_unlock(req);
     91		}
     92		spin_unlock(&sk->sk_receive_queue.lock);
     93	}
     94
     95	return 0;
     96
     97errout:
     98	spin_unlock(&sk->sk_receive_queue.lock);
     99	return -EMSGSIZE;
    100}
    101
    102static int sk_diag_show_rqlen(struct sock *sk, struct sk_buff *nlskb)
    103{
    104	struct unix_diag_rqlen rql;
    105
    106	if (sk->sk_state == TCP_LISTEN) {
    107		rql.udiag_rqueue = sk->sk_receive_queue.qlen;
    108		rql.udiag_wqueue = sk->sk_max_ack_backlog;
    109	} else {
    110		rql.udiag_rqueue = (u32) unix_inq_len(sk);
    111		rql.udiag_wqueue = (u32) unix_outq_len(sk);
    112	}
    113
    114	return nla_put(nlskb, UNIX_DIAG_RQLEN, sizeof(rql), &rql);
    115}
    116
    117static int sk_diag_dump_uid(struct sock *sk, struct sk_buff *nlskb)
    118{
    119	uid_t uid = from_kuid_munged(sk_user_ns(nlskb->sk), sock_i_uid(sk));
    120	return nla_put(nlskb, UNIX_DIAG_UID, sizeof(uid_t), &uid);
    121}
    122
    123static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
    124		u32 portid, u32 seq, u32 flags, int sk_ino)
    125{
    126	struct nlmsghdr *nlh;
    127	struct unix_diag_msg *rep;
    128
    129	nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
    130			flags);
    131	if (!nlh)
    132		return -EMSGSIZE;
    133
    134	rep = nlmsg_data(nlh);
    135	rep->udiag_family = AF_UNIX;
    136	rep->udiag_type = sk->sk_type;
    137	rep->udiag_state = sk->sk_state;
    138	rep->pad = 0;
    139	rep->udiag_ino = sk_ino;
    140	sock_diag_save_cookie(sk, rep->udiag_cookie);
    141
    142	if ((req->udiag_show & UDIAG_SHOW_NAME) &&
    143	    sk_diag_dump_name(sk, skb))
    144		goto out_nlmsg_trim;
    145
    146	if ((req->udiag_show & UDIAG_SHOW_VFS) &&
    147	    sk_diag_dump_vfs(sk, skb))
    148		goto out_nlmsg_trim;
    149
    150	if ((req->udiag_show & UDIAG_SHOW_PEER) &&
    151	    sk_diag_dump_peer(sk, skb))
    152		goto out_nlmsg_trim;
    153
    154	if ((req->udiag_show & UDIAG_SHOW_ICONS) &&
    155	    sk_diag_dump_icons(sk, skb))
    156		goto out_nlmsg_trim;
    157
    158	if ((req->udiag_show & UDIAG_SHOW_RQLEN) &&
    159	    sk_diag_show_rqlen(sk, skb))
    160		goto out_nlmsg_trim;
    161
    162	if ((req->udiag_show & UDIAG_SHOW_MEMINFO) &&
    163	    sock_diag_put_meminfo(sk, skb, UNIX_DIAG_MEMINFO))
    164		goto out_nlmsg_trim;
    165
    166	if (nla_put_u8(skb, UNIX_DIAG_SHUTDOWN, sk->sk_shutdown))
    167		goto out_nlmsg_trim;
    168
    169	if ((req->udiag_show & UDIAG_SHOW_UID) &&
    170	    sk_diag_dump_uid(sk, skb))
    171		goto out_nlmsg_trim;
    172
    173	nlmsg_end(skb, nlh);
    174	return 0;
    175
    176out_nlmsg_trim:
    177	nlmsg_cancel(skb, nlh);
    178	return -EMSGSIZE;
    179}
    180
    181static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
    182		u32 portid, u32 seq, u32 flags)
    183{
    184	int sk_ino;
    185
    186	unix_state_lock(sk);
    187	sk_ino = sock_i_ino(sk);
    188	unix_state_unlock(sk);
    189
    190	if (!sk_ino)
    191		return 0;
    192
    193	return sk_diag_fill(sk, skb, req, portid, seq, flags, sk_ino);
    194}
    195
    196static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
    197{
    198	struct unix_diag_req *req;
    199	int num, s_num, slot, s_slot;
    200	struct net *net = sock_net(skb->sk);
    201
    202	req = nlmsg_data(cb->nlh);
    203
    204	s_slot = cb->args[0];
    205	num = s_num = cb->args[1];
    206
    207	for (slot = s_slot;
    208	     slot < ARRAY_SIZE(unix_socket_table);
    209	     s_num = 0, slot++) {
    210		struct sock *sk;
    211
    212		num = 0;
    213		spin_lock(&unix_table_locks[slot]);
    214		sk_for_each(sk, &unix_socket_table[slot]) {
    215			if (!net_eq(sock_net(sk), net))
    216				continue;
    217			if (num < s_num)
    218				goto next;
    219			if (!(req->udiag_states & (1 << sk->sk_state)))
    220				goto next;
    221			if (sk_diag_dump(sk, skb, req,
    222					 NETLINK_CB(cb->skb).portid,
    223					 cb->nlh->nlmsg_seq,
    224					 NLM_F_MULTI) < 0) {
    225				spin_unlock(&unix_table_locks[slot]);
    226				goto done;
    227			}
    228next:
    229			num++;
    230		}
    231		spin_unlock(&unix_table_locks[slot]);
    232	}
    233done:
    234	cb->args[0] = slot;
    235	cb->args[1] = num;
    236
    237	return skb->len;
    238}
    239
    240static struct sock *unix_lookup_by_ino(unsigned int ino)
    241{
    242	struct sock *sk;
    243	int i;
    244
    245	for (i = 0; i < ARRAY_SIZE(unix_socket_table); i++) {
    246		spin_lock(&unix_table_locks[i]);
    247		sk_for_each(sk, &unix_socket_table[i])
    248			if (ino == sock_i_ino(sk)) {
    249				sock_hold(sk);
    250				spin_unlock(&unix_table_locks[i]);
    251				return sk;
    252			}
    253		spin_unlock(&unix_table_locks[i]);
    254	}
    255	return NULL;
    256}
    257
    258static int unix_diag_get_exact(struct sk_buff *in_skb,
    259			       const struct nlmsghdr *nlh,
    260			       struct unix_diag_req *req)
    261{
    262	int err = -EINVAL;
    263	struct sock *sk;
    264	struct sk_buff *rep;
    265	unsigned int extra_len;
    266	struct net *net = sock_net(in_skb->sk);
    267
    268	if (req->udiag_ino == 0)
    269		goto out_nosk;
    270
    271	sk = unix_lookup_by_ino(req->udiag_ino);
    272	err = -ENOENT;
    273	if (sk == NULL)
    274		goto out_nosk;
    275	if (!net_eq(sock_net(sk), net))
    276		goto out;
    277
    278	err = sock_diag_check_cookie(sk, req->udiag_cookie);
    279	if (err)
    280		goto out;
    281
    282	extra_len = 256;
    283again:
    284	err = -ENOMEM;
    285	rep = nlmsg_new(sizeof(struct unix_diag_msg) + extra_len, GFP_KERNEL);
    286	if (!rep)
    287		goto out;
    288
    289	err = sk_diag_fill(sk, rep, req, NETLINK_CB(in_skb).portid,
    290			   nlh->nlmsg_seq, 0, req->udiag_ino);
    291	if (err < 0) {
    292		nlmsg_free(rep);
    293		extra_len += 256;
    294		if (extra_len >= PAGE_SIZE)
    295			goto out;
    296
    297		goto again;
    298	}
    299	err = nlmsg_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid);
    300
    301out:
    302	if (sk)
    303		sock_put(sk);
    304out_nosk:
    305	return err;
    306}
    307
    308static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
    309{
    310	int hdrlen = sizeof(struct unix_diag_req);
    311	struct net *net = sock_net(skb->sk);
    312
    313	if (nlmsg_len(h) < hdrlen)
    314		return -EINVAL;
    315
    316	if (h->nlmsg_flags & NLM_F_DUMP) {
    317		struct netlink_dump_control c = {
    318			.dump = unix_diag_dump,
    319		};
    320		return netlink_dump_start(net->diag_nlsk, skb, h, &c);
    321	} else
    322		return unix_diag_get_exact(skb, h, nlmsg_data(h));
    323}
    324
    325static const struct sock_diag_handler unix_diag_handler = {
    326	.family = AF_UNIX,
    327	.dump = unix_diag_handler_dump,
    328};
    329
    330static int __init unix_diag_init(void)
    331{
    332	return sock_diag_register(&unix_diag_handler);
    333}
    334
    335static void __exit unix_diag_exit(void)
    336{
    337	sock_diag_unregister(&unix_diag_handler);
    338}
    339
    340module_init(unix_diag_init);
    341module_exit(unix_diag_exit);
    342MODULE_LICENSE("GPL");
    343MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1 /* AF_LOCAL */);