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