Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'BPF iterator for UNIX domain socket.'

Kuniyuki Iwashima says:

====================

This patch set adds BPF iterator support for UNIX domain socket. The first
patch implements it, and the second adds "%c" support for BPF_SEQ_PRINTF().

Thanks to Yonghong Song for the fix [0] for the LLVM code gen. The fix
prevents the LLVM compiler from transforming the loop exit condition '<' to
'!=', where the upper bound is not a constant. The transformation leads
the verifier to interpret it as an infinite loop.

And thanks to Andrii Nakryiko for its workaround [1].

[0] https://reviews.llvm.org/D107483
[1] https://lore.kernel.org/netdev/CAEf4BzZ3sVx1m1mOCcPcuVPiY6cWEAO=6VGHDiXEs9ZVD-RoLg@mail.gmail.com/

Changelog:
v6:
- Align the header "Inde" column
- Change int vars to __u64 not to break test_progs-no_alu32
- Move the if statement into the for loop not to depend on the fix [0]
- Drop the README change
- Modify "%c" positive test patterns

v5:
https://lore.kernel.org/netdev/20210812164557.79046-1-kuniyu@amazon.co.jp/
- Align header line of bpf_iter_unix.c
- Add test for "%c"

v4:
https://lore.kernel.org/netdev/20210810092807.13190-1-kuniyu@amazon.co.jp/
- Check IS_BUILTIN(CONFIG_UNIX)
- Support "%c" in BPF_SEQ_PRINTF()
- Uncomment the code to print the name of the abstract socket
- Mention the LLVM fix in README.rst
- Remove the 'aligned' attribute in bpf_iter.h
- Keep the format string on a single line

v3:
https://lore.kernel.org/netdev/20210804070851.97834-1-kuniyu@amazon.co.jp/
- Export some functions for CONFIG_UNIX=m

v2:
https://lore.kernel.org/netdev/20210803011110.21205-1-kuniyu@amazon.co.jp/
- Implement bpf_iter specific seq_ops->stop()
- Add bpf_iter__unix in bpf_iter.h
- Move common definitions in selftest to bpf_tracing_net.h
- Include the code for abstract UNIX domain socket as comment in selftest
- Use ASSERT_OK_PTR() instead of CHECK()
- Make ternary operators on single line

v1:
https://lore.kernel.org/netdev/20210729233645.4869-1-kuniyu@amazon.co.jp/
====================

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+223 -5
+2 -1
include/linux/btf_ids.h
··· 172 172 BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP_TW, tcp_timewait_sock) \ 173 173 BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP6, tcp6_sock) \ 174 174 BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP, udp_sock) \ 175 - BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP6, udp6_sock) 175 + BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP6, udp6_sock) \ 176 + BTF_SOCK_TYPE(BTF_SOCK_TYPE_UNIX, unix_sock) 176 177 177 178 enum { 178 179 #define BTF_SOCK_TYPE(name, str) name,
+14
kernel/bpf/helpers.c
··· 908 908 num_spec++; 909 909 910 910 continue; 911 + } else if (fmt[i] == 'c') { 912 + if (!tmp_buf) 913 + goto nocopy_fmt; 914 + 915 + if (tmp_buf_end == tmp_buf) { 916 + err = -ENOSPC; 917 + goto out; 918 + } 919 + 920 + *tmp_buf = raw_args[num_spec]; 921 + tmp_buf++; 922 + num_spec++; 923 + 924 + continue; 911 925 } 912 926 913 927 sizeof_cur_arg = sizeof(int);
+93
net/unix/af_unix.c
··· 113 113 #include <linux/security.h> 114 114 #include <linux/freezer.h> 115 115 #include <linux/file.h> 116 + #include <linux/btf_ids.h> 116 117 117 118 #include "scm.h" 118 119 ··· 3144 3143 .stop = unix_seq_stop, 3145 3144 .show = unix_seq_show, 3146 3145 }; 3146 + 3147 + #if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL) 3148 + struct bpf_iter__unix { 3149 + __bpf_md_ptr(struct bpf_iter_meta *, meta); 3150 + __bpf_md_ptr(struct unix_sock *, unix_sk); 3151 + uid_t uid __aligned(8); 3152 + }; 3153 + 3154 + static int unix_prog_seq_show(struct bpf_prog *prog, struct bpf_iter_meta *meta, 3155 + struct unix_sock *unix_sk, uid_t uid) 3156 + { 3157 + struct bpf_iter__unix ctx; 3158 + 3159 + meta->seq_num--; /* skip SEQ_START_TOKEN */ 3160 + ctx.meta = meta; 3161 + ctx.unix_sk = unix_sk; 3162 + ctx.uid = uid; 3163 + return bpf_iter_run_prog(prog, &ctx); 3164 + } 3165 + 3166 + static int bpf_iter_unix_seq_show(struct seq_file *seq, void *v) 3167 + { 3168 + struct bpf_iter_meta meta; 3169 + struct bpf_prog *prog; 3170 + struct sock *sk = v; 3171 + uid_t uid; 3172 + 3173 + if (v == SEQ_START_TOKEN) 3174 + return 0; 3175 + 3176 + uid = from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)); 3177 + meta.seq = seq; 3178 + prog = bpf_iter_get_info(&meta, false); 3179 + return unix_prog_seq_show(prog, &meta, v, uid); 3180 + } 3181 + 3182 + static void bpf_iter_unix_seq_stop(struct seq_file *seq, void *v) 3183 + { 3184 + struct bpf_iter_meta meta; 3185 + struct bpf_prog *prog; 3186 + 3187 + if (!v) { 3188 + meta.seq = seq; 3189 + prog = bpf_iter_get_info(&meta, true); 3190 + if (prog) 3191 + (void)unix_prog_seq_show(prog, &meta, v, 0); 3192 + } 3193 + 3194 + unix_seq_stop(seq, v); 3195 + } 3196 + 3197 + static const struct seq_operations bpf_iter_unix_seq_ops = { 3198 + .start = unix_seq_start, 3199 + .next = unix_seq_next, 3200 + .stop = bpf_iter_unix_seq_stop, 3201 + .show = bpf_iter_unix_seq_show, 3202 + }; 3203 + #endif 3147 3204 #endif 3148 3205 3149 3206 static const struct net_proto_family unix_family_ops = { ··· 3242 3183 .exit = unix_net_exit, 3243 3184 }; 3244 3185 3186 + #if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 3187 + DEFINE_BPF_ITER_FUNC(unix, struct bpf_iter_meta *meta, 3188 + struct unix_sock *unix_sk, uid_t uid) 3189 + 3190 + static const struct bpf_iter_seq_info unix_seq_info = { 3191 + .seq_ops = &bpf_iter_unix_seq_ops, 3192 + .init_seq_private = bpf_iter_init_seq_net, 3193 + .fini_seq_private = bpf_iter_fini_seq_net, 3194 + .seq_priv_size = sizeof(struct seq_net_private), 3195 + }; 3196 + 3197 + static struct bpf_iter_reg unix_reg_info = { 3198 + .target = "unix", 3199 + .ctx_arg_info_size = 1, 3200 + .ctx_arg_info = { 3201 + { offsetof(struct bpf_iter__unix, unix_sk), 3202 + PTR_TO_BTF_ID_OR_NULL }, 3203 + }, 3204 + .seq_info = &unix_seq_info, 3205 + }; 3206 + 3207 + static void __init bpf_iter_register(void) 3208 + { 3209 + unix_reg_info.ctx_arg_info[0].btf_id = btf_sock_ids[BTF_SOCK_TYPE_UNIX]; 3210 + if (bpf_iter_reg_target(&unix_reg_info)) 3211 + pr_warn("Warning: could not register bpf iterator unix\n"); 3212 + } 3213 + #endif 3214 + 3245 3215 static int __init af_unix_init(void) 3246 3216 { 3247 3217 int rc = -1; ··· 3286 3198 sock_register(&unix_family_ops); 3287 3199 register_pernet_subsys(&unix_net_ops); 3288 3200 unix_bpf_build_proto(); 3201 + 3202 + #if IS_BUILTIN(CONFIG_UNIX) && defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) 3203 + bpf_iter_register(); 3204 + #endif 3205 + 3289 3206 out: 3290 3207 return rc; 3291 3208 }
+16
tools/testing/selftests/bpf/prog_tests/bpf_iter.c
··· 13 13 #include "bpf_iter_tcp6.skel.h" 14 14 #include "bpf_iter_udp4.skel.h" 15 15 #include "bpf_iter_udp6.skel.h" 16 + #include "bpf_iter_unix.skel.h" 16 17 #include "bpf_iter_test_kern1.skel.h" 17 18 #include "bpf_iter_test_kern2.skel.h" 18 19 #include "bpf_iter_test_kern3.skel.h" ··· 312 311 do_dummy_read(skel->progs.dump_udp6); 313 312 314 313 bpf_iter_udp6__destroy(skel); 314 + } 315 + 316 + static void test_unix(void) 317 + { 318 + struct bpf_iter_unix *skel; 319 + 320 + skel = bpf_iter_unix__open_and_load(); 321 + if (!ASSERT_OK_PTR(skel, "bpf_iter_unix__open_and_load")) 322 + return; 323 + 324 + do_dummy_read(skel->progs.dump_unix); 325 + 326 + bpf_iter_unix__destroy(skel); 315 327 } 316 328 317 329 /* The expected string is less than 16 bytes */ ··· 1269 1255 test_udp4(); 1270 1256 if (test__start_subtest("udp6")) 1271 1257 test_udp6(); 1258 + if (test__start_subtest("unix")) 1259 + test_unix(); 1272 1260 if (test__start_subtest("anon")) 1273 1261 test_anon_iter(false); 1274 1262 if (test__start_subtest("anon-read-one-char"))
+3 -1
tools/testing/selftests/bpf/prog_tests/snprintf.c
··· 19 19 #define EXP_ADDR_OUT "0000000000000000 ffff00000add4e55 " 20 20 #define EXP_ADDR_RET sizeof(EXP_ADDR_OUT "unknownhashedptr") 21 21 22 - #define EXP_STR_OUT "str1 longstr" 22 + #define EXP_STR_OUT "str1 a b c d e longstr" 23 23 #define EXP_STR_RET sizeof(EXP_STR_OUT) 24 24 25 25 #define EXP_OVER_OUT "%over" ··· 114 114 ASSERT_ERR(load_single_snprintf("%"), "invalid specifier 3"); 115 115 ASSERT_ERR(load_single_snprintf("%12345678"), "invalid specifier 4"); 116 116 ASSERT_ERR(load_single_snprintf("%--------"), "invalid specifier 5"); 117 + ASSERT_ERR(load_single_snprintf("%lc"), "invalid specifier 6"); 118 + ASSERT_ERR(load_single_snprintf("%llc"), "invalid specifier 7"); 117 119 ASSERT_ERR(load_single_snprintf("\x80"), "non ascii character"); 118 120 ASSERT_ERR(load_single_snprintf("\x1"), "non printable character"); 119 121 }
+8
tools/testing/selftests/bpf/progs/bpf_iter.h
··· 12 12 #define tcp6_sock tcp6_sock___not_used 13 13 #define bpf_iter__udp bpf_iter__udp___not_used 14 14 #define udp6_sock udp6_sock___not_used 15 + #define bpf_iter__unix bpf_iter__unix___not_used 15 16 #define bpf_iter__bpf_map_elem bpf_iter__bpf_map_elem___not_used 16 17 #define bpf_iter__bpf_sk_storage_map bpf_iter__bpf_sk_storage_map___not_used 17 18 #define bpf_iter__sockmap bpf_iter__sockmap___not_used ··· 33 32 #undef tcp6_sock 34 33 #undef bpf_iter__udp 35 34 #undef udp6_sock 35 + #undef bpf_iter__unix 36 36 #undef bpf_iter__bpf_map_elem 37 37 #undef bpf_iter__bpf_sk_storage_map 38 38 #undef bpf_iter__sockmap ··· 103 101 struct udp6_sock { 104 102 struct udp_sock udp; 105 103 struct ipv6_pinfo inet6; 104 + } __attribute__((preserve_access_index)); 105 + 106 + struct bpf_iter__unix { 107 + struct bpf_iter_meta *meta; 108 + struct unix_sock *unix_sk; 109 + uid_t uid; 106 110 } __attribute__((preserve_access_index)); 107 111 108 112 struct bpf_iter__bpf_map_elem {
+80
tools/testing/selftests/bpf/progs/bpf_iter_unix.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright Amazon.com Inc. or its affiliates. */ 3 + #include "bpf_iter.h" 4 + #include "bpf_tracing_net.h" 5 + #include <bpf/bpf_helpers.h> 6 + #include <bpf/bpf_endian.h> 7 + 8 + char _license[] SEC("license") = "GPL"; 9 + 10 + static long sock_i_ino(const struct sock *sk) 11 + { 12 + const struct socket *sk_socket = sk->sk_socket; 13 + const struct inode *inode; 14 + unsigned long ino; 15 + 16 + if (!sk_socket) 17 + return 0; 18 + 19 + inode = &container_of(sk_socket, struct socket_alloc, socket)->vfs_inode; 20 + bpf_probe_read_kernel(&ino, sizeof(ino), &inode->i_ino); 21 + return ino; 22 + } 23 + 24 + SEC("iter/unix") 25 + int dump_unix(struct bpf_iter__unix *ctx) 26 + { 27 + struct unix_sock *unix_sk = ctx->unix_sk; 28 + struct sock *sk = (struct sock *)unix_sk; 29 + struct seq_file *seq; 30 + __u32 seq_num; 31 + 32 + if (!unix_sk) 33 + return 0; 34 + 35 + seq = ctx->meta->seq; 36 + seq_num = ctx->meta->seq_num; 37 + if (seq_num == 0) 38 + BPF_SEQ_PRINTF(seq, "Num RefCount Protocol Flags Type St Inode Path\n"); 39 + 40 + BPF_SEQ_PRINTF(seq, "%pK: %08X %08X %08X %04X %02X %8lu", 41 + unix_sk, 42 + sk->sk_refcnt.refs.counter, 43 + 0, 44 + sk->sk_state == TCP_LISTEN ? __SO_ACCEPTCON : 0, 45 + sk->sk_type, 46 + sk->sk_socket ? 47 + (sk->sk_state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) : 48 + (sk->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING), 49 + sock_i_ino(sk)); 50 + 51 + if (unix_sk->addr) { 52 + if (!UNIX_ABSTRACT(unix_sk)) { 53 + BPF_SEQ_PRINTF(seq, " %s", unix_sk->addr->name->sun_path); 54 + } else { 55 + /* The name of the abstract UNIX domain socket starts 56 + * with '\0' and can contain '\0'. The null bytes 57 + * should be escaped as done in unix_seq_show(). 58 + */ 59 + __u64 i, len; 60 + 61 + len = unix_sk->addr->len - sizeof(short); 62 + 63 + BPF_SEQ_PRINTF(seq, " @"); 64 + 65 + for (i = 1; i < len; i++) { 66 + /* unix_mkname() tests this upper bound. */ 67 + if (i >= sizeof(struct sockaddr_un)) 68 + break; 69 + 70 + BPF_SEQ_PRINTF(seq, "%c", 71 + unix_sk->addr->name->sun_path[i] ?: 72 + '@'); 73 + } 74 + } 75 + } 76 + 77 + BPF_SEQ_PRINTF(seq, "\n"); 78 + 79 + return 0; 80 + }
+4
tools/testing/selftests/bpf/progs/bpf_tracing_net.h
··· 5 5 #define AF_INET 2 6 6 #define AF_INET6 10 7 7 8 + #define __SO_ACCEPTCON (1 << 16) 9 + #define UNIX_HASH_SIZE 256 10 + #define UNIX_ABSTRACT(unix_sk) (unix_sk->addr->hash < UNIX_HASH_SIZE) 11 + 8 12 #define SOL_TCP 6 9 13 #define TCP_CONGESTION 13 10 14 #define TCP_CA_NAME_MAX 16
+3 -3
tools/testing/selftests/bpf/progs/test_snprintf.c
··· 59 59 /* Kernel pointers */ 60 60 addr_ret = BPF_SNPRINTF(addr_out, sizeof(addr_out), "%pK %px %p", 61 61 0, 0xFFFF00000ADD4E55, 0xFFFF00000ADD4E55); 62 - /* Strings embedding */ 63 - str_ret = BPF_SNPRINTF(str_out, sizeof(str_out), "%s %+05s", 64 - str1, longstr); 62 + /* Strings and single-byte character embedding */ 63 + str_ret = BPF_SNPRINTF(str_out, sizeof(str_out), "%s % 9c %+2c %-3c %04c %0c %+05s", 64 + str1, 'a', 'b', 'c', 'd', 'e', longstr); 65 65 /* Overflow */ 66 66 over_ret = BPF_SNPRINTF(over_out, sizeof(over_out), "%%overflow"); 67 67 /* Padding of fixed width numbers */