Format a socket as a one-line summary, ready to drop into the `target` column of /proc/ /fd_table.txt. Returns "" if the inode isn't a socket or if the socket isn't in our index.
| 54 | // column of /proc/<pid>/fd_table.txt. Returns "" if the inode isn't a |
| 55 | // socket or if the socket isn't in our index. |
| 56 | std::string format_socket_for_fd(const Engine& eng, VAddr inode_va, |
| 57 | const SocketIndex& idx) |
| 58 | { |
| 59 | if (inode_va == 0) return {}; |
| 60 | const auto& isf = eng.isf(); |
| 61 | |
| 62 | // Read inode.i_mode and check S_IFMT == S_IFSOCK. |
| 63 | u64 i_mode_off = 0; |
| 64 | try { i_mode_off = isf.field_offset("inode", "i_mode"); } |
| 65 | catch (...) { return {}; } |
| 66 | u16 i_mode = 0; |
| 67 | if (!kva_read_pod(eng, inode_va + i_mode_off, i_mode)) return {}; |
| 68 | if ((i_mode & 0170000) != 0140000) return {}; // S_IFSOCK |
| 69 | |
| 70 | // inode is embedded inside socket_alloc at offset 0x80 (= sizeof(struct |
| 71 | // socket) on x86_64 6.x). socket_alloc.socket is at offset 0. |
| 72 | u64 sa_vfs_inode_off = 0x80; |
| 73 | try { sa_vfs_inode_off = isf.field_offset("socket_alloc", "vfs_inode"); } |
| 74 | catch (...) {} |
| 75 | VAddr socket_va = inode_va - sa_vfs_inode_off; |
| 76 | |
| 77 | // socket.sk is at offset 0x18. |
| 78 | u64 sock_off_in_socket = 0x18; |
| 79 | try { sock_off_in_socket = isf.field_offset("socket", "sk"); } |
| 80 | catch (...) {} |
| 81 | VAddr sock_va = 0; |
| 82 | if (!kva_read_pod(eng, socket_va + sock_off_in_socket, sock_va) || |
| 83 | sock_va == 0) |
| 84 | return fmt::format("socket:[<unread> sk @ {:#x}]", socket_va); |
| 85 | |
| 86 | auto* s = find_socket_by_va(idx, sock_va); |
| 87 | if (!s) { |
| 88 | // Not in TCP/UDP index — could be Unix, netlink, packet, raw, etc. |
| 89 | // Read sock_common.skc_family + sock.sk_protocol directly so we can |
| 90 | // at least say what KIND of socket this is, even if we can't show |
| 91 | // an endpoint. |
| 92 | u64 skc_family_off = 0x10, sk_protocol_off = 0x23e; |
| 93 | try { skc_family_off = isf.field_offset("sock_common", "skc_family"); } catch (...) {} |
| 94 | try { sk_protocol_off = isf.field_offset("sock", "sk_protocol"); } catch (...) {} |
| 95 | u16 fam = 0; u16 proto = 0; |
| 96 | kva_read_pod(eng, sock_va + skc_family_off, fam); |
| 97 | kva_read_pod(eng, sock_va + sk_protocol_off, proto); |
| 98 | |
| 99 | const char* fname = "?"; |
| 100 | switch (fam) { |
| 101 | case 1: fname = "UNIX"; break; |
| 102 | case 2: fname = "INET"; break; |
| 103 | case 10: fname = "INET6"; break; |
| 104 | case 16: fname = "NETLINK"; break; |
| 105 | case 17: fname = "PACKET"; break; |
| 106 | case 40: fname = "VSOCK"; break; |
| 107 | default: break; |
| 108 | } |
| 109 | |
| 110 | // For AF_UNIX, try to read the bound path from unix_sock.addr.name. |
| 111 | if (fam == 1) { |
| 112 | // unix_sock embeds sock at offset 0, so sock_va == unix_sock_va. |
| 113 | u64 unix_addr_off = 0, unix_addr_name_off = 0; |
no test coverage detected