From cd1d3274547a7f9c273d92caa848f83ade3bf100 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 27 Jul 2019 20:25:26 -0700 Subject: [PATCH 01/43] libbpf: return previous print callback from libbpf_set_print ANBZ: #5530 commit e87fd8bae44c3eaa6205c9c81419e773896dc157 upstream. By returning previously set print callback from libbpf_set_print, it's possible to restore it, eventually. This is useful when running many independent test with one default print function, but overriding log verbosity for particular subset of tests. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- tools/lib/bpf/libbpf.c | 5 ++++- tools/lib/bpf/libbpf.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 5c136af2b01d..24e91117b9e9 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -75,9 +75,12 @@ static int __base_pr(enum libbpf_print_level level, const char *format, static libbpf_print_fn_t __libbpf_pr = __base_pr; -void libbpf_set_print(libbpf_print_fn_t fn) +libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn) { + libbpf_print_fn_t old_print_fn = __libbpf_pr; + __libbpf_pr = fn; + return old_print_fn; } __printf(2, 3) diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 5cbf459ece0b..8a9d462a6f6d 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -57,7 +57,7 @@ enum libbpf_print_level { typedef int (*libbpf_print_fn_t)(enum libbpf_print_level level, const char *, va_list ap); -LIBBPF_API void libbpf_set_print(libbpf_print_fn_t fn); +LIBBPF_API libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn); /* Hide internal to user */ struct bpf_object; -- Gitee From 79f9b82a352b7f331f9df3606dbdf40cb6ec2222 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Mon, 29 Jul 2019 09:59:13 -0700 Subject: [PATCH 02/43] tcp: tcp_syn_flood_action read port from socket ANBZ: #5530 commit 965112785e4bd4355262c6c5a32ea8f349adb401 upstream. This allows us to call this function before an SKB has been allocated. Signed-off-by: Petar Penkov Reviewed-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- net/ipv4/tcp_input.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4c139892d457..db6976f0ddbf 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6365,9 +6365,7 @@ EXPORT_SYMBOL(inet_reqsk_alloc); /* * Return true if a syncookie should be sent */ -static bool tcp_syn_flood_action(const struct sock *sk, - const struct sk_buff *skb, - const char *proto) +static bool tcp_syn_flood_action(const struct sock *sk, const char *proto) { struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; const char *msg = "Dropping request"; @@ -6387,7 +6385,7 @@ static bool tcp_syn_flood_action(const struct sock *sk, net->ipv4.sysctl_tcp_syncookies != 2 && xchg(&queue->synflood_warned, 1) == 0) net_info_ratelimited("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n", - proto, ntohs(tcp_hdr(skb)->dest), msg); + proto, sk->sk_num, msg); return want_cookie; } @@ -6430,7 +6428,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, */ if ((net->ipv4.sysctl_tcp_syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) { - want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name); + want_cookie = tcp_syn_flood_action(sk, rsk_ops->slab_name); if (!want_cookie) goto drop; } -- Gitee From 2b07373c64591993c3e7356238bcc1cffd4d0fc0 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Mon, 29 Jul 2019 09:59:14 -0700 Subject: [PATCH 03/43] tcp: add skb-less helpers to retrieve SYN cookie ANBZ: #5530 commit 9349d600fb6a1ca0aaeb515523e1bb5409483d76 upstream. This patch allows generation of a SYN cookie before an SKB has been allocated, as is the case at XDP. Signed-off-by: Petar Penkov Reviewed-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- include/net/tcp.h | 10 ++++++ net/ipv4/tcp_input.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 15 +++++++++ net/ipv6/tcp_ipv6.c | 15 +++++++++ 4 files changed, 113 insertions(+) diff --git a/include/net/tcp.h b/include/net/tcp.h index 5e5cfa25f195..74346f910c8c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -417,6 +417,16 @@ void tcp_parse_options(const struct net *net, const struct sk_buff *skb, int estab, struct tcp_fastopen_cookie *foc); const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); +/* + * BPF SKB-less helpers + */ +u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, + struct tcphdr *th, u32 *cookie); +u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, + struct tcphdr *th, u32 *cookie); +u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, + const struct tcp_request_sock_ops *af_ops, + struct sock *sk, struct tcphdr *th); /* * TCP v4 functions exported for the inet6 API */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index db6976f0ddbf..e47464e64550 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3778,6 +3778,49 @@ static void smc_parse_options(const struct tcphdr *th, #endif } +/* Try to parse the MSS option from the TCP header. Return 0 on failure, clamped + * value on success. + */ +static u16 tcp_parse_mss_option(const struct tcphdr *th, u16 user_mss) +{ + const unsigned char *ptr = (const unsigned char *)(th + 1); + int length = (th->doff * 4) - sizeof(struct tcphdr); + u16 mss = 0; + + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return mss; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + if (length < 2) + return mss; + opsize = *ptr++; + if (opsize < 2) /* "silly options" */ + return mss; + if (opsize > length) + return mss; /* fail on partial options */ + if (opcode == TCPOPT_MSS && opsize == TCPOLEN_MSS) { + u16 in_mss = get_unaligned_be16(ptr); + + if (in_mss) { + if (user_mss && user_mss < in_mss) + in_mss = user_mss; + mss = in_mss; + } + } + ptr += opsize - 2; + length -= opsize; + } + } + return mss; +} + /* Look for tcp options. Normally only called on SYN and SYNACK packets. * But, this can also be called on packets in the established flow when * the fast version below fails. @@ -6407,6 +6450,36 @@ static void tcp_reqsk_record_syn(const struct sock *sk, } } +/* If a SYN cookie is required and supported, returns a clamped MSS value to be + * used for SYN cookie generation. + */ +u16 tcp_get_syncookie_mss(struct request_sock_ops *rsk_ops, + const struct tcp_request_sock_ops *af_ops, + struct sock *sk, struct tcphdr *th) +{ + struct tcp_sock *tp = tcp_sk(sk); + u16 mss; + + if (sock_net(sk)->ipv4.sysctl_tcp_syncookies != 2 && + !inet_csk_reqsk_queue_is_full(sk)) + return 0; + + if (!tcp_syn_flood_action(sk, rsk_ops->slab_name)) + return 0; + + if (sk_acceptq_is_full(sk)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); + return 0; + } + + mss = tcp_parse_mss_option(th, tp->rx_opt.user_mss); + if (!mss) + mss = af_ops->mss_clamp; + + return mss; +} +EXPORT_SYMBOL_GPL(tcp_get_syncookie_mss); + int tcp_conn_request(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 06577df66971..6cb4b9d81e87 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1508,6 +1508,21 @@ static struct sock *tcp_v4_cookie_check(struct sock *sk, struct sk_buff *skb) return sk; } +u16 tcp_v4_get_syncookie(struct sock *sk, struct iphdr *iph, + struct tcphdr *th, u32 *cookie) +{ + u16 mss = 0; +#ifdef CONFIG_SYN_COOKIES + mss = tcp_get_syncookie_mss(&tcp_request_sock_ops, + &tcp_request_sock_ipv4_ops, sk, th); + if (mss) { + *cookie = __cookie_v4_init_sequence(iph, th, &mss); + tcp_synq_overflow(sk); + } +#endif + return mss; +} + /* The socket must have it's spinlock held when we get * here, unless it is a TCP_LISTEN socket. * diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1287534d7f20..f44a2588a272 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1033,6 +1033,21 @@ static struct sock *tcp_v6_cookie_check(struct sock *sk, struct sk_buff *skb) return sk; } +u16 tcp_v6_get_syncookie(struct sock *sk, struct ipv6hdr *iph, + struct tcphdr *th, u32 *cookie) +{ + u16 mss = 0; +#ifdef CONFIG_SYN_COOKIES + mss = tcp_get_syncookie_mss(&tcp6_request_sock_ops, + &tcp_request_sock_ipv6_ops, sk, th); + if (mss) { + *cookie = __cookie_v6_init_sequence(iph, th, &mss); + tcp_synq_overflow(sk); + } +#endif + return mss; +} + static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { if (skb->protocol == htons(ETH_P_IP)) -- Gitee From 696b04c995ea5a4c508138dbda886d0e40a71c02 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Mon, 29 Jul 2019 09:59:15 -0700 Subject: [PATCH 04/43] bpf: add bpf_tcp_gen_syncookie helper ANBZ: #5530 commit 70d66244317e958092e9c971b08dd5b7fd29d9cb upstream. This helper function allows BPF programs to try to generate SYN cookies, given a reference to a listener socket. The function works from XDP and with an skb context since bpf_skc_lookup_tcp can lookup a socket in both cases. Signed-off-by: Petar Penkov Suggested-by: Eric Dumazet Reviewed-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- include/uapi/linux/bpf.h | 30 ++++++++++++++++- net/core/filter.c | 73 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index e7f524b1f059..919d9633a67a 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2701,6 +2701,33 @@ union bpf_attr { * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. + * + * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header. + * + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** SYN cookie cannot be issued due to error + * + * **-ENOENT** SYN cookie should not be issued (no SYN flood) + * + * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies + * + * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -2812,7 +2839,8 @@ union bpf_attr { FN(strtoul), \ FN(sk_storage_get), \ FN(sk_storage_delete), \ - FN(send_signal), + FN(send_signal), \ + FN(tcp_gen_syncookie), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index ca1bd466b3c1..ff4e9d9f7278 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5811,6 +5811,75 @@ static const struct bpf_func_proto bpf_tcp_check_syncookie_proto = { .arg5_type = ARG_CONST_SIZE, }; +BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len, + struct tcphdr *, th, u32, th_len) +{ +#ifdef CONFIG_SYN_COOKIES + u32 cookie; + u16 mss; + + if (unlikely(th_len < sizeof(*th) || th_len != th->doff * 4)) + return -EINVAL; + + if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) + return -EINVAL; + + if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies) + return -ENOENT; + + if (!th->syn || th->ack || th->fin || th->rst) + return -EINVAL; + + if (unlikely(iph_len < sizeof(struct iphdr))) + return -EINVAL; + + /* Both struct iphdr and struct ipv6hdr have the version field at the + * same offset so we can cast to the shorter header (struct iphdr). + */ + switch (((struct iphdr *)iph)->version) { + case 4: + if (sk->sk_family == AF_INET6 && sk->sk_ipv6only) + return -EINVAL; + + mss = tcp_v4_get_syncookie(sk, iph, th, &cookie); + break; + +#if IS_BUILTIN(CONFIG_IPV6) + case 6: + if (unlikely(iph_len < sizeof(struct ipv6hdr))) + return -EINVAL; + + if (sk->sk_family != AF_INET6) + return -EINVAL; + + mss = tcp_v6_get_syncookie(sk, iph, th, &cookie); + break; +#endif /* CONFIG_IPV6 */ + + default: + return -EPROTONOSUPPORT; + } + if (mss <= 0) + return -ENOENT; + + return cookie | ((u64)mss << 32); +#else + return -EOPNOTSUPP; +#endif /* CONFIG_SYN_COOKIES */ +} + +static const struct bpf_func_proto bpf_tcp_gen_syncookie_proto = { + .func = bpf_tcp_gen_syncookie, + .gpl_only = true, /* __cookie_v*_init_sequence() is GPL */ + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_SOCK_COMMON, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_PTR_TO_MEM, + .arg5_type = ARG_CONST_SIZE, +}; + #endif /* CONFIG_INET */ bool bpf_helper_changes_pkt_data(void *func) @@ -6091,6 +6160,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_tcp_check_syncookie_proto; case BPF_FUNC_skb_ecn_set_ce: return &bpf_skb_ecn_set_ce_proto; + case BPF_FUNC_tcp_gen_syncookie: + return &bpf_tcp_gen_syncookie_proto; #endif default: return bpf_base_func_proto(func_id); @@ -6130,6 +6201,8 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_xdp_skc_lookup_tcp_proto; case BPF_FUNC_tcp_check_syncookie: return &bpf_tcp_check_syncookie_proto; + case BPF_FUNC_tcp_gen_syncookie: + return &bpf_tcp_gen_syncookie_proto; #endif default: return bpf_base_func_proto(func_id); -- Gitee From 31afd6b8a569117044a7542f61935c8d082df1a6 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Mon, 29 Jul 2019 09:59:16 -0700 Subject: [PATCH 05/43] bpf: sync bpf.h to tools/ ANBZ: #5530 commit 3745ee18017e36be34f6f2a81e802a20e54e5e8b upstream. Sync updated documentation for bpf_redirect_map. Sync the bpf_tcp_gen_syncookie helper function definition with the one in tools/uapi. Signed-off-by: Petar Penkov Reviewed-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- tools/include/uapi/linux/bpf.h | 37 +++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 178a40f8a298..d7217ff52ca0 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1572,8 +1572,11 @@ union bpf_attr { * but this is only implemented for native XDP (with driver * support) as of this writing). * - * All values for *flags* are reserved for future usage, and must - * be left at zero. + * The lower two bits of *flags* are used as the return code if + * the map lookup fails. This is so that the return value can be + * one of the XDP program return codes up to XDP_TX, as chosen by + * the caller. Any higher bits in the *flags* argument must be + * unset. * * When used to redirect packets to net devices, this helper * provides a high performance increase over **bpf_redirect**\ (). @@ -2698,6 +2701,33 @@ union bpf_attr { * **-EPERM** if no permission to send the *sig*. * * **-EAGAIN** if bpf program can try again. + * + * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header. + * + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** SYN cookie cannot be issued due to error + * + * **-ENOENT** SYN cookie should not be issued (no SYN flood) + * + * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies + * + * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -2809,7 +2839,8 @@ union bpf_attr { FN(strtoul), \ FN(sk_storage_get), \ FN(sk_storage_delete), \ - FN(send_signal), + FN(send_signal), \ + FN(tcp_gen_syncookie), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- Gitee From c4d8e9cfe2ca1e726de17943736c60bdb36a3239 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Mon, 29 Jul 2019 09:59:17 -0700 Subject: [PATCH 06/43] selftests/bpf: bpf_tcp_gen_syncookie->bpf_helpers ANBZ: #5530 commit 637f71c09ba22d1042f5b48b58c249ee5665d44d upstream. Expose bpf_tcp_gen_syncookie to selftests. Signed-off-by: Petar Penkov Reviewed-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- tools/testing/selftests/bpf/bpf_helpers.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 9eb803211fa2..7b93f3b636f1 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -216,6 +216,9 @@ static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk, static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) = (void *)BPF_FUNC_sk_storage_delete; static int (*bpf_send_signal)(unsigned sig) = (void *)BPF_FUNC_send_signal; +static long long (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *ip, + int ip_len, void *tcp, int tcp_len) = + (void *) BPF_FUNC_tcp_gen_syncookie; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions -- Gitee From 47c83db7b9180d5dbda172e385837b3aa3036dc4 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Mon, 29 Jul 2019 09:59:18 -0700 Subject: [PATCH 07/43] selftests/bpf: add test for bpf_tcp_gen_syncookie ANBZ: #5530 commit 91bc35789db4e1a489be7ab6e318e6265202e096 upstream. Modify the existing bpf_tcp_check_syncookie test to also generate a SYN cookie, pass the packet to the kernel, and verify that the two cookies are the same (and both valid). Since cloned SKBs are skipped during generic XDP, this test does not issue a SYN cookie when run in XDP mode. We therefore only check that a valid SYN cookie was issued at the TC hook. Additionally, verify that the MSS for that SYN cookie is within expected range. Signed-off-by: Petar Penkov Reviewed-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- .../bpf/progs/test_tcp_check_syncookie_kern.c | 48 +++++++++++++-- .../selftests/bpf/test_tcp_check_syncookie.sh | 3 + .../bpf/test_tcp_check_syncookie_user.c | 61 ++++++++++++++++--- 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c index 1ab095bcacd8..d8803dfa8d32 100644 --- a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c +++ b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c @@ -19,10 +19,29 @@ struct bpf_map_def SEC("maps") results = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(__u32), - .value_size = sizeof(__u64), - .max_entries = 1, + .value_size = sizeof(__u32), + .max_entries = 3, }; +static __always_inline __s64 gen_syncookie(void *data_end, struct bpf_sock *sk, + void *iph, __u32 ip_size, + struct tcphdr *tcph) +{ + __u32 thlen = tcph->doff * 4; + + if (tcph->syn && !tcph->ack) { + // packet should only have an MSS option + if (thlen != 24) + return 0; + + if ((void *)tcph + thlen > data_end) + return 0; + + return bpf_tcp_gen_syncookie(sk, iph, ip_size, tcph, thlen); + } + return 0; +} + static __always_inline void check_syncookie(void *ctx, void *data, void *data_end) { @@ -33,8 +52,10 @@ static __always_inline void check_syncookie(void *ctx, void *data, struct ipv6hdr *ipv6h; struct tcphdr *tcph; int ret; + __u32 key_mss = 2; + __u32 key_gen = 1; __u32 key = 0; - __u64 value = 1; + __s64 seq_mss; ethh = data; if (ethh + 1 > data_end) @@ -66,6 +87,9 @@ static __always_inline void check_syncookie(void *ctx, void *data, if (sk->state != BPF_TCP_LISTEN) goto release; + seq_mss = gen_syncookie(data_end, sk, ipv4h, sizeof(*ipv4h), + tcph); + ret = bpf_tcp_check_syncookie(sk, ipv4h, sizeof(*ipv4h), tcph, sizeof(*tcph)); break; @@ -95,6 +119,9 @@ static __always_inline void check_syncookie(void *ctx, void *data, if (sk->state != BPF_TCP_LISTEN) goto release; + seq_mss = gen_syncookie(data_end, sk, ipv6h, sizeof(*ipv6h), + tcph); + ret = bpf_tcp_check_syncookie(sk, ipv6h, sizeof(*ipv6h), tcph, sizeof(*tcph)); break; @@ -103,8 +130,19 @@ static __always_inline void check_syncookie(void *ctx, void *data, return; } - if (ret == 0) - bpf_map_update_elem(&results, &key, &value, 0); + if (seq_mss > 0) { + __u32 cookie = (__u32)seq_mss; + __u32 mss = seq_mss >> 32; + + bpf_map_update_elem(&results, &key_gen, &cookie, 0); + bpf_map_update_elem(&results, &key_mss, &mss, 0); + } + + if (ret == 0) { + __u32 cookie = bpf_ntohl(tcph->ack_seq) - 1; + + bpf_map_update_elem(&results, &key, &cookie, 0); + } release: bpf_sk_release(sk); diff --git a/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh b/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh index d48e51716d19..9b3617d770a5 100755 --- a/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh +++ b/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh @@ -37,6 +37,9 @@ setup() ns1_exec ip link set lo up ns1_exec sysctl -w net.ipv4.tcp_syncookies=2 + ns1_exec sysctl -w net.ipv4.tcp_window_scaling=0 + ns1_exec sysctl -w net.ipv4.tcp_timestamps=0 + ns1_exec sysctl -w net.ipv4.tcp_sack=0 wait_for_ip 127.0.0.1 wait_for_ip ::1 diff --git a/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c b/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c index 87829c86c746..b9e991d43155 100644 --- a/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c +++ b/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c @@ -2,6 +2,7 @@ // Copyright (c) 2018 Facebook // Copyright (c) 2019 Cloudflare +#include #include #include #include @@ -77,7 +78,7 @@ static int connect_to_server(int server_fd) return fd; } -static int get_map_fd_by_prog_id(int prog_id) +static int get_map_fd_by_prog_id(int prog_id, bool *xdp) { struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); @@ -104,6 +105,8 @@ static int get_map_fd_by_prog_id(int prog_id) goto err; } + *xdp = info.type == BPF_PROG_TYPE_XDP; + map_fd = bpf_map_get_fd_by_id(map_ids[0]); if (map_fd < 0) log_err("Failed to get fd by map id %d", map_ids[0]); @@ -113,18 +116,32 @@ static int get_map_fd_by_prog_id(int prog_id) return map_fd; } -static int run_test(int server_fd, int results_fd) +static int run_test(int server_fd, int results_fd, bool xdp) { int client = -1, srv_client = -1; int ret = 0; __u32 key = 0; - __u64 value = 0; + __u32 key_gen = 1; + __u32 key_mss = 2; + __u32 value = 0; + __u32 value_gen = 0; + __u32 value_mss = 0; if (bpf_map_update_elem(results_fd, &key, &value, 0) < 0) { log_err("Can't clear results"); goto err; } + if (bpf_map_update_elem(results_fd, &key_gen, &value_gen, 0) < 0) { + log_err("Can't clear results"); + goto err; + } + + if (bpf_map_update_elem(results_fd, &key_mss, &value_mss, 0) < 0) { + log_err("Can't clear results"); + goto err; + } + client = connect_to_server(server_fd); if (client == -1) goto err; @@ -140,8 +157,35 @@ static int run_test(int server_fd, int results_fd) goto err; } - if (value != 1) { - log_err("Didn't match syncookie: %llu", value); + if (value == 0) { + log_err("Didn't match syncookie: %u", value); + goto err; + } + + if (bpf_map_lookup_elem(results_fd, &key_gen, &value_gen) < 0) { + log_err("Can't lookup result"); + goto err; + } + + if (xdp && value_gen == 0) { + // SYN packets do not get passed through generic XDP, skip the + // rest of the test. + printf("Skipping XDP cookie check\n"); + goto out; + } + + if (bpf_map_lookup_elem(results_fd, &key_mss, &value_mss) < 0) { + log_err("Can't lookup result"); + goto err; + } + + if (value != value_gen) { + log_err("BPF generated cookie does not match kernel one"); + goto err; + } + + if (value_mss < 536 || value_mss > USHRT_MAX) { + log_err("Unexpected MSS retrieved"); goto err; } @@ -163,13 +207,14 @@ int main(int argc, char **argv) int server_v6 = -1; int results = -1; int err = 0; + bool xdp; if (argc < 2) { fprintf(stderr, "Usage: %s prog_id\n", argv[0]); exit(1); } - results = get_map_fd_by_prog_id(atoi(argv[1])); + results = get_map_fd_by_prog_id(atoi(argv[1]), &xdp); if (results < 0) { log_err("Can't get map"); goto err; @@ -194,10 +239,10 @@ int main(int argc, char **argv) if (server_v6 == -1) goto err; - if (run_test(server, results)) + if (run_test(server, results, xdp)) goto err; - if (run_test(server_v6, results)) + if (run_test(server_v6, results, xdp)) goto err; printf("ok\n"); -- Gitee From 34a645ab95dae8c30eb34adc94740a6732a278d2 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Tue, 27 Aug 2019 16:46:22 -0700 Subject: [PATCH 08/43] bpf: fix error check in bpf_tcp_gen_syncookie ANBZ: #5530 commit 0741be358d5adc266244f909e4434fe17fe1a109 upstream. If a SYN cookie is not issued by tcp_v#_gen_syncookie, then the return value will be exactly 0, rather than <= 0. Let's change the check to reflect that, especially since mss is an unsigned value and cannot be negative. Fixes: 70d66244317e ("bpf: add bpf_tcp_gen_syncookie helper") Reported-by: Stanislav Fomichev Signed-off-by: Petar Penkov Acked-by: Song Liu Signed-off-by: Daniel Borkmann Signed-off-by: Qiao Ma --- net/core/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index ff4e9d9f7278..d6fc0ea59ede 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -5859,7 +5859,7 @@ BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len, default: return -EPROTONOSUPPORT; } - if (mss <= 0) + if (mss == 0) return -ENOENT; return cookie | ((u64)mss << 32); -- Gitee From f5ab0a01796e653d82bdac75c808d24f6b9d6e9e Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Thu, 27 Jun 2019 18:12:33 -0700 Subject: [PATCH 09/43] selftests/bpf: fix -Wstrict-aliasing in test_sockopt_sk.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #5530 commit 2d6dbb9a65f4001f2878512078394c11301994f3 upstream. Let's use union with u8[4] and u32 members for sockopt buffer, that should fix any possible aliasing issues. test_sockopt_sk.c: In function ‘getsetsockopt’: test_sockopt_sk.c:115:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] if (*(__u32 *)buf != 0x55AA*2) { ^~ test_sockopt_sk.c:116:3: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2", ^~~~~~~ Fixes: 8a027dc0d8f5 ("selftests/bpf: add sockopt test that exercises sk helpers") Reported-by: Alexei Starovoitov Signed-off-by: Stanislav Fomichev Acked-by: Song Liu Signed-off-by: Daniel Borkmann Signed-off-by: Qiao Ma --- tools/testing/selftests/bpf/test_sockopt_sk.c | 51 +++++++++---------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/tools/testing/selftests/bpf/test_sockopt_sk.c b/tools/testing/selftests/bpf/test_sockopt_sk.c index 12e79ed075ce..036b652e5ca9 100644 --- a/tools/testing/selftests/bpf/test_sockopt_sk.c +++ b/tools/testing/selftests/bpf/test_sockopt_sk.c @@ -22,7 +22,10 @@ static int getsetsockopt(void) { int fd, err; - char buf[4] = {}; + union { + char u8[4]; + __u32 u32; + } buf = {}; socklen_t optlen; fd = socket(AF_INET, SOCK_STREAM, 0); @@ -33,31 +36,31 @@ static int getsetsockopt(void) /* IP_TOS - BPF bypass */ - buf[0] = 0x08; - err = setsockopt(fd, SOL_IP, IP_TOS, buf, 1); + buf.u8[0] = 0x08; + err = setsockopt(fd, SOL_IP, IP_TOS, &buf, 1); if (err) { log_err("Failed to call setsockopt(IP_TOS)"); goto err; } - buf[0] = 0x00; + buf.u8[0] = 0x00; optlen = 1; - err = getsockopt(fd, SOL_IP, IP_TOS, buf, &optlen); + err = getsockopt(fd, SOL_IP, IP_TOS, &buf, &optlen); if (err) { log_err("Failed to call getsockopt(IP_TOS)"); goto err; } - if (buf[0] != 0x08) { + if (buf.u8[0] != 0x08) { log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08", - buf[0]); + buf.u8[0]); goto err; } /* IP_TTL - EPERM */ - buf[0] = 1; - err = setsockopt(fd, SOL_IP, IP_TTL, buf, 1); + buf.u8[0] = 1; + err = setsockopt(fd, SOL_IP, IP_TTL, &buf, 1); if (!err || errno != EPERM) { log_err("Unexpected success from setsockopt(IP_TTL)"); goto err; @@ -65,16 +68,16 @@ static int getsetsockopt(void) /* SOL_CUSTOM - handled by BPF */ - buf[0] = 0x01; - err = setsockopt(fd, SOL_CUSTOM, 0, buf, 1); + buf.u8[0] = 0x01; + err = setsockopt(fd, SOL_CUSTOM, 0, &buf, 1); if (err) { log_err("Failed to call setsockopt"); goto err; } - buf[0] = 0x00; + buf.u32 = 0x00; optlen = 4; - err = getsockopt(fd, SOL_CUSTOM, 0, buf, &optlen); + err = getsockopt(fd, SOL_CUSTOM, 0, &buf, &optlen); if (err) { log_err("Failed to call getsockopt"); goto err; @@ -84,37 +87,31 @@ static int getsetsockopt(void) log_err("Unexpected optlen %d != 1", optlen); goto err; } - if (buf[0] != 0x01) { - log_err("Unexpected buf[0] 0x%02x != 0x01", buf[0]); + if (buf.u8[0] != 0x01) { + log_err("Unexpected buf[0] 0x%02x != 0x01", buf.u8[0]); goto err; } /* SO_SNDBUF is overwritten */ - buf[0] = 0x01; - buf[1] = 0x01; - buf[2] = 0x01; - buf[3] = 0x01; - err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, buf, 4); + buf.u32 = 0x01010101; + err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, 4); if (err) { log_err("Failed to call setsockopt(SO_SNDBUF)"); goto err; } - buf[0] = 0x00; - buf[1] = 0x00; - buf[2] = 0x00; - buf[3] = 0x00; + buf.u32 = 0x00; optlen = 4; - err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, buf, &optlen); + err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, &optlen); if (err) { log_err("Failed to call getsockopt(SO_SNDBUF)"); goto err; } - if (*(__u32 *)buf != 0x55AA*2) { + if (buf.u32 != 0x55AA*2) { log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2", - *(__u32 *)buf); + buf.u32); goto err; } -- Gitee From 54fd8324898dc81039f819c7a5f207f62c24af17 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 29 Jul 2019 14:51:10 -0700 Subject: [PATCH 10/43] bpf: always allocate at least 16 bytes for setsockopt hook ANBZ: #5530 commit 9babe825da769cfbd7080f95e6bddbe98ce6b95d upstream. Since we always allocate memory, allocate just a little bit more for the BPF program in case it need to override user input with bigger value. The canonical example is TCP_CONGESTION where input string might be too small to override (nv -> bbr or cubic). 16 bytes are chosen to match the size of TCP_CA_NAME_MAX and can be extended in the future if needed. Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- kernel/bpf/cgroup.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 122c67617583..435d33f46555 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -975,7 +975,6 @@ static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen) return -ENOMEM; ctx->optval_end = ctx->optval + max_optlen; - ctx->optlen = max_optlen; return 0; } @@ -995,7 +994,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, .level = *level, .optname = *optname, }; - int ret; + int ret, max_optlen; /* Opportunistic check to see whether we have any BPF program * attached to the hook so we don't waste time allocating @@ -1005,10 +1004,18 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, __cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_SETSOCKOPT)) return 0; - ret = sockopt_alloc_buf(&ctx, *optlen); + /* Allocate a bit more than the initial user buffer for + * BPF program. The canonical use case is overriding + * TCP_CONGESTION(nv) to TCP_CONGESTION(cubic). + */ + max_optlen = max_t(int, 16, *optlen); + + ret = sockopt_alloc_buf(&ctx, max_optlen); if (ret) return ret; + ctx.optlen = *optlen; + if (copy_from_user(ctx.optval, optval, *optlen) != 0) { ret = -EFAULT; goto out; @@ -1027,7 +1034,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, if (ctx.optlen == -1) { /* optlen set to -1, bypass kernel */ ret = 1; - } else if (ctx.optlen > *optlen || ctx.optlen < -1) { + } else if (ctx.optlen > max_optlen || ctx.optlen < -1) { /* optlen is out of bounds */ ret = -EFAULT; } else { @@ -1074,6 +1081,8 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, if (ret) return ret; + ctx.optlen = max_optlen; + if (!retval) { /* If kernel getsockopt finished successfully, * copy whatever was returned to the user back -- Gitee From 7f44ecf3b762629cceb2241c514bbd69540295f6 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 29 Jul 2019 14:51:11 -0700 Subject: [PATCH 11/43] selftests/bpf: extend sockopt_sk selftest with TCP_CONGESTION use case ANBZ: #5530 commit fd5ef31f370a8b7000794cd8a428b349dbfbbb80 upstream. Ignore SOL_TCP:TCP_CONGESTION in getsockopt and always override SOL_TCP:TCP_CONGESTION with "cubic" in setsockopt hook. Call setsockopt(SOL_TCP, TCP_CONGESTION) with short optval ("nv") to make sure BPF program has enough buffer space to replace it with "cubic". Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- .../testing/selftests/bpf/progs/sockopt_sk.c | 22 ++++++++++++++++ tools/testing/selftests/bpf/test_sockopt_sk.c | 25 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c index 076122c898e9..9a3d1c79e6fe 100644 --- a/tools/testing/selftests/bpf/progs/sockopt_sk.c +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include +#include #include #include "bpf_helpers.h" @@ -42,6 +44,14 @@ int _getsockopt(struct bpf_sockopt *ctx) return 1; } + if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) { + /* Not interested in SOL_TCP:TCP_CONGESTION; + * let next BPF program in the cgroup chain or kernel + * handle it. + */ + return 1; + } + if (ctx->level != SOL_CUSTOM) return 0; /* EPERM, deny everything except custom level */ @@ -91,6 +101,18 @@ int _setsockopt(struct bpf_sockopt *ctx) return 1; } + if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) { + /* Always use cubic */ + + if (optval + 5 > optval_end) + return 0; /* EPERM, bounds check */ + + memcpy(optval, "cubic", 5); + ctx->optlen = 5; + + return 1; + } + if (ctx->level != SOL_CUSTOM) return 0; /* EPERM, deny everything except custom level */ diff --git a/tools/testing/selftests/bpf/test_sockopt_sk.c b/tools/testing/selftests/bpf/test_sockopt_sk.c index 036b652e5ca9..e4f6055d92e9 100644 --- a/tools/testing/selftests/bpf/test_sockopt_sk.c +++ b/tools/testing/selftests/bpf/test_sockopt_sk.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,7 @@ static int getsetsockopt(void) union { char u8[4]; __u32 u32; + char cc[16]; /* TCP_CA_NAME_MAX */ } buf = {}; socklen_t optlen; @@ -115,6 +117,29 @@ static int getsetsockopt(void) goto err; } + /* TCP_CONGESTION can extend the string */ + + strcpy(buf.cc, "nv"); + err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv")); + if (err) { + log_err("Failed to call setsockopt(TCP_CONGESTION)"); + goto err; + } + + + optlen = sizeof(buf.cc); + err = getsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, &optlen); + if (err) { + log_err("Failed to call getsockopt(TCP_CONGESTION)"); + goto err; + } + + if (strcmp(buf.cc, "cubic") != 0) { + log_err("Unexpected getsockopt(TCP_CONGESTION) %s != %s", + buf.cc, "cubic"); + goto err; + } + close(fd); return 0; err: -- Gitee From 600d394f07dd8503a266f0a78accda707c2f4637 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 30 Jul 2019 14:03:00 -0700 Subject: [PATCH 12/43] tools: bpftool: add support for reporting the effective cgroup progs ANBZ: #5530 commit a98bf57391a24a68ec8381b9d35b60c2bee79150 upstream. Takshak said in the original submission: With different bpf attach_flags available to attach bpf programs specially with BPF_F_ALLOW_OVERRIDE and BPF_F_ALLOW_MULTI, the list of effective bpf-programs available to any sub-cgroups really needs to be available for easy debugging. Using BPF_F_QUERY_EFFECTIVE flag, one can get the list of not only attached bpf-programs to a cgroup but also the inherited ones from parent cgroup. So a new option is introduced to use BPF_F_QUERY_EFFECTIVE query flag here to list all the effective bpf-programs available for execution at a specified cgroup. Reused modified test program test_cgroup_attach from tools/testing/selftests/bpf: # ./test_cgroup_attach With old bpftool: # bpftool cgroup show /sys/fs/cgroup/cgroup-test-work-dir/cg1/ ID AttachType AttachFlags Name 271 egress multi pkt_cntr_1 272 egress multi pkt_cntr_2 Attached new program pkt_cntr_4 in cg2 gives following: # bpftool cgroup show /sys/fs/cgroup/cgroup-test-work-dir/cg1/cg2 ID AttachType AttachFlags Name 273 egress override pkt_cntr_4 And with new "effective" option it shows all effective programs for cg2: # bpftool cgroup show /sys/fs/cgroup/cgroup-test-work-dir/cg1/cg2 effective ID AttachType AttachFlags Name 273 egress override pkt_cntr_4 271 egress override pkt_cntr_1 272 egress override pkt_cntr_2 Compared to original submission use a local flag instead of global option. We need to clear query_flags on every command, in case batch mode wants to use varying settings. v2: (Takshak) - forbid duplicated flags; - fix cgroup path freeing. Signed-off-by: Takshak Chahande Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Reviewed-by: Takshak Chahande Signed-off-by: Alexei Starovoitov Signed-off-by: Qiao Ma --- .../bpftool/Documentation/bpftool-cgroup.rst | 16 +++- tools/bpf/bpftool/bash-completion/bpftool | 15 ++-- tools/bpf/bpftool/cgroup.c | 83 ++++++++++++------- 3 files changed, 76 insertions(+), 38 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst index 585f270c2d25..06a28b07787d 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst @@ -20,8 +20,8 @@ SYNOPSIS CGROUP COMMANDS =============== -| **bpftool** **cgroup { show | list }** *CGROUP* -| **bpftool** **cgroup tree** [*CGROUP_ROOT*] +| **bpftool** **cgroup { show | list }** *CGROUP* [**effective**] +| **bpftool** **cgroup tree** [*CGROUP_ROOT*] [**effective**] | **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*] | **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG* | **bpftool** **cgroup help** @@ -35,13 +35,17 @@ CGROUP COMMANDS DESCRIPTION =========== - **bpftool cgroup { show | list }** *CGROUP* + **bpftool cgroup { show | list }** *CGROUP* [**effective**] List all programs attached to the cgroup *CGROUP*. Output will start with program ID followed by attach type, attach flags and program name. - **bpftool cgroup tree** [*CGROUP_ROOT*] + If **effective** is specified retrieve effective programs that + will execute for events within a cgroup. This includes + inherited along with attached ones. + + **bpftool cgroup tree** [*CGROUP_ROOT*] [**effective**] Iterate over all cgroups in *CGROUP_ROOT* and list all attached programs. If *CGROUP_ROOT* is not specified, bpftool uses cgroup v2 mountpoint. @@ -50,6 +54,10 @@ DESCRIPTION commands: it starts with absolute cgroup path, followed by program ID, attach type, attach flags and program name. + If **effective** is specified retrieve effective programs that + will execute for events within a cgroup. This includes + inherited along with attached ones. + **bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*] Attach program *PROG* to the cgroup *CGROUP* with attach type *ATTACH_TYPE* and optional *ATTACH_FLAGS*. diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 6b961a5ed100..df16c5415444 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -710,12 +710,15 @@ _bpftool() ;; cgroup) case $command in - show|list) - _filedir - return 0 - ;; - tree) - _filedir + show|list|tree) + case $cword in + 3) + _filedir + ;; + 4) + COMPREPLY=( $( compgen -W 'effective' -- "$cur" ) ) + ;; + esac return 0 ;; attach|detach) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index 390b89a224f1..1a6b3caba1a7 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -29,6 +29,8 @@ " recvmsg4 | recvmsg6 | sysctl |\n" \ " getsockopt | setsockopt }" +static unsigned int query_flags; + static const char * const attach_type_strings[] = { [BPF_CGROUP_INET_INGRESS] = "ingress", [BPF_CGROUP_INET_EGRESS] = "egress", @@ -107,7 +109,8 @@ static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) __u32 prog_cnt = 0; int ret; - ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt); + ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL, + NULL, &prog_cnt); if (ret) return -1; @@ -125,8 +128,8 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, int ret; prog_cnt = ARRAY_SIZE(prog_ids); - ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids, - &prog_cnt); + ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags, + prog_ids, &prog_cnt); if (ret) return ret; @@ -158,20 +161,34 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, static int do_show(int argc, char **argv) { enum bpf_attach_type type; + const char *path; int cgroup_fd; int ret = -1; - if (argc < 1) { - p_err("too few parameters for cgroup show"); - goto exit; - } else if (argc > 1) { - p_err("too many parameters for cgroup show"); - goto exit; + query_flags = 0; + + if (!REQ_ARGS(1)) + return -1; + path = GET_ARG(); + + while (argc) { + if (is_prefix(*argv, "effective")) { + if (query_flags & BPF_F_QUERY_EFFECTIVE) { + p_err("duplicated argument: %s", *argv); + return -1; + } + query_flags |= BPF_F_QUERY_EFFECTIVE; + NEXT_ARG(); + } else { + p_err("expected no more arguments, 'effective', got: '%s'?", + *argv); + return -1; + } } - cgroup_fd = open(argv[0], O_RDONLY); + cgroup_fd = open(path, O_RDONLY); if (cgroup_fd < 0) { - p_err("can't open cgroup %s", argv[1]); + p_err("can't open cgroup %s", path); goto exit; } @@ -294,26 +311,37 @@ static char *find_cgroup_root(void) static int do_show_tree(int argc, char **argv) { - char *cgroup_root; + char *cgroup_root, *cgroup_alloced = NULL; int ret; - switch (argc) { - case 0: - cgroup_root = find_cgroup_root(); - if (!cgroup_root) { + query_flags = 0; + + if (!argc) { + cgroup_alloced = find_cgroup_root(); + if (!cgroup_alloced) { p_err("cgroup v2 isn't mounted"); return -1; } - break; - case 1: - cgroup_root = argv[0]; - break; - default: - p_err("too many parameters for cgroup tree"); - return -1; + cgroup_root = cgroup_alloced; + } else { + cgroup_root = GET_ARG(); + + while (argc) { + if (is_prefix(*argv, "effective")) { + if (query_flags & BPF_F_QUERY_EFFECTIVE) { + p_err("duplicated argument: %s", *argv); + return -1; + } + query_flags |= BPF_F_QUERY_EFFECTIVE; + NEXT_ARG(); + } else { + p_err("expected no more arguments, 'effective', got: '%s'?", + *argv); + return -1; + } + } } - if (json_output) jsonw_start_array(json_wtr); else @@ -338,8 +366,7 @@ static int do_show_tree(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); - if (argc == 0) - free(cgroup_root); + free(cgroup_alloced); return ret; } @@ -459,8 +486,8 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s %s { show | list } CGROUP\n" - " %s %s tree [CGROUP_ROOT]\n" + "Usage: %s %s { show | list } CGROUP [**effective**]\n" + " %s %s tree [CGROUP_ROOT] [**effective**]\n" " %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n" " %s %s detach CGROUP ATTACH_TYPE PROG\n" " %s %s help\n" -- Gitee From e7aabdf16a8e6e2969e629e0bf894d587ab508dc Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 16 Jun 2020 18:04:14 -0700 Subject: [PATCH 13/43] bpf: Don't return EINVAL from {get,set}sockopt when optlen > PAGE_SIZE ANBZ: #5530 commit d8fe449a9c51a37d844ab607e14e2f5c657d3cf2 upstream. Attaching to these hooks can break iptables because its optval is usually quite big, or at least bigger than the current PAGE_SIZE limit. David also mentioned some SCTP options can be big (around 256k). For such optvals we expose only the first PAGE_SIZE bytes to the BPF program. BPF program has two options: 1. Set ctx->optlen to 0 to indicate that the BPF's optval should be ignored and the kernel should use original userspace value. 2. Set ctx->optlen to something that's smaller than the PAGE_SIZE. v5: * use ctx->optlen == 0 with trimmed buffer (Alexei Starovoitov) * update the docs accordingly v4: * use temporary buffer to avoid optval == optval_end == NULL; this removes the corner case in the verifier that might assume non-zero PTR_TO_PACKET/PTR_TO_PACKET_END. v3: * don't increase the limit, bypass the argument v2: * proper comments formatting (Jakub Kicinski) Fixes: 0d01da6afc54 ("bpf: implement getsockopt and setsockopt hooks") Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov Cc: David Laight Link: https://lore.kernel.org/bpf/20200617010416.93086-1-sdf@google.com Signed-off-by: Qiao Ma --- kernel/bpf/cgroup.c | 53 ++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 435d33f46555..bc25f54ed9d9 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -967,16 +967,23 @@ static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp, static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen) { - if (unlikely(max_optlen > PAGE_SIZE) || max_optlen < 0) + if (unlikely(max_optlen < 0)) return -EINVAL; + if (unlikely(max_optlen > PAGE_SIZE)) { + /* We don't expose optvals that are greater than PAGE_SIZE + * to the BPF program. + */ + max_optlen = PAGE_SIZE; + } + ctx->optval = kzalloc(max_optlen, GFP_USER); if (!ctx->optval) return -ENOMEM; ctx->optval_end = ctx->optval + max_optlen; - return 0; + return max_optlen; } static void sockopt_free_buf(struct bpf_sockopt_kern *ctx) @@ -1010,13 +1017,13 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, */ max_optlen = max_t(int, 16, *optlen); - ret = sockopt_alloc_buf(&ctx, max_optlen); - if (ret) - return ret; + max_optlen = sockopt_alloc_buf(&ctx, max_optlen); + if (max_optlen < 0) + return max_optlen; ctx.optlen = *optlen; - if (copy_from_user(ctx.optval, optval, *optlen) != 0) { + if (copy_from_user(ctx.optval, optval, min(*optlen, max_optlen)) != 0) { ret = -EFAULT; goto out; } @@ -1044,8 +1051,14 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, /* export any potential modifications */ *level = ctx.level; *optname = ctx.optname; - *optlen = ctx.optlen; - *kernel_optval = ctx.optval; + + /* optlen == 0 from BPF indicates that we should + * use original userspace data. + */ + if (ctx.optlen != 0) { + *optlen = ctx.optlen; + *kernel_optval = ctx.optval; + } } out: @@ -1077,12 +1090,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, __cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_GETSOCKOPT)) return retval; - ret = sockopt_alloc_buf(&ctx, max_optlen); - if (ret) - return ret; - ctx.optlen = max_optlen; + max_optlen = sockopt_alloc_buf(&ctx, max_optlen); + if (max_optlen < 0) + return max_optlen; + if (!retval) { /* If kernel getsockopt finished successfully, * copy whatever was returned to the user back @@ -1096,10 +1109,8 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, goto out; } - if (ctx.optlen > max_optlen) - ctx.optlen = max_optlen; - - if (copy_from_user(ctx.optval, optval, ctx.optlen) != 0) { + if (copy_from_user(ctx.optval, optval, + min(ctx.optlen, max_optlen)) != 0) { ret = -EFAULT; goto out; } @@ -1128,10 +1139,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, goto out; } - if (copy_to_user(optval, ctx.optval, ctx.optlen) || - put_user(ctx.optlen, optlen)) { - ret = -EFAULT; - goto out; + if (ctx.optlen != 0) { + if (copy_to_user(optval, ctx.optval, ctx.optlen) || + put_user(ctx.optlen, optlen)) { + ret = -EFAULT; + goto out; + } } ret = ctx.retval; -- Gitee From b374cc1295486f69d8a6854cbcc6b39e05462bc9 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 16 Jun 2020 18:04:15 -0700 Subject: [PATCH 14/43] selftests/bpf: Make sure optvals > PAGE_SIZE are bypassed ANBZ: #5530 commit a0cb12b03132befbdb29d835ca330c18792e8134 upstream. We are relying on the fact, that we can pass > sizeof(int) optvals to the SOL_IP+IP_FREEBIND option (the kernel will take first 4 bytes). In the BPF program we check that we can only touch PAGE_SIZE bytes, but the real optlen is PAGE_SIZE * 2. In both cases, we override it to some predefined value and trim the optlen. Also, let's modify exiting IP_TOS usecase to test optlen=0 case where BPF program just bypasses the data as is. Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200617010416.93086-2-sdf@google.com Signed-off-by: Qiao Ma --- .../testing/selftests/bpf/progs/sockopt_sk.c | 54 ++++++++++++++++++- tools/testing/selftests/bpf/test_sockopt_sk.c | 46 +++++++++++++--- 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c index 9a3d1c79e6fe..205ab567e39b 100644 --- a/tools/testing/selftests/bpf/progs/sockopt_sk.c +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -8,6 +8,10 @@ char _license[] SEC("license") = "GPL"; __u32 _version SEC("version") = 1; +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + #define SOL_CUSTOM 0xdeadbeef struct sockopt_sk { @@ -29,12 +33,14 @@ int _getsockopt(struct bpf_sockopt *ctx) __u8 *optval = ctx->optval; struct sockopt_sk *storage; - if (ctx->level == SOL_IP && ctx->optname == IP_TOS) + if (ctx->level == SOL_IP && ctx->optname == IP_TOS) { /* Not interested in SOL_IP:IP_TOS; * let next BPF program in the cgroup chain or kernel * handle it. */ + ctx->optlen = 0; /* bypass optval>PAGE_SIZE */ return 1; + } if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) { /* Not interested in SOL_SOCKET:SO_SNDBUF; @@ -52,6 +58,26 @@ int _getsockopt(struct bpf_sockopt *ctx) return 1; } + if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { + if (optval + 1 > optval_end) + return 0; /* EPERM, bounds check */ + + ctx->retval = 0; /* Reset system call return value to zero */ + + /* Always export 0x55 */ + optval[0] = 0x55; + ctx->optlen = 1; + + /* Userspace buffer is PAGE_SIZE * 2, but BPF + * program can only see the first PAGE_SIZE + * bytes of data. + */ + if (optval_end - optval != PAGE_SIZE) + return 0; /* EPERM, unexpected data size */ + + return 1; + } + if (ctx->level != SOL_CUSTOM) return 0; /* EPERM, deny everything except custom level */ @@ -82,12 +108,14 @@ int _setsockopt(struct bpf_sockopt *ctx) __u8 *optval = ctx->optval; struct sockopt_sk *storage; - if (ctx->level == SOL_IP && ctx->optname == IP_TOS) + if (ctx->level == SOL_IP && ctx->optname == IP_TOS) { /* Not interested in SOL_IP:IP_TOS; * let next BPF program in the cgroup chain or kernel * handle it. */ + ctx->optlen = 0; /* bypass optval>PAGE_SIZE */ return 1; + } if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) { /* Overwrite SO_SNDBUF value */ @@ -113,6 +141,28 @@ int _setsockopt(struct bpf_sockopt *ctx) return 1; } + if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { + /* Original optlen is larger than PAGE_SIZE. */ + if (ctx->optlen != PAGE_SIZE * 2) + return 0; /* EPERM, unexpected data size */ + + if (optval + 1 > optval_end) + return 0; /* EPERM, bounds check */ + + /* Make sure we can trim the buffer. */ + optval[0] = 0; + ctx->optlen = 1; + + /* Usepace buffer is PAGE_SIZE * 2, but BPF + * program can only see the first PAGE_SIZE + * bytes of data. + */ + if (optval_end - optval != PAGE_SIZE) + return 0; /* EPERM, unexpected data size */ + + return 1; + } + if (ctx->level != SOL_CUSTOM) return 0; /* EPERM, deny everything except custom level */ diff --git a/tools/testing/selftests/bpf/test_sockopt_sk.c b/tools/testing/selftests/bpf/test_sockopt_sk.c index e4f6055d92e9..506c1135ba9c 100644 --- a/tools/testing/selftests/bpf/test_sockopt_sk.c +++ b/tools/testing/selftests/bpf/test_sockopt_sk.c @@ -29,6 +29,7 @@ static int getsetsockopt(void) char cc[16]; /* TCP_CA_NAME_MAX */ } buf = {}; socklen_t optlen; + char *big_buf = NULL; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { @@ -38,24 +39,31 @@ static int getsetsockopt(void) /* IP_TOS - BPF bypass */ - buf.u8[0] = 0x08; - err = setsockopt(fd, SOL_IP, IP_TOS, &buf, 1); + optlen = getpagesize() * 2; + big_buf = calloc(1, optlen); + if (!big_buf) { + log_err("Couldn't allocate two pages"); + goto err; + } + + *(int *)big_buf = 0x08; + err = setsockopt(fd, SOL_IP, IP_TOS, big_buf, optlen); if (err) { log_err("Failed to call setsockopt(IP_TOS)"); goto err; } - buf.u8[0] = 0x00; + memset(big_buf, 0, optlen); optlen = 1; - err = getsockopt(fd, SOL_IP, IP_TOS, &buf, &optlen); + err = getsockopt(fd, SOL_IP, IP_TOS, big_buf, &optlen); if (err) { log_err("Failed to call getsockopt(IP_TOS)"); goto err; } - if (buf.u8[0] != 0x08) { - log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08", - buf.u8[0]); + if (*(int *)big_buf != 0x08) { + log_err("Unexpected getsockopt(IP_TOS) optval 0x%x != 0x08", + *(int *)big_buf); goto err; } @@ -94,6 +102,28 @@ static int getsetsockopt(void) goto err; } + /* IP_FREEBIND - BPF can't access optval past PAGE_SIZE */ + + optlen = getpagesize() * 2; + memset(big_buf, 0, optlen); + + err = setsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, optlen); + if (err != 0) { + log_err("Failed to call setsockopt, ret=%d", err); + goto err; + } + + err = getsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, &optlen); + if (err != 0) { + log_err("Failed to call getsockopt, ret=%d", err); + goto err; + } + + if (optlen != 1 || *(__u8 *)big_buf != 0x55) { + log_err("Unexpected IP_FREEBIND getsockopt, optlen=%d, optval=0x%x", + optlen, *(__u8 *)big_buf); + } + /* SO_SNDBUF is overwritten */ buf.u32 = 0x01010101; @@ -140,9 +170,11 @@ static int getsetsockopt(void) goto err; } + free(big_buf); close(fd); return 0; err: + free(big_buf); close(fd); return -1; } -- Gitee From 51365a20d76da04e367e98f3f8c794ef2e1a6f10 Mon Sep 17 00:00:00 2001 From: Loris Reiff Date: Fri, 22 Jan 2021 17:42:31 +0100 Subject: [PATCH 15/43] bpf, cgroup: Fix optlen WARN_ON_ONCE toctou ANBZ: #5530 commit bb8b81e396f7afbe7c50d789e2107512274d2a35 upstream. A toctou issue in `__cgroup_bpf_run_filter_getsockopt` can trigger a WARN_ON_ONCE in a check of `copy_from_user`. `*optlen` is checked to be non-negative in the individual getsockopt functions beforehand. Changing `*optlen` in a race to a negative value will result in a `copy_from_user(ctx.optval, optval, ctx.optlen)` with `ctx.optlen` being a negative integer. Fixes: 0d01da6afc54 ("bpf: implement getsockopt and setsockopt hooks") Signed-off-by: Loris Reiff Signed-off-by: Daniel Borkmann Reviewed-by: Stanislav Fomichev Link: https://lore.kernel.org/bpf/20210122164232.61770-1-loris.reiff@liblor.ch Signed-off-by: Qiao Ma --- kernel/bpf/cgroup.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index bc25f54ed9d9..dea6abdf09c7 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1109,6 +1109,11 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, goto out; } + if (ctx.optlen < 0) { + ret = -EFAULT; + goto out; + } + if (copy_from_user(ctx.optval, optval, min(ctx.optlen, max_optlen)) != 0) { ret = -EFAULT; -- Gitee From 4a3e88830ab138e1988ee018d3ae25c87dfa2214 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 12 Jan 2021 08:28:29 -0800 Subject: [PATCH 16/43] bpf: Don't leak memory in bpf getsockopt when optlen == 0 ANBZ: #5530 commit 4be34f3d0731b38a1b24566b37fbb39500aaf3a2 upstream. optlen == 0 indicates that the kernel should ignore BPF buffer and use the original one from the user. We, however, forget to free the temporary buffer that we've allocated for BPF. Fixes: d8fe449a9c51 ("bpf: Don't return EINVAL from {get,set}sockopt when optlen > PAGE_SIZE") Reported-by: Martin KaFai Lau Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20210112162829.775079-1-sdf@google.com --- kernel/bpf/cgroup.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index dea6abdf09c7..c777bb2a97c1 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1058,12 +1058,13 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, if (ctx.optlen != 0) { *optlen = ctx.optlen; *kernel_optval = ctx.optval; + /* export and don't free sockopt buf */ + return 0; } } out: - if (ret) - sockopt_free_buf(&ctx); + sockopt_free_buf(&ctx); return ret; } EXPORT_SYMBOL(__cgroup_bpf_run_filter_setsockopt); -- Gitee From c653a1cb3b5bb07068dc3638c7cdb783dc90215c Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 15 Apr 2019 16:48:07 -0700 Subject: [PATCH 17/43] btf: add support for VAR and DATASEC in btf_dedup() ANBZ: #5530 commit 189cf5a4a7d5e0349f3079f05a8ccf4e4a2f0c3e upstream. This patch adds support for VAR and DATASEC in btf_dedup(). VAR/DATASEC are never deduplicated, but they need to be processed anyway as types they refer to might need to be remapped due to deduplication and compaction. Cc: Daniel Borkmann Cc: Yonghong Song Cc: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann --- tools/lib/bpf/btf.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 15f466f71c02..f9ee1bb271e4 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -1438,8 +1438,16 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext, } /* special BTF "void" type is made canonical immediately */ d->map[0] = 0; - for (i = 1; i <= btf->nr_types; i++) - d->map[i] = BTF_UNPROCESSED_ID; + for (i = 1; i <= btf->nr_types; i++) { + struct btf_type *t = d->btf->types[i]; + __u16 kind = BTF_INFO_KIND(t->info); + + /* VAR and DATASEC are never deduped and are self-canonical */ + if (kind == BTF_KIND_VAR || kind == BTF_KIND_DATASEC) + d->map[i] = i; + else + d->map[i] = BTF_UNPROCESSED_ID; + } d->hypot_map = malloc(sizeof(__u32) * (1 + btf->nr_types)); if (!d->hypot_map) { @@ -2031,6 +2039,8 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_UNION: case BTF_KIND_FUNC: case BTF_KIND_FUNC_PROTO: + case BTF_KIND_VAR: + case BTF_KIND_DATASEC: return 0; case BTF_KIND_INT: @@ -2793,6 +2803,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: + case BTF_KIND_VAR: r = btf_dedup_remap_type_id(d, t->type); if (r < 0) return r; @@ -2847,6 +2858,20 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) break; } + case BTF_KIND_DATASEC: { + struct btf_var_secinfo *var = (struct btf_var_secinfo *)(t + 1); + __u16 vlen = BTF_INFO_VLEN(t->info); + + for (i = 0; i < vlen; i++) { + r = btf_dedup_remap_type_id(d, var->type); + if (r < 0) + return r; + var->type = r; + var++; + } + break; + } + default: return -EINVAL; } -- Gitee From 13216d2726f86cf4144b3e43cc0a077e213a999b Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 15 Apr 2019 16:48:08 -0700 Subject: [PATCH 18/43] selftests/btf: add VAR and DATASEC case for dedup tests ANBZ: #5530 commit efb2ddc4ce5dba9b6c5ec106528d18a645424f3f upstream. Add test case verifying that dedup happens (INTs are deduped in this case) and VAR/DATASEC types are not deduped, but have their referenced type IDs adjusted correctly. Cc: Daniel Borkmann Cc: Yonghong Song Cc: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann --- tools/testing/selftests/bpf/test_btf.c | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c index 48de7746ed4d..67340bb30248 100644 --- a/tools/testing/selftests/bpf/test_btf.c +++ b/tools/testing/selftests/bpf/test_btf.c @@ -6561,6 +6561,51 @@ const struct btf_dedup_test dedup_tests[] = { .dont_resolve_fwds = false, }, }, +{ + .descr = "dedup: datasec and vars pass-through", + .input = { + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* static int t */ + BTF_VAR_ENC(NAME_NTH(2), 1, 0), /* [2] */ + /* .bss section */ /* [3] */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4), + BTF_VAR_SECINFO_ENC(2, 0, 4), + /* int, referenced from [5] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [4] */ + /* another static int t */ + BTF_VAR_ENC(NAME_NTH(2), 4, 0), /* [5] */ + /* another .bss section */ /* [6] */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4), + BTF_VAR_SECINFO_ENC(5, 0, 4), + BTF_END_RAW, + }, + BTF_STR_SEC("\0.bss\0t"), + }, + .expect = { + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* static int t */ + BTF_VAR_ENC(NAME_NTH(2), 1, 0), /* [2] */ + /* .bss section */ /* [3] */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4), + BTF_VAR_SECINFO_ENC(2, 0, 4), + /* another static int t */ + BTF_VAR_ENC(NAME_NTH(2), 1, 0), /* [4] */ + /* another .bss section */ /* [5] */ + BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4), + BTF_VAR_SECINFO_ENC(4, 0, 4), + BTF_END_RAW, + }, + BTF_STR_SEC("\0.bss\0t"), + }, + .opts = { + .dont_resolve_fwds = false, + .dedup_table_size = 1 + }, +}, }; @@ -6590,6 +6635,10 @@ static int btf_type_size(const struct btf_type *t) return base_size + vlen * sizeof(struct btf_member); case BTF_KIND_FUNC_PROTO: return base_size + vlen * sizeof(struct btf_param); + case BTF_KIND_VAR: + return base_size + sizeof(struct btf_var); + case BTF_KIND_DATASEC: + return base_size + vlen * sizeof(struct btf_var_secinfo); default: fprintf(stderr, "Unsupported BTF_KIND:%u\n", kind); return -EINVAL; -- Gitee From e4f16a3d8410c15b631375933bcb7e8b7e5903dd Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:48 -0700 Subject: [PATCH 19/43] libbpf: add helpers for working with BTF types ANBZ: #5530 commit ef20a9b27c66278ac2f85006db8ea11d5f61a781 upstream. Add lots of frequently used helpers that simplify working with BTF types. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/btf.h | 178 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 88a52ae56fc6..2604dc099855 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -5,6 +5,7 @@ #define __LIBBPF_BTF_H #include +#include #include #ifdef __cplusplus @@ -120,6 +121,183 @@ LIBBPF_API void btf_dump__free(struct btf_dump *d); LIBBPF_API int btf_dump__dump_type(struct btf_dump *d, __u32 id); +/* + * A set of helpers for easier BTF types handling + */ +static inline __u16 btf_kind(const struct btf_type *t) +{ + return BTF_INFO_KIND(t->info); +} + +static inline __u16 btf_vlen(const struct btf_type *t) +{ + return BTF_INFO_VLEN(t->info); +} + +static inline bool btf_kflag(const struct btf_type *t) +{ + return BTF_INFO_KFLAG(t->info); +} + +static inline bool btf_is_int(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_INT; +} + +static inline bool btf_is_ptr(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_PTR; +} + +static inline bool btf_is_array(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_ARRAY; +} + +static inline bool btf_is_struct(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_STRUCT; +} + +static inline bool btf_is_union(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_UNION; +} + +static inline bool btf_is_composite(const struct btf_type *t) +{ + __u16 kind = btf_kind(t); + + return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION; +} + +static inline bool btf_is_enum(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_ENUM; +} + +static inline bool btf_is_fwd(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_FWD; +} + +static inline bool btf_is_typedef(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_TYPEDEF; +} + +static inline bool btf_is_volatile(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_VOLATILE; +} + +static inline bool btf_is_const(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_CONST; +} + +static inline bool btf_is_restrict(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_RESTRICT; +} + +static inline bool btf_is_mod(const struct btf_type *t) +{ + __u16 kind = btf_kind(t); + + return kind == BTF_KIND_VOLATILE || + kind == BTF_KIND_CONST || + kind == BTF_KIND_RESTRICT; +} + +static inline bool btf_is_func(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_FUNC; +} + +static inline bool btf_is_func_proto(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_FUNC_PROTO; +} + +static inline bool btf_is_var(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_VAR; +} + +static inline bool btf_is_datasec(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_DATASEC; +} + +static inline __u8 btf_int_encoding(const struct btf_type *t) +{ + return BTF_INT_ENCODING(*(__u32 *)(t + 1)); +} + +static inline __u8 btf_int_offset(const struct btf_type *t) +{ + return BTF_INT_OFFSET(*(__u32 *)(t + 1)); +} + +static inline __u8 btf_int_bits(const struct btf_type *t) +{ + return BTF_INT_BITS(*(__u32 *)(t + 1)); +} + +static inline struct btf_array *btf_array(const struct btf_type *t) +{ + return (struct btf_array *)(t + 1); +} + +static inline struct btf_enum *btf_enum(const struct btf_type *t) +{ + return (struct btf_enum *)(t + 1); +} + +static inline struct btf_member *btf_members(const struct btf_type *t) +{ + return (struct btf_member *)(t + 1); +} + +/* Get bit offset of a member with specified index. */ +static inline __u32 btf_member_bit_offset(const struct btf_type *t, + __u32 member_idx) +{ + const struct btf_member *m = btf_members(t) + member_idx; + bool kflag = btf_kflag(t); + + return kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset; +} +/* + * Get bitfield size of a member, assuming t is BTF_KIND_STRUCT or + * BTF_KIND_UNION. If member is not a bitfield, zero is returned. + */ +static inline __u32 btf_member_bitfield_size(const struct btf_type *t, + __u32 member_idx) +{ + const struct btf_member *m = btf_members(t) + member_idx; + bool kflag = btf_kflag(t); + + return kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0; +} + +static inline struct btf_param *btf_params(const struct btf_type *t) +{ + return (struct btf_param *)(t + 1); +} + +static inline struct btf_var *btf_var(const struct btf_type *t) +{ + return (struct btf_var *)(t + 1); +} + +static inline struct btf_var_secinfo * +btf_var_secinfos(const struct btf_type *t) +{ + return (struct btf_var_secinfo *)(t + 1); +} + #ifdef __cplusplus } /* extern "C" */ #endif -- Gitee From 6c0695aa70e9728600353eda29fcf510e59f48f4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:49 -0700 Subject: [PATCH 20/43] libbpf: convert libbpf code to use new btf helpers ANBZ: #5530 commit b03bc6853c0e0c97da842434e8056f1b9d9a1f4a upstream. Simplify code by relying on newly added BTF helper functions. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/btf.c | 181 ++++++++++++++++++--------------------- tools/lib/bpf/btf_dump.c | 138 ++++++++++------------------- tools/lib/bpf/libbpf.c | 60 +++++++------ 3 files changed, 158 insertions(+), 221 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index f9ee1bb271e4..1e9f2a42896c 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -20,13 +20,6 @@ #define BTF_MAX_NR_TYPES 0x7fffffff #define BTF_MAX_STR_OFFSET 0x7fffffff -#define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \ - ((k) == BTF_KIND_VOLATILE) || \ - ((k) == BTF_KIND_CONST) || \ - ((k) == BTF_KIND_RESTRICT)) - -#define IS_VAR(k) ((k) == BTF_KIND_VAR) - static struct btf_type btf_void; struct btf { @@ -193,9 +186,9 @@ static int btf_parse_str_sec(struct btf *btf) static int btf_type_size(struct btf_type *t) { int base_size = sizeof(struct btf_type); - __u16 vlen = BTF_INFO_VLEN(t->info); + __u16 vlen = btf_vlen(t); - switch (BTF_INFO_KIND(t->info)) { + switch (btf_kind(t)) { case BTF_KIND_FWD: case BTF_KIND_CONST: case BTF_KIND_VOLATILE: @@ -220,7 +213,7 @@ static int btf_type_size(struct btf_type *t) case BTF_KIND_DATASEC: return base_size + vlen * sizeof(struct btf_var_secinfo); default: - pr_debug("Unsupported BTF_KIND:%u\n", BTF_INFO_KIND(t->info)); + pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t)); return -EINVAL; } } @@ -264,7 +257,7 @@ const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id) static bool btf_type_is_void(const struct btf_type *t) { - return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD; + return t == &btf_void || btf_is_fwd(t); } static bool btf_type_is_void_or_null(const struct btf_type *t) @@ -285,7 +278,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id) t = btf__type_by_id(btf, type_id); for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t); i++) { - switch (BTF_INFO_KIND(t->info)) { + switch (btf_kind(t)) { case BTF_KIND_INT: case BTF_KIND_STRUCT: case BTF_KIND_UNION: @@ -304,7 +297,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id) type_id = t->type; break; case BTF_KIND_ARRAY: - array = (const struct btf_array *)(t + 1); + array = btf_array(t); if (nelems && array->nelems > UINT32_MAX / nelems) return -E2BIG; nelems *= array->nelems; @@ -334,8 +327,7 @@ int btf__resolve_type(const struct btf *btf, __u32 type_id) t = btf__type_by_id(btf, type_id); while (depth < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t) && - (IS_MODIFIER(BTF_INFO_KIND(t->info)) || - IS_VAR(BTF_INFO_KIND(t->info)))) { + (btf_is_mod(t) || btf_is_typedef(t) || btf_is_var(t))) { type_id = t->type; t = btf__type_by_id(btf, type_id); depth++; @@ -554,11 +546,11 @@ static int compare_vsi_off(const void *_a, const void *_b) static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, struct btf_type *t) { - __u32 size = 0, off = 0, i, vars = BTF_INFO_VLEN(t->info); + __u32 size = 0, off = 0, i, vars = btf_vlen(t); const char *name = btf__name_by_offset(btf, t->name_off); const struct btf_type *t_var; struct btf_var_secinfo *vsi; - struct btf_var *var; + const struct btf_var *var; int ret; if (!name) { @@ -574,12 +566,11 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, t->size = size; - for (i = 0, vsi = (struct btf_var_secinfo *)(t + 1); - i < vars; i++, vsi++) { + for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) { t_var = btf__type_by_id(btf, vsi->type); - var = (struct btf_var *)(t_var + 1); + var = btf_var(t_var); - if (BTF_INFO_KIND(t_var->info) != BTF_KIND_VAR) { + if (!btf_is_var(t_var)) { pr_debug("Non-VAR type seen in section %s\n", name); return -EINVAL; } @@ -595,7 +586,8 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, ret = bpf_object__variable_offset(obj, name, &off); if (ret) { - pr_debug("No offset found in symbol table for VAR %s\n", name); + pr_debug("No offset found in symbol table for VAR %s\n", + name); return -ENOENT; } @@ -619,7 +611,7 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf) * is section size and global variable offset. We use * the info from the ELF itself for this purpose. */ - if (BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC) { + if (btf_is_datasec(t)) { err = btf_fixup_datasec(obj, btf, t); if (err) break; @@ -774,14 +766,13 @@ int btf__get_map_kv_tids(const struct btf *btf, const char *map_name, return -EINVAL; } - if (BTF_INFO_KIND(container_type->info) != BTF_KIND_STRUCT || - BTF_INFO_VLEN(container_type->info) < 2) { + if (!btf_is_struct(container_type) || btf_vlen(container_type) < 2) { pr_warning("map:%s container_name:%s is an invalid container struct\n", map_name, container_name); return -EINVAL; } - key = (struct btf_member *)(container_type + 1); + key = btf_members(container_type); value = key + 1; key_size = btf__resolve_size(btf, key->type); @@ -1440,10 +1431,9 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext, d->map[0] = 0; for (i = 1; i <= btf->nr_types; i++) { struct btf_type *t = d->btf->types[i]; - __u16 kind = BTF_INFO_KIND(t->info); /* VAR and DATASEC are never deduped and are self-canonical */ - if (kind == BTF_KIND_VAR || kind == BTF_KIND_DATASEC) + if (btf_is_var(t) || btf_is_datasec(t)) d->map[i] = i; else d->map[i] = BTF_UNPROCESSED_ID; @@ -1484,11 +1474,11 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx) if (r) return r; - switch (BTF_INFO_KIND(t->info)) { + switch (btf_kind(t)) { case BTF_KIND_STRUCT: case BTF_KIND_UNION: { - struct btf_member *m = (struct btf_member *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + struct btf_member *m = btf_members(t); + __u16 vlen = btf_vlen(t); for (j = 0; j < vlen; j++) { r = fn(&m->name_off, ctx); @@ -1499,8 +1489,8 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx) break; } case BTF_KIND_ENUM: { - struct btf_enum *m = (struct btf_enum *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + struct btf_enum *m = btf_enum(t); + __u16 vlen = btf_vlen(t); for (j = 0; j < vlen; j++) { r = fn(&m->name_off, ctx); @@ -1511,8 +1501,8 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx) break; } case BTF_KIND_FUNC_PROTO: { - struct btf_param *m = (struct btf_param *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + struct btf_param *m = btf_params(t); + __u16 vlen = btf_vlen(t); for (j = 0; j < vlen; j++) { r = fn(&m->name_off, ctx); @@ -1801,16 +1791,16 @@ static long btf_hash_enum(struct btf_type *t) /* Check structural equality of two ENUMs. */ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2) { - struct btf_enum *m1, *m2; + const struct btf_enum *m1, *m2; __u16 vlen; int i; if (!btf_equal_common(t1, t2)) return false; - vlen = BTF_INFO_VLEN(t1->info); - m1 = (struct btf_enum *)(t1 + 1); - m2 = (struct btf_enum *)(t2 + 1); + vlen = btf_vlen(t1); + m1 = btf_enum(t1); + m2 = btf_enum(t2); for (i = 0; i < vlen; i++) { if (m1->name_off != m2->name_off || m1->val != m2->val) return false; @@ -1822,8 +1812,7 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2) static inline bool btf_is_enum_fwd(struct btf_type *t) { - return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM && - BTF_INFO_VLEN(t->info) == 0; + return btf_is_enum(t) && btf_vlen(t) == 0; } static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2) @@ -1843,8 +1832,8 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2) */ static long btf_hash_struct(struct btf_type *t) { - struct btf_member *member = (struct btf_member *)(t + 1); - __u32 vlen = BTF_INFO_VLEN(t->info); + const struct btf_member *member = btf_members(t); + __u32 vlen = btf_vlen(t); long h = btf_hash_common(t); int i; @@ -1864,16 +1853,16 @@ static long btf_hash_struct(struct btf_type *t) */ static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2) { - struct btf_member *m1, *m2; + const struct btf_member *m1, *m2; __u16 vlen; int i; if (!btf_equal_common(t1, t2)) return false; - vlen = BTF_INFO_VLEN(t1->info); - m1 = (struct btf_member *)(t1 + 1); - m2 = (struct btf_member *)(t2 + 1); + vlen = btf_vlen(t1); + m1 = btf_members(t1); + m2 = btf_members(t2); for (i = 0; i < vlen; i++) { if (m1->name_off != m2->name_off || m1->offset != m2->offset) return false; @@ -1890,7 +1879,7 @@ static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2) */ static long btf_hash_array(struct btf_type *t) { - struct btf_array *info = (struct btf_array *)(t + 1); + const struct btf_array *info = btf_array(t); long h = btf_hash_common(t); h = hash_combine(h, info->type); @@ -1908,13 +1897,13 @@ static long btf_hash_array(struct btf_type *t) */ static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2) { - struct btf_array *info1, *info2; + const struct btf_array *info1, *info2; if (!btf_equal_common(t1, t2)) return false; - info1 = (struct btf_array *)(t1 + 1); - info2 = (struct btf_array *)(t2 + 1); + info1 = btf_array(t1); + info2 = btf_array(t2); return info1->type == info2->type && info1->index_type == info2->index_type && info1->nelems == info2->nelems; @@ -1927,14 +1916,10 @@ static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2) */ static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2) { - struct btf_array *info1, *info2; - if (!btf_equal_common(t1, t2)) return false; - info1 = (struct btf_array *)(t1 + 1); - info2 = (struct btf_array *)(t2 + 1); - return info1->nelems == info2->nelems; + return btf_array(t1)->nelems == btf_array(t2)->nelems; } /* @@ -1944,8 +1929,8 @@ static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2) */ static long btf_hash_fnproto(struct btf_type *t) { - struct btf_param *member = (struct btf_param *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + const struct btf_param *member = btf_params(t); + __u16 vlen = btf_vlen(t); long h = btf_hash_common(t); int i; @@ -1966,16 +1951,16 @@ static long btf_hash_fnproto(struct btf_type *t) */ static bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2) { - struct btf_param *m1, *m2; + const struct btf_param *m1, *m2; __u16 vlen; int i; if (!btf_equal_common(t1, t2)) return false; - vlen = BTF_INFO_VLEN(t1->info); - m1 = (struct btf_param *)(t1 + 1); - m2 = (struct btf_param *)(t2 + 1); + vlen = btf_vlen(t1); + m1 = btf_params(t1); + m2 = btf_params(t2); for (i = 0; i < vlen; i++) { if (m1->name_off != m2->name_off || m1->type != m2->type) return false; @@ -1992,7 +1977,7 @@ static bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2) */ static bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2) { - struct btf_param *m1, *m2; + const struct btf_param *m1, *m2; __u16 vlen; int i; @@ -2000,9 +1985,9 @@ static bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2) if (t1->name_off != t2->name_off || t1->info != t2->info) return false; - vlen = BTF_INFO_VLEN(t1->info); - m1 = (struct btf_param *)(t1 + 1); - m2 = (struct btf_param *)(t2 + 1); + vlen = btf_vlen(t1); + m1 = btf_params(t1); + m2 = btf_params(t2); for (i = 0; i < vlen; i++) { if (m1->name_off != m2->name_off) return false; @@ -2028,7 +2013,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) __u32 cand_id; long h; - switch (BTF_INFO_KIND(t->info)) { + switch (btf_kind(t)) { case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: @@ -2141,13 +2126,13 @@ static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id) { __u32 orig_type_id = type_id; - if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD) + if (!btf_is_fwd(d->btf->types[type_id])) return type_id; while (is_type_mapped(d, type_id) && d->map[type_id] != type_id) type_id = d->map[type_id]; - if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD) + if (!btf_is_fwd(d->btf->types[type_id])) return type_id; return orig_type_id; @@ -2156,7 +2141,7 @@ static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id) static inline __u16 btf_fwd_kind(struct btf_type *t) { - return BTF_INFO_KFLAG(t->info) ? BTF_KIND_UNION : BTF_KIND_STRUCT; + return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT; } /* @@ -2277,8 +2262,8 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, cand_type = d->btf->types[cand_id]; canon_type = d->btf->types[canon_id]; - cand_kind = BTF_INFO_KIND(cand_type->info); - canon_kind = BTF_INFO_KIND(canon_type->info); + cand_kind = btf_kind(cand_type); + canon_kind = btf_kind(canon_type); if (cand_type->name_off != canon_type->name_off) return 0; @@ -2327,12 +2312,12 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, return btf_dedup_is_equiv(d, cand_type->type, canon_type->type); case BTF_KIND_ARRAY: { - struct btf_array *cand_arr, *canon_arr; + const struct btf_array *cand_arr, *canon_arr; if (!btf_compat_array(cand_type, canon_type)) return 0; - cand_arr = (struct btf_array *)(cand_type + 1); - canon_arr = (struct btf_array *)(canon_type + 1); + cand_arr = btf_array(cand_type); + canon_arr = btf_array(canon_type); eq = btf_dedup_is_equiv(d, cand_arr->index_type, canon_arr->index_type); if (eq <= 0) @@ -2342,14 +2327,14 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, case BTF_KIND_STRUCT: case BTF_KIND_UNION: { - struct btf_member *cand_m, *canon_m; + const struct btf_member *cand_m, *canon_m; __u16 vlen; if (!btf_shallow_equal_struct(cand_type, canon_type)) return 0; - vlen = BTF_INFO_VLEN(cand_type->info); - cand_m = (struct btf_member *)(cand_type + 1); - canon_m = (struct btf_member *)(canon_type + 1); + vlen = btf_vlen(cand_type); + cand_m = btf_members(cand_type); + canon_m = btf_members(canon_type); for (i = 0; i < vlen; i++) { eq = btf_dedup_is_equiv(d, cand_m->type, canon_m->type); if (eq <= 0) @@ -2362,7 +2347,7 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, } case BTF_KIND_FUNC_PROTO: { - struct btf_param *cand_p, *canon_p; + const struct btf_param *cand_p, *canon_p; __u16 vlen; if (!btf_compat_fnproto(cand_type, canon_type)) @@ -2370,9 +2355,9 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, eq = btf_dedup_is_equiv(d, cand_type->type, canon_type->type); if (eq <= 0) return eq; - vlen = BTF_INFO_VLEN(cand_type->info); - cand_p = (struct btf_param *)(cand_type + 1); - canon_p = (struct btf_param *)(canon_type + 1); + vlen = btf_vlen(cand_type); + cand_p = btf_params(cand_type); + canon_p = btf_params(canon_type); for (i = 0; i < vlen; i++) { eq = btf_dedup_is_equiv(d, cand_p->type, canon_p->type); if (eq <= 0) @@ -2427,8 +2412,8 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d) targ_type_id = d->hypot_map[cand_type_id]; t_id = resolve_type_id(d, targ_type_id); c_id = resolve_type_id(d, cand_type_id); - t_kind = BTF_INFO_KIND(d->btf->types[t_id]->info); - c_kind = BTF_INFO_KIND(d->btf->types[c_id]->info); + t_kind = btf_kind(d->btf->types[t_id]); + c_kind = btf_kind(d->btf->types[c_id]); /* * Resolve FWD into STRUCT/UNION. * It's ok to resolve FWD into STRUCT/UNION that's not yet @@ -2497,7 +2482,7 @@ static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id) return 0; t = d->btf->types[type_id]; - kind = BTF_INFO_KIND(t->info); + kind = btf_kind(t); if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION) return 0; @@ -2592,7 +2577,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) t = d->btf->types[type_id]; d->map[type_id] = BTF_IN_PROGRESS_ID; - switch (BTF_INFO_KIND(t->info)) { + switch (btf_kind(t)) { case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: @@ -2616,7 +2601,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) break; case BTF_KIND_ARRAY: { - struct btf_array *info = (struct btf_array *)(t + 1); + struct btf_array *info = btf_array(t); ref_type_id = btf_dedup_ref_type(d, info->type); if (ref_type_id < 0) @@ -2650,8 +2635,8 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) return ref_type_id; t->type = ref_type_id; - vlen = BTF_INFO_VLEN(t->info); - param = (struct btf_param *)(t + 1); + vlen = btf_vlen(t); + param = btf_params(t); for (i = 0; i < vlen; i++) { ref_type_id = btf_dedup_ref_type(d, param->type); if (ref_type_id < 0) @@ -2791,7 +2776,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) struct btf_type *t = d->btf->types[type_id]; int i, r; - switch (BTF_INFO_KIND(t->info)) { + switch (btf_kind(t)) { case BTF_KIND_INT: case BTF_KIND_ENUM: break; @@ -2811,7 +2796,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) break; case BTF_KIND_ARRAY: { - struct btf_array *arr_info = (struct btf_array *)(t + 1); + struct btf_array *arr_info = btf_array(t); r = btf_dedup_remap_type_id(d, arr_info->type); if (r < 0) @@ -2826,8 +2811,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_STRUCT: case BTF_KIND_UNION: { - struct btf_member *member = (struct btf_member *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + struct btf_member *member = btf_members(t); + __u16 vlen = btf_vlen(t); for (i = 0; i < vlen; i++) { r = btf_dedup_remap_type_id(d, member->type); @@ -2840,8 +2825,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) } case BTF_KIND_FUNC_PROTO: { - struct btf_param *param = (struct btf_param *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + struct btf_param *param = btf_params(t); + __u16 vlen = btf_vlen(t); r = btf_dedup_remap_type_id(d, t->type); if (r < 0) @@ -2859,8 +2844,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id) } case BTF_KIND_DATASEC: { - struct btf_var_secinfo *var = (struct btf_var_secinfo *)(t + 1); - __u16 vlen = BTF_INFO_VLEN(t->info); + struct btf_var_secinfo *var = btf_var_secinfos(t); + __u16 vlen = btf_vlen(t); for (i = 0; i < vlen; i++) { r = btf_dedup_remap_type_id(d, var->type); diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 7065bb5b2752..715967762312 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -100,21 +100,6 @@ static bool str_equal_fn(const void *a, const void *b, void *ctx) return strcmp(a, b) == 0; } -static __u16 btf_kind_of(const struct btf_type *t) -{ - return BTF_INFO_KIND(t->info); -} - -static __u16 btf_vlen_of(const struct btf_type *t) -{ - return BTF_INFO_VLEN(t->info); -} - -static bool btf_kflag_of(const struct btf_type *t) -{ - return BTF_INFO_KFLAG(t->info); -} - static const char *btf_name_of(const struct btf_dump *d, __u32 name_off) { return btf__name_by_offset(d->btf, name_off); @@ -349,7 +334,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) */ struct btf_dump_type_aux_state *tstate = &d->type_states[id]; const struct btf_type *t; - __u16 kind, vlen; + __u16 vlen; int err, i; /* return true, letting typedefs know that it's ok to be emitted */ @@ -357,18 +342,16 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) return 1; t = btf__type_by_id(d->btf, id); - kind = btf_kind_of(t); if (tstate->order_state == ORDERING) { /* type loop, but resolvable through fwd declaration */ - if ((kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION) && - through_ptr && t->name_off != 0) + if (btf_is_composite(t) && through_ptr && t->name_off != 0) return 0; pr_warning("unsatisfiable type cycle, id:[%u]\n", id); return -ELOOP; } - switch (kind) { + switch (btf_kind(t)) { case BTF_KIND_INT: tstate->order_state = ORDERED; return 0; @@ -378,14 +361,12 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) tstate->order_state = ORDERED; return err; - case BTF_KIND_ARRAY: { - const struct btf_array *a = (void *)(t + 1); + case BTF_KIND_ARRAY: + return btf_dump_order_type(d, btf_array(t)->type, through_ptr); - return btf_dump_order_type(d, a->type, through_ptr); - } case BTF_KIND_STRUCT: case BTF_KIND_UNION: { - const struct btf_member *m = (void *)(t + 1); + const struct btf_member *m = btf_members(t); /* * struct/union is part of strong link, only if it's embedded * (so no ptr in a path) or it's anonymous (so has to be @@ -396,7 +377,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) tstate->order_state = ORDERING; - vlen = btf_vlen_of(t); + vlen = btf_vlen(t); for (i = 0; i < vlen; i++, m++) { err = btf_dump_order_type(d, m->type, false); if (err < 0) @@ -447,7 +428,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) return btf_dump_order_type(d, t->type, through_ptr); case BTF_KIND_FUNC_PROTO: { - const struct btf_param *p = (void *)(t + 1); + const struct btf_param *p = btf_params(t); bool is_strong; err = btf_dump_order_type(d, t->type, through_ptr); @@ -455,7 +436,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) return err; is_strong = err > 0; - vlen = btf_vlen_of(t); + vlen = btf_vlen(t); for (i = 0; i < vlen; i++, p++) { err = btf_dump_order_type(d, p->type, through_ptr); if (err < 0) @@ -553,7 +534,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) return; t = btf__type_by_id(d->btf, id); - kind = btf_kind_of(t); + kind = btf_kind(t); if (top_level_def && t->name_off == 0) { pr_warning("unexpected nameless definition, id:[%u]\n", id); @@ -618,12 +599,9 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) case BTF_KIND_RESTRICT: btf_dump_emit_type(d, t->type, cont_id); break; - case BTF_KIND_ARRAY: { - const struct btf_array *a = (void *)(t + 1); - - btf_dump_emit_type(d, a->type, cont_id); + case BTF_KIND_ARRAY: + btf_dump_emit_type(d, btf_array(t)->type, cont_id); break; - } case BTF_KIND_FWD: btf_dump_emit_fwd_def(d, id, t); btf_dump_printf(d, ";\n\n"); @@ -656,8 +634,8 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) * applicable */ if (top_level_def || t->name_off == 0) { - const struct btf_member *m = (void *)(t + 1); - __u16 vlen = btf_vlen_of(t); + const struct btf_member *m = btf_members(t); + __u16 vlen = btf_vlen(t); int i, new_cont_id; new_cont_id = t->name_off == 0 ? cont_id : id; @@ -678,8 +656,8 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) } break; case BTF_KIND_FUNC_PROTO: { - const struct btf_param *p = (void *)(t + 1); - __u16 vlen = btf_vlen_of(t); + const struct btf_param *p = btf_params(t); + __u16 vlen = btf_vlen(t); int i; btf_dump_emit_type(d, t->type, cont_id); @@ -696,7 +674,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) static int btf_align_of(const struct btf *btf, __u32 id) { const struct btf_type *t = btf__type_by_id(btf, id); - __u16 kind = btf_kind_of(t); + __u16 kind = btf_kind(t); switch (kind) { case BTF_KIND_INT: @@ -709,15 +687,12 @@ static int btf_align_of(const struct btf *btf, __u32 id) case BTF_KIND_CONST: case BTF_KIND_RESTRICT: return btf_align_of(btf, t->type); - case BTF_KIND_ARRAY: { - const struct btf_array *a = (void *)(t + 1); - - return btf_align_of(btf, a->type); - } + case BTF_KIND_ARRAY: + return btf_align_of(btf, btf_array(t)->type); case BTF_KIND_STRUCT: case BTF_KIND_UNION: { - const struct btf_member *m = (void *)(t + 1); - __u16 vlen = btf_vlen_of(t); + const struct btf_member *m = btf_members(t); + __u16 vlen = btf_vlen(t); int i, align = 1; for (i = 0; i < vlen; i++, m++) @@ -726,7 +701,7 @@ static int btf_align_of(const struct btf *btf, __u32 id) return align; } default: - pr_warning("unsupported BTF_KIND:%u\n", btf_kind_of(t)); + pr_warning("unsupported BTF_KIND:%u\n", btf_kind(t)); return 1; } } @@ -737,20 +712,18 @@ static bool btf_is_struct_packed(const struct btf *btf, __u32 id, const struct btf_member *m; int align, i, bit_sz; __u16 vlen; - bool kflag; align = btf_align_of(btf, id); /* size of a non-packed struct has to be a multiple of its alignment*/ if (t->size % align) return true; - m = (void *)(t + 1); - kflag = btf_kflag_of(t); - vlen = btf_vlen_of(t); + m = btf_members(t); + vlen = btf_vlen(t); /* all non-bitfield fields have to be naturally aligned */ for (i = 0; i < vlen; i++, m++) { align = btf_align_of(btf, m->type); - bit_sz = kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0; + bit_sz = btf_member_bitfield_size(t, i); if (bit_sz == 0 && m->offset % (8 * align) != 0) return true; } @@ -807,7 +780,7 @@ static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id, const struct btf_type *t) { btf_dump_printf(d, "%s %s", - btf_kind_of(t) == BTF_KIND_STRUCT ? "struct" : "union", + btf_is_struct(t) ? "struct" : "union", btf_dump_type_name(d, id)); } @@ -816,12 +789,11 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, const struct btf_type *t, int lvl) { - const struct btf_member *m = (void *)(t + 1); - bool kflag = btf_kflag_of(t), is_struct; + const struct btf_member *m = btf_members(t); + bool is_struct = btf_is_struct(t); int align, i, packed, off = 0; - __u16 vlen = btf_vlen_of(t); + __u16 vlen = btf_vlen(t); - is_struct = btf_kind_of(t) == BTF_KIND_STRUCT; packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0; align = packed ? 1 : btf_align_of(d->btf, id); @@ -835,8 +807,8 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, int m_off, m_sz; fname = btf_name_of(d, m->name_off); - m_sz = kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0; - m_off = kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset; + m_sz = btf_member_bitfield_size(t, i); + m_off = btf_member_bit_offset(t, i); align = packed ? 1 : btf_align_of(d->btf, m->type); btf_dump_emit_bit_padding(d, off, m_off, m_sz, align, lvl + 1); @@ -870,8 +842,8 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id, const struct btf_type *t, int lvl) { - const struct btf_enum *v = (void *)(t+1); - __u16 vlen = btf_vlen_of(t); + const struct btf_enum *v = btf_enum(t); + __u16 vlen = btf_vlen(t); const char *name; size_t dup_cnt; int i; @@ -905,7 +877,7 @@ static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id, { const char *name = btf_dump_type_name(d, id); - if (btf_kflag_of(t)) + if (btf_kflag(t)) btf_dump_printf(d, "union %s", name); else btf_dump_printf(d, "struct %s", name); @@ -987,7 +959,6 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, struct id_stack decl_stack; const struct btf_type *t; int err, stack_start; - __u16 kind; stack_start = d->decl_stack_cnt; for (;;) { @@ -1008,8 +979,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, break; t = btf__type_by_id(d->btf, id); - kind = btf_kind_of(t); - switch (kind) { + switch (btf_kind(t)) { case BTF_KIND_PTR: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: @@ -1017,12 +987,9 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, case BTF_KIND_FUNC_PROTO: id = t->type; break; - case BTF_KIND_ARRAY: { - const struct btf_array *a = (void *)(t + 1); - - id = a->type; + case BTF_KIND_ARRAY: + id = btf_array(t)->type; break; - } case BTF_KIND_INT: case BTF_KIND_ENUM: case BTF_KIND_FWD: @@ -1032,7 +999,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, goto done; default: pr_warning("unexpected type in decl chain, kind:%u, id:[%u]\n", - kind, id); + btf_kind(t), id); goto done; } } @@ -1070,7 +1037,7 @@ static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack) id = decl_stack->ids[decl_stack->cnt - 1]; t = btf__type_by_id(d->btf, id); - switch (btf_kind_of(t)) { + switch (btf_kind(t)) { case BTF_KIND_VOLATILE: btf_dump_printf(d, "volatile "); break; @@ -1087,20 +1054,6 @@ static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack) } } -static bool btf_is_mod_kind(const struct btf *btf, __u32 id) -{ - const struct btf_type *t = btf__type_by_id(btf, id); - - switch (btf_kind_of(t)) { - case BTF_KIND_VOLATILE: - case BTF_KIND_CONST: - case BTF_KIND_RESTRICT: - return true; - default: - return false; - } -} - static void btf_dump_emit_name(const struct btf_dump *d, const char *name, bool last_was_ptr) { @@ -1139,7 +1092,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, } t = btf__type_by_id(d->btf, id); - kind = btf_kind_of(t); + kind = btf_kind(t); switch (kind) { case BTF_KIND_INT: @@ -1185,7 +1138,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, btf_dump_printf(d, " restrict"); break; case BTF_KIND_ARRAY: { - const struct btf_array *a = (void *)(t + 1); + const struct btf_array *a = btf_array(t); const struct btf_type *next_t; __u32 next_id; bool multidim; @@ -1201,7 +1154,8 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, */ while (decls->cnt) { next_id = decls->ids[decls->cnt - 1]; - if (btf_is_mod_kind(d->btf, next_id)) + next_t = btf__type_by_id(d->btf, next_id); + if (btf_is_mod(next_t)) decls->cnt--; else break; @@ -1214,7 +1168,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, } next_t = btf__type_by_id(d->btf, next_id); - multidim = btf_kind_of(next_t) == BTF_KIND_ARRAY; + multidim = btf_is_array(next_t); /* we need space if we have named non-pointer */ if (fname[0] && !last_was_ptr) btf_dump_printf(d, " "); @@ -1228,8 +1182,8 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, return; } case BTF_KIND_FUNC_PROTO: { - const struct btf_param *p = (void *)(t + 1); - __u16 vlen = btf_vlen_of(t); + const struct btf_param *p = btf_params(t); + __u16 vlen = btf_vlen(t); int i; btf_dump_emit_mods(d, decls); diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 24e91117b9e9..a2d9f7489104 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1049,9 +1049,9 @@ static bool get_map_field_int(const char *map_name, const struct btf *btf, const struct btf_array *arr_info; const struct btf_type *arr_t; - if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) { + if (!btf_is_ptr(t)) { pr_warning("map '%s': attr '%s': expected PTR, got %u.\n", - map_name, name, BTF_INFO_KIND(t->info)); + map_name, name, btf_kind(t)); return false; } @@ -1061,12 +1061,12 @@ static bool get_map_field_int(const char *map_name, const struct btf *btf, map_name, name, t->type); return false; } - if (BTF_INFO_KIND(arr_t->info) != BTF_KIND_ARRAY) { + if (!btf_is_array(arr_t)) { pr_warning("map '%s': attr '%s': expected ARRAY, got %u.\n", - map_name, name, BTF_INFO_KIND(arr_t->info)); + map_name, name, btf_kind(arr_t)); return false; } - arr_info = (const void *)(arr_t + 1); + arr_info = btf_array(arr_t); *res = arr_info->nelems; return true; } @@ -1084,11 +1084,11 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, struct bpf_map *map; int vlen, i; - vi = (const struct btf_var_secinfo *)(const void *)(sec + 1) + var_idx; + vi = btf_var_secinfos(sec) + var_idx; var = btf__type_by_id(obj->btf, vi->type); - var_extra = (const void *)(var + 1); + var_extra = btf_var(var); map_name = btf__name_by_offset(obj->btf, var->name_off); - vlen = BTF_INFO_VLEN(var->info); + vlen = btf_vlen(var); if (map_name == NULL || map_name[0] == '\0') { pr_warning("map #%d: empty name.\n", var_idx); @@ -1098,9 +1098,9 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, pr_warning("map '%s' BTF data is corrupted.\n", map_name); return -EINVAL; } - if (BTF_INFO_KIND(var->info) != BTF_KIND_VAR) { + if (!btf_is_var(var)) { pr_warning("map '%s': unexpected var kind %u.\n", - map_name, BTF_INFO_KIND(var->info)); + map_name, btf_kind(var)); return -EINVAL; } if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED && @@ -1111,9 +1111,9 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, } def = skip_mods_and_typedefs(obj->btf, var->type); - if (BTF_INFO_KIND(def->info) != BTF_KIND_STRUCT) { + if (!btf_is_struct(def)) { pr_warning("map '%s': unexpected def kind %u.\n", - map_name, BTF_INFO_KIND(var->info)); + map_name, btf_kind(var)); return -EINVAL; } if (def->size > vi->size) { @@ -1136,8 +1136,8 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, pr_debug("map '%s': at sec_idx %d, offset %zu.\n", map_name, map->sec_idx, map->sec_offset); - vlen = BTF_INFO_VLEN(def->info); - m = (const void *)(def + 1); + vlen = btf_vlen(def); + m = btf_members(def); for (i = 0; i < vlen; i++, m++) { const char *name = btf__name_by_offset(obj->btf, m->name_off); @@ -1187,9 +1187,9 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, map_name, m->type); return -EINVAL; } - if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) { + if (!btf_is_ptr(t)) { pr_warning("map '%s': key spec is not PTR: %u.\n", - map_name, BTF_INFO_KIND(t->info)); + map_name, btf_kind(t)); return -EINVAL; } sz = btf__resolve_size(obj->btf, t->type); @@ -1230,9 +1230,9 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, map_name, m->type); return -EINVAL; } - if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) { + if (!btf_is_ptr(t)) { pr_warning("map '%s': value spec is not PTR: %u.\n", - map_name, BTF_INFO_KIND(t->info)); + map_name, btf_kind(t)); return -EINVAL; } sz = btf__resolve_size(obj->btf, t->type); @@ -1293,7 +1293,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict) nr_types = btf__get_nr_types(obj->btf); for (i = 1; i <= nr_types; i++) { t = btf__type_by_id(obj->btf, i); - if (BTF_INFO_KIND(t->info) != BTF_KIND_DATASEC) + if (!btf_is_datasec(t)) continue; name = btf__name_by_offset(obj->btf, t->name_off); if (strcmp(name, MAPS_ELF_SEC) == 0) { @@ -1307,7 +1307,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict) return -ENOENT; } - vlen = BTF_INFO_VLEN(sec->info); + vlen = btf_vlen(sec); for (i = 0; i < vlen; i++) { err = bpf_object__init_user_btf_map(obj, sec, i, obj->efile.btf_maps_shndx, @@ -1369,16 +1369,14 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj) struct btf *btf = obj->btf; struct btf_type *t; int i, j, vlen; - __u16 kind; if (!obj->btf || (has_func && has_datasec && has_func_global)) return; for (i = 1; i <= btf__get_nr_types(btf); i++) { t = (struct btf_type *)btf__type_by_id(btf, i); - kind = BTF_INFO_KIND(t->info); - if (!has_datasec && kind == BTF_KIND_VAR) { + if (!has_datasec && btf_is_var(t)) { /* replace VAR with INT */ t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0); /* @@ -1388,10 +1386,10 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj) */ t->size = 1; *(int *)(t+1) = BTF_INT_ENC(0, 0, 8); - } else if (!has_datasec && kind == BTF_KIND_DATASEC) { + } else if (!has_datasec && btf_is_datasec(t)) { /* replace DATASEC with STRUCT */ - struct btf_var_secinfo *v = (void *)(t + 1); - struct btf_member *m = (void *)(t + 1); + const struct btf_var_secinfo *v = btf_var_secinfos(t); + struct btf_member *m = btf_members(t); struct btf_type *vt; char *name; @@ -1402,7 +1400,7 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj) name++; } - vlen = BTF_INFO_VLEN(t->info); + vlen = btf_vlen(t); t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, vlen); for (j = 0; j < vlen; j++, v++, m++) { /* order of field assignments is important */ @@ -1412,15 +1410,15 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj) vt = (void *)btf__type_by_id(btf, v->type); m->name_off = vt->name_off; } - } else if (!has_func && kind == BTF_KIND_FUNC_PROTO) { + } else if (!has_func && btf_is_func_proto(t)) { /* replace FUNC_PROTO with ENUM */ - vlen = BTF_INFO_VLEN(t->info); + vlen = btf_vlen(t); t->info = BTF_INFO_ENC(BTF_KIND_ENUM, 0, vlen); t->size = sizeof(__u32); /* kernel enforced */ - } else if (!has_func && kind == BTF_KIND_FUNC) { + } else if (!has_func && btf_is_func(t)) { /* replace FUNC with TYPEDEF */ t->info = BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0); - } else if (!has_func_global && kind == BTF_KIND_FUNC) { + } else if (!has_func_global && btf_is_func(t)) { /* replace BTF_FUNC_GLOBAL with BTF_FUNC_STATIC */ t->info = BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0); } -- Gitee From 6d0030cc67fc9dd27129cfb6ccfdf4737f389986 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:50 -0700 Subject: [PATCH 21/43] libbpf: add .BTF.ext offset relocation section loading ANBZ: #5530 commit 4cedc0dad9b5bf55c4180c833be35e27e5d6cdbb upstream. Add support for BPF CO-RE offset relocations. Add section/record iteration macros for .BTF.ext. These macro are useful for iterating over each .BTF.ext record, either for dumping out contents or later for BPF CO-RE relocation handling. To enable other parts of libbpf to work with .BTF.ext contents, moved a bunch of type definitions into libbpf_internal.h. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/btf.c | 69 ++++++++------------- tools/lib/bpf/btf.h | 4 ++ tools/lib/bpf/libbpf_internal.h | 105 ++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 42 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 1e9f2a42896c..d606a358480d 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -36,47 +36,6 @@ struct btf { int fd; }; -struct btf_ext_info { - /* - * info points to the individual info section (e.g. func_info and - * line_info) from the .BTF.ext. It does not include the __u32 rec_size. - */ - void *info; - __u32 rec_size; - __u32 len; -}; - -struct btf_ext { - union { - struct btf_ext_header *hdr; - void *data; - }; - struct btf_ext_info func_info; - struct btf_ext_info line_info; - __u32 data_size; -}; - -struct btf_ext_info_sec { - __u32 sec_name_off; - __u32 num_info; - /* Followed by num_info * record_size number of bytes */ - __u8 data[0]; -}; - -/* The minimum bpf_func_info checked by the loader */ -struct bpf_func_info_min { - __u32 insn_off; - __u32 type_id; -}; - -/* The minimum bpf_line_info checked by the loader */ -struct bpf_line_info_min { - __u32 insn_off; - __u32 file_name_off; - __u32 line_off; - __u32 line_col; -}; - static inline __u64 ptr_to_u64(const void *ptr) { return (__u64) (unsigned long) ptr; @@ -822,6 +781,9 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, /* The start of the info sec (including the __u32 record_size). */ void *info; + if (ext_sec->len == 0) + return 0; + if (ext_sec->off & 0x03) { pr_debug(".BTF.ext %s section is not aligned to 4 bytes\n", ext_sec->desc); @@ -925,11 +887,24 @@ static int btf_ext_setup_line_info(struct btf_ext *btf_ext) return btf_ext_setup_info(btf_ext, ¶m); } +static int btf_ext_setup_offset_reloc(struct btf_ext *btf_ext) +{ + struct btf_ext_sec_setup_param param = { + .off = btf_ext->hdr->offset_reloc_off, + .len = btf_ext->hdr->offset_reloc_len, + .min_rec_size = sizeof(struct bpf_offset_reloc), + .ext_info = &btf_ext->offset_reloc_info, + .desc = "offset_reloc", + }; + + return btf_ext_setup_info(btf_ext, ¶m); +} + static int btf_ext_parse_hdr(__u8 *data, __u32 data_size) { const struct btf_ext_header *hdr = (struct btf_ext_header *)data; - if (data_size < offsetof(struct btf_ext_header, func_info_off) || + if (data_size < offsetofend(struct btf_ext_header, hdr_len) || data_size < hdr->hdr_len) { pr_debug("BTF.ext header not found"); return -EINVAL; @@ -987,6 +962,9 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size) } memcpy(btf_ext->data, data, size); + if (btf_ext->hdr->hdr_len < + offsetofend(struct btf_ext_header, line_info_len)) + goto done; err = btf_ext_setup_func_info(btf_ext); if (err) goto done; @@ -995,6 +973,13 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size) if (err) goto done; + if (btf_ext->hdr->hdr_len < + offsetofend(struct btf_ext_header, offset_reloc_len)) + goto done; + err = btf_ext_setup_offset_reloc(btf_ext); + if (err) + goto done; + done: if (err) { btf_ext__free(btf_ext); diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 2604dc099855..9cb44b4fbf60 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -58,6 +58,10 @@ struct btf_ext_header { __u32 func_info_len; __u32 line_info_off; __u32 line_info_len; + + /* optional part of .BTF.ext header */ + __u32 offset_reloc_off; + __u32 offset_reloc_len; }; LIBBPF_API void btf__free(struct btf *btf); diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index 86df37462914..475ce78ddc27 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -29,6 +29,10 @@ #ifndef max # define max(x, y) ((x) < (y) ? (y) : (x)) #endif +#ifndef offsetofend +# define offsetofend(TYPE, FIELD) \ + (offsetof(TYPE, FIELD) + sizeof(((TYPE *)0)->FIELD)) +#endif extern void libbpf_print(enum libbpf_print_level level, const char *format, ...) @@ -48,4 +52,105 @@ int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); int libbpf__load_raw_btf(const char *raw_types, size_t types_len, const char *str_sec, size_t str_len); +struct btf_ext_info { + /* + * info points to the individual info section (e.g. func_info and + * line_info) from the .BTF.ext. It does not include the __u32 rec_size. + */ + void *info; + __u32 rec_size; + __u32 len; +}; + +#define for_each_btf_ext_sec(seg, sec) \ + for (sec = (seg)->info; \ + (void *)sec < (seg)->info + (seg)->len; \ + sec = (void *)sec + sizeof(struct btf_ext_info_sec) + \ + (seg)->rec_size * sec->num_info) + +#define for_each_btf_ext_rec(seg, sec, i, rec) \ + for (i = 0, rec = (void *)&(sec)->data; \ + i < (sec)->num_info; \ + i++, rec = (void *)rec + (seg)->rec_size) + +struct btf_ext { + union { + struct btf_ext_header *hdr; + void *data; + }; + struct btf_ext_info func_info; + struct btf_ext_info line_info; + struct btf_ext_info offset_reloc_info; + __u32 data_size; +}; + +struct btf_ext_info_sec { + __u32 sec_name_off; + __u32 num_info; + /* Followed by num_info * record_size number of bytes */ + __u8 data[0]; +}; + +/* The minimum bpf_func_info checked by the loader */ +struct bpf_func_info_min { + __u32 insn_off; + __u32 type_id; +}; + +/* The minimum bpf_line_info checked by the loader */ +struct bpf_line_info_min { + __u32 insn_off; + __u32 file_name_off; + __u32 line_off; + __u32 line_col; +}; + +/* The minimum bpf_offset_reloc checked by the loader + * + * Offset relocation captures the following data: + * - insn_off - instruction offset (in bytes) within a BPF program that needs + * its insn->imm field to be relocated with actual offset; + * - type_id - BTF type ID of the "root" (containing) entity of a relocatable + * offset; + * - access_str_off - offset into corresponding .BTF string section. String + * itself encodes an accessed field using a sequence of field and array + * indicies, separated by colon (:). It's conceptually very close to LLVM's + * getelementptr ([0]) instruction's arguments for identifying offset to + * a field. + * + * Example to provide a better feel. + * + * struct sample { + * int a; + * struct { + * int b[10]; + * }; + * }; + * + * struct sample *s = ...; + * int x = &s->a; // encoded as "0:0" (a is field #0) + * int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1, + * // b is field #0 inside anon struct, accessing elem #5) + * int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array) + * + * type_id for all relocs in this example will capture BTF type id of + * `struct sample`. + * + * Such relocation is emitted when using __builtin_preserve_access_index() + * Clang built-in, passing expression that captures field address, e.g.: + * + * bpf_probe_read(&dst, sizeof(dst), + * __builtin_preserve_access_index(&src->a.b.c)); + * + * In this case Clang will emit offset relocation recording necessary data to + * be able to find offset of embedded `a.b.c` field within `src` struct. + * + * [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction + */ +struct bpf_offset_reloc { + __u32 insn_off; + __u32 type_id; + __u32 access_str_off; +}; + #endif /* __LIBBPF_LIBBPF_INTERNAL_H */ -- Gitee From 5b30881140a58572e8a9a3d0216357e599124601 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:51 -0700 Subject: [PATCH 22/43] libbpf: implement BPF CO-RE offset relocation algorithm ANBZ: #5530 commit ddc7c3042614e273044f698d2beab25cc3842d45 upstream. This patch implements the core logic for BPF CO-RE offsets relocations. Every instruction that needs to be relocated has corresponding bpf_offset_reloc as part of BTF.ext. Relocations are performed by trying to match recorded "local" relocation spec against potentially many compatible "target" types, creating corresponding spec. Details of the algorithm are noted in corresponding comments in the code. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- tools/lib/bpf/libbpf.c | 881 ++++++++++++++++++++++++++++++++++++++++- tools/lib/bpf/libbpf.h | 1 + 2 files changed, 864 insertions(+), 18 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a2d9f7489104..a83be6c939ac 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include "btf.h" #include "str_error.h" #include "libbpf_internal.h" +#include "hashmap.h" #ifndef EM_BPF #define EM_BPF 247 @@ -1015,23 +1017,21 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) return 0; } -static const struct btf_type *skip_mods_and_typedefs(const struct btf *btf, - __u32 id) +static const struct btf_type * +skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id) { const struct btf_type *t = btf__type_by_id(btf, id); - while (true) { - switch (BTF_INFO_KIND(t->info)) { - case BTF_KIND_VOLATILE: - case BTF_KIND_CONST: - case BTF_KIND_RESTRICT: - case BTF_KIND_TYPEDEF: - t = btf__type_by_id(btf, t->type); - break; - default: - return t; - } + if (res_id) + *res_id = id; + + while (btf_is_mod(t) || btf_is_typedef(t)) { + if (res_id) + *res_id = t->type; + t = btf__type_by_id(btf, t->type); } + + return t; } /* @@ -1044,7 +1044,7 @@ static const struct btf_type *skip_mods_and_typedefs(const struct btf *btf, static bool get_map_field_int(const char *map_name, const struct btf *btf, const struct btf_type *def, const struct btf_member *m, __u32 *res) { - const struct btf_type *t = skip_mods_and_typedefs(btf, m->type); + const struct btf_type *t = skip_mods_and_typedefs(btf, m->type, NULL); const char *name = btf__name_by_offset(btf, m->name_off); const struct btf_array *arr_info; const struct btf_type *arr_t; @@ -1110,7 +1110,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, return -EOPNOTSUPP; } - def = skip_mods_and_typedefs(obj->btf, var->type); + def = skip_mods_and_typedefs(obj->btf, var->type, NULL); if (!btf_is_struct(def)) { pr_warning("map '%s': unexpected def kind %u.\n", map_name, btf_kind(var)); @@ -2334,6 +2334,844 @@ bpf_program_reloc_btf_ext(struct bpf_program *prog, struct bpf_object *obj, return 0; } +#define BPF_CORE_SPEC_MAX_LEN 64 + +/* represents BPF CO-RE field or array element accessor */ +struct bpf_core_accessor { + __u32 type_id; /* struct/union type or array element type */ + __u32 idx; /* field index or array index */ + const char *name; /* field name or NULL for array accessor */ +}; + +struct bpf_core_spec { + const struct btf *btf; + /* high-level spec: named fields and array indices only */ + struct bpf_core_accessor spec[BPF_CORE_SPEC_MAX_LEN]; + /* high-level spec length */ + int len; + /* raw, low-level spec: 1-to-1 with accessor spec string */ + int raw_spec[BPF_CORE_SPEC_MAX_LEN]; + /* raw spec length */ + int raw_len; + /* field byte offset represented by spec */ + __u32 offset; +}; + +static bool str_is_empty(const char *s) +{ + return !s || !s[0]; +} + +/* + * Turn bpf_offset_reloc into a low- and high-level spec representation, + * validating correctness along the way, as well as calculating resulting + * field offset (in bytes), specified by accessor string. Low-level spec + * captures every single level of nestedness, including traversing anonymous + * struct/union members. High-level one only captures semantically meaningful + * "turning points": named fields and array indicies. + * E.g., for this case: + * + * struct sample { + * int __unimportant; + * struct { + * int __1; + * int __2; + * int a[7]; + * }; + * }; + * + * struct sample *s = ...; + * + * int x = &s->a[3]; // access string = '0:1:2:3' + * + * Low-level spec has 1:1 mapping with each element of access string (it's + * just a parsed access string representation): [0, 1, 2, 3]. + * + * High-level spec will capture only 3 points: + * - intial zero-index access by pointer (&s->... is the same as &s[0]...); + * - field 'a' access (corresponds to '2' in low-level spec); + * - array element #3 access (corresponds to '3' in low-level spec). + * + */ +static int bpf_core_spec_parse(const struct btf *btf, + __u32 type_id, + const char *spec_str, + struct bpf_core_spec *spec) +{ + int access_idx, parsed_len, i; + const struct btf_type *t; + const char *name; + __u32 id; + __s64 sz; + + if (str_is_empty(spec_str) || *spec_str == ':') + return -EINVAL; + + memset(spec, 0, sizeof(*spec)); + spec->btf = btf; + + /* parse spec_str="0:1:2:3:4" into array raw_spec=[0, 1, 2, 3, 4] */ + while (*spec_str) { + if (*spec_str == ':') + ++spec_str; + if (sscanf(spec_str, "%d%n", &access_idx, &parsed_len) != 1) + return -EINVAL; + if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN) + return -E2BIG; + spec_str += parsed_len; + spec->raw_spec[spec->raw_len++] = access_idx; + } + + if (spec->raw_len == 0) + return -EINVAL; + + /* first spec value is always reloc type array index */ + t = skip_mods_and_typedefs(btf, type_id, &id); + if (!t) + return -EINVAL; + + access_idx = spec->raw_spec[0]; + spec->spec[0].type_id = id; + spec->spec[0].idx = access_idx; + spec->len++; + + sz = btf__resolve_size(btf, id); + if (sz < 0) + return sz; + spec->offset = access_idx * sz; + + for (i = 1; i < spec->raw_len; i++) { + t = skip_mods_and_typedefs(btf, id, &id); + if (!t) + return -EINVAL; + + access_idx = spec->raw_spec[i]; + + if (btf_is_composite(t)) { + const struct btf_member *m; + __u32 offset; + + if (access_idx >= btf_vlen(t)) + return -EINVAL; + if (btf_member_bitfield_size(t, access_idx)) + return -EINVAL; + + offset = btf_member_bit_offset(t, access_idx); + if (offset % 8) + return -EINVAL; + spec->offset += offset / 8; + + m = btf_members(t) + access_idx; + if (m->name_off) { + name = btf__name_by_offset(btf, m->name_off); + if (str_is_empty(name)) + return -EINVAL; + + spec->spec[spec->len].type_id = id; + spec->spec[spec->len].idx = access_idx; + spec->spec[spec->len].name = name; + spec->len++; + } + + id = m->type; + } else if (btf_is_array(t)) { + const struct btf_array *a = btf_array(t); + + t = skip_mods_and_typedefs(btf, a->type, &id); + if (!t || access_idx >= a->nelems) + return -EINVAL; + + spec->spec[spec->len].type_id = id; + spec->spec[spec->len].idx = access_idx; + spec->len++; + + sz = btf__resolve_size(btf, id); + if (sz < 0) + return sz; + spec->offset += access_idx * sz; + } else { + pr_warning("relo for [%u] %s (at idx %d) captures type [%d] of unexpected kind %d\n", + type_id, spec_str, i, id, btf_kind(t)); + return -EINVAL; + } + } + + return 0; +} + +static bool bpf_core_is_flavor_sep(const char *s) +{ + /* check X___Y name pattern, where X and Y are not underscores */ + return s[0] != '_' && /* X */ + s[1] == '_' && s[2] == '_' && s[3] == '_' && /* ___ */ + s[4] != '_'; /* Y */ +} + +/* Given 'some_struct_name___with_flavor' return the length of a name prefix + * before last triple underscore. Struct name part after last triple + * underscore is ignored by BPF CO-RE relocation during relocation matching. + */ +static size_t bpf_core_essential_name_len(const char *name) +{ + size_t n = strlen(name); + int i; + + for (i = n - 5; i >= 0; i--) { + if (bpf_core_is_flavor_sep(name + i)) + return i + 1; + } + return n; +} + +/* dynamically sized list of type IDs */ +struct ids_vec { + __u32 *data; + int len; +}; + +static void bpf_core_free_cands(struct ids_vec *cand_ids) +{ + free(cand_ids->data); + free(cand_ids); +} + +static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf, + __u32 local_type_id, + const struct btf *targ_btf) +{ + size_t local_essent_len, targ_essent_len; + const char *local_name, *targ_name; + const struct btf_type *t; + struct ids_vec *cand_ids; + __u32 *new_ids; + int i, err, n; + + t = btf__type_by_id(local_btf, local_type_id); + if (!t) + return ERR_PTR(-EINVAL); + + local_name = btf__name_by_offset(local_btf, t->name_off); + if (str_is_empty(local_name)) + return ERR_PTR(-EINVAL); + local_essent_len = bpf_core_essential_name_len(local_name); + + cand_ids = calloc(1, sizeof(*cand_ids)); + if (!cand_ids) + return ERR_PTR(-ENOMEM); + + n = btf__get_nr_types(targ_btf); + for (i = 1; i <= n; i++) { + t = btf__type_by_id(targ_btf, i); + targ_name = btf__name_by_offset(targ_btf, t->name_off); + if (str_is_empty(targ_name)) + continue; + + targ_essent_len = bpf_core_essential_name_len(targ_name); + if (targ_essent_len != local_essent_len) + continue; + + if (strncmp(local_name, targ_name, local_essent_len) == 0) { + pr_debug("[%d] %s: found candidate [%d] %s\n", + local_type_id, local_name, i, targ_name); + new_ids = realloc(cand_ids->data, cand_ids->len + 1); + if (!new_ids) { + err = -ENOMEM; + goto err_out; + } + cand_ids->data = new_ids; + cand_ids->data[cand_ids->len++] = i; + } + } + return cand_ids; +err_out: + bpf_core_free_cands(cand_ids); + return ERR_PTR(err); +} + +/* Check two types for compatibility, skipping const/volatile/restrict and + * typedefs, to ensure we are relocating offset to the compatible entities: + * - any two STRUCTs/UNIONs are compatible and can be mixed; + * - any two FWDs are compatible; + * - any two PTRs are always compatible; + * - for ENUMs, check sizes, names are ignored; + * - for INT, size and bitness should match, signedness is ignored; + * - for ARRAY, dimensionality is ignored, element types are checked for + * compatibility recursively; + * - everything else shouldn't be ever a target of relocation. + * These rules are not set in stone and probably will be adjusted as we get + * more experience with using BPF CO-RE relocations. + */ +static int bpf_core_fields_are_compat(const struct btf *local_btf, + __u32 local_id, + const struct btf *targ_btf, + __u32 targ_id) +{ + const struct btf_type *local_type, *targ_type; + +recur: + local_type = skip_mods_and_typedefs(local_btf, local_id, &local_id); + targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id); + if (!local_type || !targ_type) + return -EINVAL; + + if (btf_is_composite(local_type) && btf_is_composite(targ_type)) + return 1; + if (btf_kind(local_type) != btf_kind(targ_type)) + return 0; + + switch (btf_kind(local_type)) { + case BTF_KIND_FWD: + case BTF_KIND_PTR: + return 1; + case BTF_KIND_ENUM: + return local_type->size == targ_type->size; + case BTF_KIND_INT: + return btf_int_offset(local_type) == 0 && + btf_int_offset(targ_type) == 0 && + local_type->size == targ_type->size && + btf_int_bits(local_type) == btf_int_bits(targ_type); + case BTF_KIND_ARRAY: + local_id = btf_array(local_type)->type; + targ_id = btf_array(targ_type)->type; + goto recur; + default: + pr_warning("unexpected kind %d relocated, local [%d], target [%d]\n", + btf_kind(local_type), local_id, targ_id); + return 0; + } +} + +/* + * Given single high-level named field accessor in local type, find + * corresponding high-level accessor for a target type. Along the way, + * maintain low-level spec for target as well. Also keep updating target + * offset. + * + * Searching is performed through recursive exhaustive enumeration of all + * fields of a struct/union. If there are any anonymous (embedded) + * structs/unions, they are recursively searched as well. If field with + * desired name is found, check compatibility between local and target types, + * before returning result. + * + * 1 is returned, if field is found. + * 0 is returned if no compatible field is found. + * <0 is returned on error. + */ +static int bpf_core_match_member(const struct btf *local_btf, + const struct bpf_core_accessor *local_acc, + const struct btf *targ_btf, + __u32 targ_id, + struct bpf_core_spec *spec, + __u32 *next_targ_id) +{ + const struct btf_type *local_type, *targ_type; + const struct btf_member *local_member, *m; + const char *local_name, *targ_name; + __u32 local_id; + int i, n, found; + + targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id); + if (!targ_type) + return -EINVAL; + if (!btf_is_composite(targ_type)) + return 0; + + local_id = local_acc->type_id; + local_type = btf__type_by_id(local_btf, local_id); + local_member = btf_members(local_type) + local_acc->idx; + local_name = btf__name_by_offset(local_btf, local_member->name_off); + + n = btf_vlen(targ_type); + m = btf_members(targ_type); + for (i = 0; i < n; i++, m++) { + __u32 offset; + + /* bitfield relocations not supported */ + if (btf_member_bitfield_size(targ_type, i)) + continue; + offset = btf_member_bit_offset(targ_type, i); + if (offset % 8) + continue; + + /* too deep struct/union/array nesting */ + if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN) + return -E2BIG; + + /* speculate this member will be the good one */ + spec->offset += offset / 8; + spec->raw_spec[spec->raw_len++] = i; + + targ_name = btf__name_by_offset(targ_btf, m->name_off); + if (str_is_empty(targ_name)) { + /* embedded struct/union, we need to go deeper */ + found = bpf_core_match_member(local_btf, local_acc, + targ_btf, m->type, + spec, next_targ_id); + if (found) /* either found or error */ + return found; + } else if (strcmp(local_name, targ_name) == 0) { + /* matching named field */ + struct bpf_core_accessor *targ_acc; + + targ_acc = &spec->spec[spec->len++]; + targ_acc->type_id = targ_id; + targ_acc->idx = i; + targ_acc->name = targ_name; + + *next_targ_id = m->type; + found = bpf_core_fields_are_compat(local_btf, + local_member->type, + targ_btf, m->type); + if (!found) + spec->len--; /* pop accessor */ + return found; + } + /* member turned out not to be what we looked for */ + spec->offset -= offset / 8; + spec->raw_len--; + } + + return 0; +} + +/* + * Try to match local spec to a target type and, if successful, produce full + * target spec (high-level, low-level + offset). + */ +static int bpf_core_spec_match(struct bpf_core_spec *local_spec, + const struct btf *targ_btf, __u32 targ_id, + struct bpf_core_spec *targ_spec) +{ + const struct btf_type *targ_type; + const struct bpf_core_accessor *local_acc; + struct bpf_core_accessor *targ_acc; + int i, sz, matched; + + memset(targ_spec, 0, sizeof(*targ_spec)); + targ_spec->btf = targ_btf; + + local_acc = &local_spec->spec[0]; + targ_acc = &targ_spec->spec[0]; + + for (i = 0; i < local_spec->len; i++, local_acc++, targ_acc++) { + targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, + &targ_id); + if (!targ_type) + return -EINVAL; + + if (local_acc->name) { + matched = bpf_core_match_member(local_spec->btf, + local_acc, + targ_btf, targ_id, + targ_spec, &targ_id); + if (matched <= 0) + return matched; + } else { + /* for i=0, targ_id is already treated as array element + * type (because it's the original struct), for others + * we should find array element type first + */ + if (i > 0) { + const struct btf_array *a; + + if (!btf_is_array(targ_type)) + return 0; + + a = btf_array(targ_type); + if (local_acc->idx >= a->nelems) + return 0; + if (!skip_mods_and_typedefs(targ_btf, a->type, + &targ_id)) + return -EINVAL; + } + + /* too deep struct/union/array nesting */ + if (targ_spec->raw_len == BPF_CORE_SPEC_MAX_LEN) + return -E2BIG; + + targ_acc->type_id = targ_id; + targ_acc->idx = local_acc->idx; + targ_acc->name = NULL; + targ_spec->len++; + targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx; + targ_spec->raw_len++; + + sz = btf__resolve_size(targ_btf, targ_id); + if (sz < 0) + return sz; + targ_spec->offset += local_acc->idx * sz; + } + } + + return 1; +} + +/* + * Patch relocatable BPF instruction. + * Expected insn->imm value is provided for validation, as well as the new + * relocated value. + * + * Currently three kinds of BPF instructions are supported: + * 1. rX = (assignment with immediate operand); + * 2. rX += (arithmetic operations with immediate operand); + * 3. *(rX) = (indirect memory assignment with immediate operand). + * + * If actual insn->imm value is wrong, bail out. + */ +static int bpf_core_reloc_insn(struct bpf_program *prog, int insn_off, + __u32 orig_off, __u32 new_off) +{ + struct bpf_insn *insn; + int insn_idx; + __u8 class; + + if (insn_off % sizeof(struct bpf_insn)) + return -EINVAL; + insn_idx = insn_off / sizeof(struct bpf_insn); + + insn = &prog->insns[insn_idx]; + class = BPF_CLASS(insn->code); + + if (class == BPF_ALU || class == BPF_ALU64) { + if (BPF_SRC(insn->code) != BPF_K) + return -EINVAL; + if (insn->imm != orig_off) + return -EINVAL; + insn->imm = new_off; + pr_debug("prog '%s': patched insn #%d (ALU/ALU64) imm %d -> %d\n", + bpf_program__title(prog, false), + insn_idx, orig_off, new_off); + } else { + pr_warning("prog '%s': trying to relocate unrecognized insn #%d, code:%x, src:%x, dst:%x, off:%x, imm:%x\n", + bpf_program__title(prog, false), + insn_idx, insn->code, insn->src_reg, insn->dst_reg, + insn->off, insn->imm); + return -EINVAL; + } + return 0; +} + +/* + * Probe few well-known locations for vmlinux kernel image and try to load BTF + * data out of it to use for target BTF. + */ +static struct btf *bpf_core_find_kernel_btf(void) +{ + const char *locations[] = { + "/lib/modules/%1$s/vmlinux-%1$s", + "/usr/lib/modules/%1$s/kernel/vmlinux", + }; + char path[PATH_MAX + 1]; + struct utsname buf; + struct btf *btf; + int i; + + uname(&buf); + + for (i = 0; i < ARRAY_SIZE(locations); i++) { + snprintf(path, PATH_MAX, locations[i], buf.release); + + if (access(path, R_OK)) + continue; + + btf = btf__parse_elf(path, NULL); + pr_debug("kernel BTF load from '%s': %ld\n", + path, PTR_ERR(btf)); + if (IS_ERR(btf)) + continue; + + return btf; + } + + pr_warning("failed to find valid kernel BTF\n"); + return ERR_PTR(-ESRCH); +} + +/* Output spec definition in the format: + * [] () + => @, + * where is a C-syntax view of recorded field access, e.g.: x.a[3].b + */ +static void bpf_core_dump_spec(int level, const struct bpf_core_spec *spec) +{ + const struct btf_type *t; + const char *s; + __u32 type_id; + int i; + + type_id = spec->spec[0].type_id; + t = btf__type_by_id(spec->btf, type_id); + s = btf__name_by_offset(spec->btf, t->name_off); + libbpf_print(level, "[%u] %s + ", type_id, s); + + for (i = 0; i < spec->raw_len; i++) + libbpf_print(level, "%d%s", spec->raw_spec[i], + i == spec->raw_len - 1 ? " => " : ":"); + + libbpf_print(level, "%u @ &x", spec->offset); + + for (i = 0; i < spec->len; i++) { + if (spec->spec[i].name) + libbpf_print(level, ".%s", spec->spec[i].name); + else + libbpf_print(level, "[%u]", spec->spec[i].idx); + } + +} + +static size_t bpf_core_hash_fn(const void *key, void *ctx) +{ + return (size_t)key; +} + +static bool bpf_core_equal_fn(const void *k1, const void *k2, void *ctx) +{ + return k1 == k2; +} + +static void *u32_as_hash_key(__u32 x) +{ + return (void *)(uintptr_t)x; +} + +/* + * CO-RE relocate single instruction. + * + * The outline and important points of the algorithm: + * 1. For given local type, find corresponding candidate target types. + * Candidate type is a type with the same "essential" name, ignoring + * everything after last triple underscore (___). E.g., `sample`, + * `sample___flavor_one`, `sample___flavor_another_one`, are all candidates + * for each other. Names with triple underscore are referred to as + * "flavors" and are useful, among other things, to allow to + * specify/support incompatible variations of the same kernel struct, which + * might differ between different kernel versions and/or build + * configurations. + * + * N.B. Struct "flavors" could be generated by bpftool's BTF-to-C + * converter, when deduplicated BTF of a kernel still contains more than + * one different types with the same name. In that case, ___2, ___3, etc + * are appended starting from second name conflict. But start flavors are + * also useful to be defined "locally", in BPF program, to extract same + * data from incompatible changes between different kernel + * versions/configurations. For instance, to handle field renames between + * kernel versions, one can use two flavors of the struct name with the + * same common name and use conditional relocations to extract that field, + * depending on target kernel version. + * 2. For each candidate type, try to match local specification to this + * candidate target type. Matching involves finding corresponding + * high-level spec accessors, meaning that all named fields should match, + * as well as all array accesses should be within the actual bounds. Also, + * types should be compatible (see bpf_core_fields_are_compat for details). + * 3. It is supported and expected that there might be multiple flavors + * matching the spec. As long as all the specs resolve to the same set of + * offsets across all candidates, there is not error. If there is any + * ambiguity, CO-RE relocation will fail. This is necessary to accomodate + * imprefection of BTF deduplication, which can cause slight duplication of + * the same BTF type, if some directly or indirectly referenced (by + * pointer) type gets resolved to different actual types in different + * object files. If such situation occurs, deduplicated BTF will end up + * with two (or more) structurally identical types, which differ only in + * types they refer to through pointer. This should be OK in most cases and + * is not an error. + * 4. Candidate types search is performed by linearly scanning through all + * types in target BTF. It is anticipated that this is overall more + * efficient memory-wise and not significantly worse (if not better) + * CPU-wise compared to prebuilding a map from all local type names to + * a list of candidate type names. It's also sped up by caching resolved + * list of matching candidates per each local "root" type ID, that has at + * least one bpf_offset_reloc associated with it. This list is shared + * between multiple relocations for the same type ID and is updated as some + * of the candidates are pruned due to structural incompatibility. + */ +static int bpf_core_reloc_offset(struct bpf_program *prog, + const struct bpf_offset_reloc *relo, + int relo_idx, + const struct btf *local_btf, + const struct btf *targ_btf, + struct hashmap *cand_cache) +{ + const char *prog_name = bpf_program__title(prog, false); + struct bpf_core_spec local_spec, cand_spec, targ_spec; + const void *type_key = u32_as_hash_key(relo->type_id); + const struct btf_type *local_type, *cand_type; + const char *local_name, *cand_name; + struct ids_vec *cand_ids; + __u32 local_id, cand_id; + const char *spec_str; + int i, j, err; + + local_id = relo->type_id; + local_type = btf__type_by_id(local_btf, local_id); + if (!local_type) + return -EINVAL; + + local_name = btf__name_by_offset(local_btf, local_type->name_off); + if (str_is_empty(local_name)) + return -EINVAL; + + spec_str = btf__name_by_offset(local_btf, relo->access_str_off); + if (str_is_empty(spec_str)) + return -EINVAL; + + err = bpf_core_spec_parse(local_btf, local_id, spec_str, &local_spec); + if (err) { + pr_warning("prog '%s': relo #%d: parsing [%d] %s + %s failed: %d\n", + prog_name, relo_idx, local_id, local_name, spec_str, + err); + return -EINVAL; + } + + pr_debug("prog '%s': relo #%d: spec is ", prog_name, relo_idx); + bpf_core_dump_spec(LIBBPF_DEBUG, &local_spec); + libbpf_print(LIBBPF_DEBUG, "\n"); + + if (!hashmap__find(cand_cache, type_key, (void **)&cand_ids)) { + cand_ids = bpf_core_find_cands(local_btf, local_id, targ_btf); + if (IS_ERR(cand_ids)) { + pr_warning("prog '%s': relo #%d: target candidate search failed for [%d] %s: %ld", + prog_name, relo_idx, local_id, local_name, + PTR_ERR(cand_ids)); + return PTR_ERR(cand_ids); + } + err = hashmap__set(cand_cache, type_key, cand_ids, NULL, NULL); + if (err) { + bpf_core_free_cands(cand_ids); + return err; + } + } + + for (i = 0, j = 0; i < cand_ids->len; i++) { + cand_id = cand_ids->data[i]; + cand_type = btf__type_by_id(targ_btf, cand_id); + cand_name = btf__name_by_offset(targ_btf, cand_type->name_off); + + err = bpf_core_spec_match(&local_spec, targ_btf, + cand_id, &cand_spec); + pr_debug("prog '%s': relo #%d: matching candidate #%d %s against spec ", + prog_name, relo_idx, i, cand_name); + bpf_core_dump_spec(LIBBPF_DEBUG, &cand_spec); + libbpf_print(LIBBPF_DEBUG, ": %d\n", err); + if (err < 0) { + pr_warning("prog '%s': relo #%d: matching error: %d\n", + prog_name, relo_idx, err); + return err; + } + if (err == 0) + continue; + + if (j == 0) { + targ_spec = cand_spec; + } else if (cand_spec.offset != targ_spec.offset) { + /* if there are many candidates, they should all + * resolve to the same offset + */ + pr_warning("prog '%s': relo #%d: offset ambiguity: %u != %u\n", + prog_name, relo_idx, cand_spec.offset, + targ_spec.offset); + return -EINVAL; + } + + cand_ids->data[j++] = cand_spec.spec[0].type_id; + } + + cand_ids->len = j; + if (cand_ids->len == 0) { + pr_warning("prog '%s': relo #%d: no matching targets found for [%d] %s + %s\n", + prog_name, relo_idx, local_id, local_name, spec_str); + return -ESRCH; + } + + err = bpf_core_reloc_insn(prog, relo->insn_off, + local_spec.offset, targ_spec.offset); + if (err) { + pr_warning("prog '%s': relo #%d: failed to patch insn at offset %d: %d\n", + prog_name, relo_idx, relo->insn_off, err); + return -EINVAL; + } + + return 0; +} + +static int +bpf_core_reloc_offsets(struct bpf_object *obj, const char *targ_btf_path) +{ + const struct btf_ext_info_sec *sec; + const struct bpf_offset_reloc *rec; + const struct btf_ext_info *seg; + struct hashmap_entry *entry; + struct hashmap *cand_cache = NULL; + struct bpf_program *prog; + struct btf *targ_btf; + const char *sec_name; + int i, err = 0; + + if (targ_btf_path) + targ_btf = btf__parse_elf(targ_btf_path, NULL); + else + targ_btf = bpf_core_find_kernel_btf(); + if (IS_ERR(targ_btf)) { + pr_warning("failed to get target BTF: %ld\n", + PTR_ERR(targ_btf)); + return PTR_ERR(targ_btf); + } + + cand_cache = hashmap__new(bpf_core_hash_fn, bpf_core_equal_fn, NULL); + if (IS_ERR(cand_cache)) { + err = PTR_ERR(cand_cache); + goto out; + } + + seg = &obj->btf_ext->offset_reloc_info; + for_each_btf_ext_sec(seg, sec) { + sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off); + if (str_is_empty(sec_name)) { + err = -EINVAL; + goto out; + } + prog = bpf_object__find_program_by_title(obj, sec_name); + if (!prog) { + pr_warning("failed to find program '%s' for CO-RE offset relocation\n", + sec_name); + err = -EINVAL; + goto out; + } + + pr_debug("prog '%s': performing %d CO-RE offset relocs\n", + sec_name, sec->num_info); + + for_each_btf_ext_rec(seg, sec, i, rec) { + err = bpf_core_reloc_offset(prog, rec, i, obj->btf, + targ_btf, cand_cache); + if (err) { + pr_warning("prog '%s': relo #%d: failed to relocate: %d\n", + sec_name, i, err); + goto out; + } + } + } + +out: + btf__free(targ_btf); + if (!IS_ERR_OR_NULL(cand_cache)) { + hashmap__for_each_entry(cand_cache, entry, i) { + bpf_core_free_cands(entry->value); + } + hashmap__free(cand_cache); + } + return err; +} + +static int +bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) +{ + int err = 0; + + if (obj->btf_ext->offset_reloc_info.len) + err = bpf_core_reloc_offsets(obj, targ_btf_path); + + return err; +} + static int bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj, struct reloc_desc *relo) @@ -2441,14 +3279,21 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) return 0; } - static int -bpf_object__relocate(struct bpf_object *obj) +bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path) { struct bpf_program *prog; size_t i; int err; + if (obj->btf_ext) { + err = bpf_object__relocate_core(obj, targ_btf_path); + if (err) { + pr_warning("failed to perform CO-RE relocations: %d\n", + err); + return err; + } + } for (i = 0; i < obj->nr_programs; i++) { prog = &obj->programs[i]; @@ -2854,7 +3699,7 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr) obj->loaded = true; CHECK_ERR(bpf_object__create_maps(obj), err, out); - CHECK_ERR(bpf_object__relocate(obj), err, out); + CHECK_ERR(bpf_object__relocate(obj, attr->target_btf_path), err, out); CHECK_ERR(bpf_object__load_progs(obj, attr->log_level), err, out); return 0; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 8a9d462a6f6d..e8f70977d137 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -92,6 +92,7 @@ LIBBPF_API void bpf_object__close(struct bpf_object *object); struct bpf_object_load_attr { struct bpf_object *obj; int log_level; + const char *target_btf_path; }; /* Load/unload object into/from kernel */ -- Gitee From 0092375b7f19a4b47b69a59eef398a91a0bf7089 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:52 -0700 Subject: [PATCH 23/43] selftests/bpf: add BPF_CORE_READ relocatable read macro ANBZ: #5530 commit 2dc26d5a4f2e97364542030a4a51e4a50c14bae3 upstream. Add BPF_CORE_READ macro used in tests to do bpf_core_read(), which automatically captures offset relocation. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/bpf_helpers.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 7b93f3b636f1..09ebaf69bc83 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -439,4 +439,24 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode, (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) #endif +/* + * BPF_CORE_READ abstracts away bpf_probe_read() call and captures offset + * relocation for source address using __builtin_preserve_access_index() + * built-in, provided by Clang. + * + * __builtin_preserve_access_index() takes as an argument an expression of + * taking an address of a field within struct/union. It makes compiler emit + * a relocation, which records BTF type ID describing root struct/union and an + * accessor string which describes exact embedded field that was used to take + * an address. See detailed description of this relocation format and + * semantics in comments to struct bpf_offset_reloc in libbpf_internal.h. + * + * This relocation allows libbpf to adjust BPF instruction to use correct + * actual field offset, based on target kernel BTF type that matches original + * (local) BTF, used to record relocation. + */ +#define BPF_CORE_READ(dst, src) \ + bpf_probe_read((dst), sizeof(*(src)), \ + __builtin_preserve_access_index(src)) + #endif -- Gitee From 015bf9d56634cc5bd601a91ff0deecac34d50649 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:53 -0700 Subject: [PATCH 24/43] selftests/bpf: add CO-RE relocs testing setup ANBZ: #5530 commit df36e621418b0de4d4afec5f8002d45ee636bb5c upstream. Add CO-RE relocation test runner. Add one simple test validating that libbpf's logic for searching for kernel image and loading BTF out of it works. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 129 ++++++++++++++++++ .../bpf/progs/test_core_reloc_kernel.c | 35 +++++ 2 files changed, 164 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/core_reloc.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c new file mode 100644 index 000000000000..4323a459c8a2 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +struct core_reloc_test_case { + const char *case_name; + const char *bpf_obj_file; + const char *btf_src_file; + const char *input; + int input_len; + const char *output; + int output_len; + bool fails; +}; + +static struct core_reloc_test_case test_cases[] = { + /* validate we can find kernel image and use its BTF for relocs */ + { + .case_name = "kernel", + .bpf_obj_file = "test_core_reloc_kernel.o", + .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */ + .input = "", + .input_len = 0, + .output = "\1", /* true */ + .output_len = 1, + }, +}; + +struct data { + char in[256]; + char out[256]; +}; + +void test_core_reloc(void) +{ + const char *probe_name = "raw_tracepoint/sys_enter"; + struct bpf_object_load_attr load_attr = {}; + struct core_reloc_test_case *test_case; + int err, duration = 0, i, equal; + struct bpf_link *link = NULL; + struct bpf_map *data_map; + struct bpf_program *prog; + struct bpf_object *obj; + const int zero = 0; + struct data data; + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + test_case = &test_cases[i]; + + if (!test__start_subtest(test_case->case_name)) + continue; + + obj = bpf_object__open(test_case->bpf_obj_file); + if (CHECK(IS_ERR_OR_NULL(obj), "obj_open", + "failed to open '%s': %ld\n", + test_case->bpf_obj_file, PTR_ERR(obj))) + continue; + + prog = bpf_object__find_program_by_title(obj, probe_name); + if (CHECK(!prog, "find_probe", + "prog '%s' not found\n", probe_name)) + goto cleanup; + bpf_program__set_type(prog, BPF_PROG_TYPE_RAW_TRACEPOINT); + + load_attr.obj = obj; + load_attr.log_level = 0; + load_attr.target_btf_path = test_case->btf_src_file; + err = bpf_object__load_xattr(&load_attr); + if (test_case->fails) { + CHECK(!err, "obj_load_fail", + "should fail to load prog '%s'\n", probe_name); + goto cleanup; + } else { + if (CHECK(err, "obj_load", + "failed to load prog '%s': %d\n", + probe_name, err)) + goto cleanup; + } + + link = bpf_program__attach_raw_tracepoint(prog, "sys_enter"); + if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", + PTR_ERR(link))) + goto cleanup; + + data_map = bpf_object__find_map_by_name(obj, "test_cor.bss"); + if (CHECK(!data_map, "find_data_map", "data map not found\n")) + goto cleanup; + + memset(&data, 0, sizeof(data)); + memcpy(data.in, test_case->input, test_case->input_len); + + err = bpf_map_update_elem(bpf_map__fd(data_map), + &zero, &data, 0); + if (CHECK(err, "update_data_map", + "failed to update .data map: %d\n", err)) + goto cleanup; + + /* trigger test run */ + usleep(1); + + err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &data); + if (CHECK(err, "get_result", + "failed to get output data: %d\n", err)) + goto cleanup; + + equal = memcmp(data.out, test_case->output, + test_case->output_len) == 0; + if (CHECK(!equal, "check_result", + "input/output data don't match\n")) { + int j; + + for (j = 0; j < test_case->input_len; j++) { + printf("input byte #%d: 0x%02hhx\n", + j, test_case->input[j]); + } + for (j = 0; j < test_case->output_len; j++) { + printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n", + j, test_case->output[j], data.out[j]); + } + goto cleanup; + } + +cleanup: + if (!IS_ERR_OR_NULL(link)) { + bpf_link__destroy(link); + link = NULL; + } + bpf_object__close(obj); + } +} diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c b/tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c new file mode 100644 index 000000000000..fbaf544c7a37 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct task_struct { + int pid; + int tgid; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_kernel(void *ctx) +{ + struct task_struct *task = (void *)bpf_get_current_task(); + uint64_t pid_tgid = bpf_get_current_pid_tgid(); + int pid, tgid; + + if (BPF_CORE_READ(&pid, &task->pid) || + BPF_CORE_READ(&tgid, &task->tgid)) + return 1; + + /* validate pid + tgid matches */ + data.out[0] = (((uint64_t)pid << 32) | tgid) == pid_tgid; + + return 0; +} -- Gitee From d65590b42e838467d121ec0860573adc27b7256c Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:54 -0700 Subject: [PATCH 25/43] selftests/bpf: add CO-RE relocs struct flavors tests ANBZ: #5530 commit 002d3afce65518dc5dfc398a37c2be2a6bf559c4 upstream. Add tests verifying that BPF program can use various struct/union "flavors" to extract data from the same target struct/union. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 34 +++++++++++ .../bpf/progs/btf__core_reloc_flavors.c | 3 + .../btf__core_reloc_flavors__err_wrong_name.c | 3 + .../selftests/bpf/progs/core_reloc_types.h | 15 +++++ .../bpf/progs/test_core_reloc_flavors.c | 61 +++++++++++++++++++ 5 files changed, 116 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c create mode 100644 tools/testing/selftests/bpf/progs/core_reloc_types.h create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 4323a459c8a2..59fd9cb207ab 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -1,5 +1,32 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include "progs/core_reloc_types.h" + +#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name) + +#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = 42, \ + .b = 0xc001, \ + .c = 0xbeef, \ +} + +#define FLAVORS_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_flavors.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" \ + +#define FLAVORS_CASE(name) { \ + FLAVORS_CASE_COMMON(name), \ + .input = FLAVORS_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = FLAVORS_DATA(core_reloc_flavors), \ + .output_len = sizeof(struct core_reloc_flavors), \ +} + +#define FLAVORS_ERR_CASE(name) { \ + FLAVORS_CASE_COMMON(name), \ + .fails = true, \ +} struct core_reloc_test_case { const char *case_name; @@ -23,6 +50,13 @@ static struct core_reloc_test_case test_cases[] = { .output = "\1", /* true */ .output_len = 1, }, + + /* validate BPF program can use multiple flavors to match against + * single target BTF type + */ + FLAVORS_CASE(flavors), + + FLAVORS_ERR_CASE(flavors__err_wrong_name), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c new file mode 100644 index 000000000000..b74455b91227 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_flavors x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c new file mode 100644 index 000000000000..7b6035f86ee6 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_flavors__err_wrong_name x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h new file mode 100644 index 000000000000..33b0c6a61912 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -0,0 +1,15 @@ +/* + * FLAVORS + */ +struct core_reloc_flavors { + int a; + int b; + int c; +}; + +/* this is not a flavor, as it doesn't have triple underscore */ +struct core_reloc_flavors__err_wrong_name { + int a; + int b; + int c; +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c b/tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c new file mode 100644 index 000000000000..0f6fd3ff14e8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_flavors { + int a; + int b; + int c; +}; + +/* local flavor with reversed layout */ +struct core_reloc_flavors___reversed { + int c; + int b; + int a; +}; + +/* local flavor with nested/overlapping layout */ +struct core_reloc_flavors___weird { + struct { + int b; + }; + /* a and c overlap in local flavor, but this should still work + * correctly with target original flavor + */ + union { + int a; + int c; + }; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_flavors(void *ctx) +{ + struct core_reloc_flavors *in_orig = (void *)&data.in; + struct core_reloc_flavors___reversed *in_rev = (void *)&data.in; + struct core_reloc_flavors___weird *in_weird = (void *)&data.in; + struct core_reloc_flavors *out = (void *)&data.out; + + /* read a using weird layout */ + if (BPF_CORE_READ(&out->a, &in_weird->a)) + return 1; + /* read b using reversed layout */ + if (BPF_CORE_READ(&out->b, &in_rev->b)) + return 1; + /* read c using original layout */ + if (BPF_CORE_READ(&out->c, &in_orig->c)) + return 1; + + return 0; +} -- Gitee From ff02bbdb5655249317c362193997f9cdbb544ba7 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:55 -0700 Subject: [PATCH 26/43] selftests/bpf: add CO-RE relocs nesting tests ANBZ: #5530 commit ec6438a988a43dcb03f0a04f3f51a48aba54764a upstream. Add a bunch of test validating correct handling of nested structs/unions. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 39 +++ .../bpf/progs/btf__core_reloc_nesting.c | 3 + .../btf__core_reloc_nesting___anon_embed.c | 3 + ...f__core_reloc_nesting___dup_compat_types.c | 5 + ...core_reloc_nesting___err_array_container.c | 3 + ...tf__core_reloc_nesting___err_array_field.c | 3 + ...e_reloc_nesting___err_dup_incompat_types.c | 4 + ...re_reloc_nesting___err_missing_container.c | 3 + ...__core_reloc_nesting___err_missing_field.c | 3 + ..._reloc_nesting___err_nonstruct_container.c | 3 + ...e_reloc_nesting___err_partial_match_dups.c | 4 + .../btf__core_reloc_nesting___err_too_deep.c | 3 + .../btf__core_reloc_nesting___extra_nesting.c | 3 + ..._core_reloc_nesting___struct_union_mixup.c | 3 + .../selftests/bpf/progs/core_reloc_types.h | 293 ++++++++++++++++++ .../bpf/progs/test_core_reloc_nesting.c | 45 +++ 16 files changed, 420 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 59fd9cb207ab..3e9344e41556 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -28,6 +28,29 @@ .fails = true, \ } +#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = { .a = { .a = 42 } }, \ + .b = { .b = { .b = 0xc001 } }, \ +} + +#define NESTING_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_nesting.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define NESTING_CASE(name) { \ + NESTING_CASE_COMMON(name), \ + .input = NESTING_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = NESTING_DATA(core_reloc_nesting), \ + .output_len = sizeof(struct core_reloc_nesting) \ +} + +#define NESTING_ERR_CASE(name) { \ + NESTING_CASE_COMMON(name), \ + .fails = true, \ +} + struct core_reloc_test_case { const char *case_name; const char *bpf_obj_file; @@ -57,6 +80,22 @@ static struct core_reloc_test_case test_cases[] = { FLAVORS_CASE(flavors), FLAVORS_ERR_CASE(flavors__err_wrong_name), + + /* various struct/enum nesting and resolution scenarios */ + NESTING_CASE(nesting), + NESTING_CASE(nesting___anon_embed), + NESTING_CASE(nesting___struct_union_mixup), + NESTING_CASE(nesting___extra_nesting), + NESTING_CASE(nesting___dup_compat_types), + + NESTING_ERR_CASE(nesting___err_missing_field), + NESTING_ERR_CASE(nesting___err_array_field), + NESTING_ERR_CASE(nesting___err_missing_container), + NESTING_ERR_CASE(nesting___err_nonstruct_container), + NESTING_ERR_CASE(nesting___err_array_container), + NESTING_ERR_CASE(nesting___err_dup_incompat_types), + NESTING_ERR_CASE(nesting___err_partial_match_dups), + NESTING_ERR_CASE(nesting___err_too_deep), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c new file mode 100644 index 000000000000..4480fcc0f183 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c new file mode 100644 index 000000000000..13e108f76ece --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___anon_embed x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c new file mode 100644 index 000000000000..76b54fda5fbb --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c @@ -0,0 +1,5 @@ +#include "core_reloc_types.h" + +void f1(struct core_reloc_nesting___dup_compat_types x) {} +void f2(struct core_reloc_nesting___dup_compat_types__2 x) {} +void f3(struct core_reloc_nesting___dup_compat_types__3 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c new file mode 100644 index 000000000000..975fb95db810 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___err_array_container x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c new file mode 100644 index 000000000000..ad66c67e7980 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___err_array_field x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c new file mode 100644 index 000000000000..35c5f8da6812 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c @@ -0,0 +1,4 @@ +#include "core_reloc_types.h" + +void f1(struct core_reloc_nesting___err_dup_incompat_types__1 x) {} +void f2(struct core_reloc_nesting___err_dup_incompat_types__2 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c new file mode 100644 index 000000000000..142e332041db --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___err_missing_container x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c new file mode 100644 index 000000000000..efcae167fab9 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___err_missing_field x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c new file mode 100644 index 000000000000..97aaaedd8ada --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___err_nonstruct_container x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c new file mode 100644 index 000000000000..ffde35086e90 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c @@ -0,0 +1,4 @@ +#include "core_reloc_types.h" + +void f1(struct core_reloc_nesting___err_partial_match_dups__a x) {} +void f2(struct core_reloc_nesting___err_partial_match_dups__b x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c new file mode 100644 index 000000000000..39a2fadd8e95 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___err_too_deep x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c new file mode 100644 index 000000000000..a09d9dfb20df --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___extra_nesting x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c new file mode 100644 index 000000000000..3d8a1a74012f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_nesting___struct_union_mixup x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 33b0c6a61912..340ee2bcd463 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -13,3 +13,296 @@ struct core_reloc_flavors__err_wrong_name { int b; int c; }; + +/* + * NESTING + */ +/* original set up, used to record relocations in BPF program */ +struct core_reloc_nesting_substruct { + int a; +}; + +union core_reloc_nesting_subunion { + int b; +}; + +struct core_reloc_nesting { + union { + struct core_reloc_nesting_substruct a; + } a; + struct { + union core_reloc_nesting_subunion b; + } b; +}; + +/* inlined anonymous struct/union instead of named structs in original */ +struct core_reloc_nesting___anon_embed { + int __just_for_padding; + union { + struct { + int a; + } a; + } a; + struct { + union { + int b; + } b; + } b; +}; + +/* different mix of nested structs/unions than in original */ +struct core_reloc_nesting___struct_union_mixup { + int __a; + struct { + int __a; + union { + char __a; + int a; + } a; + } a; + int __b; + union { + int __b; + union { + char __b; + int b; + } b; + } b; +}; + +/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */ +struct core_reloc_nesting___extra_nesting { + int __padding; + struct { + struct { + struct { + struct { + union { + int a; + } a; + }; + }; + } a; + int __some_more; + struct { + union { + union { + union { + struct { + int b; + }; + } b; + }; + } b; + }; + }; +}; + +/* three flavors of same struct with different structure but same layout for + * a.a.a and b.b.b, thus successfully resolved and relocatable */ +struct core_reloc_nesting___dup_compat_types { + char __just_for_padding; + /* 3 more bytes of padding */ + struct { + struct { + int a; /* offset 4 */ + } a; + } a; + long long __more_padding; + struct { + struct { + int b; /* offset 16 */ + } b; + } b; +}; + +struct core_reloc_nesting___dup_compat_types__2 { + int __aligned_padding; + struct { + int __trickier_noop[0]; + struct { + char __some_more_noops[0]; + int a; /* offset 4 */ + } a; + } a; + int __more_padding; + struct { + struct { + struct { + int __critical_padding; + int b; /* offset 16 */ + } b; + int __does_not_matter; + }; + } b; + int __more_irrelevant_stuff; +}; + +struct core_reloc_nesting___dup_compat_types__3 { + char __correct_padding[4]; + struct { + struct { + int a; /* offset 4 */ + } a; + } a; + /* 8 byte padding due to next struct's alignment */ + struct { + struct { + int b; + } b; + } b __attribute__((aligned(16))); +}; + +/* b.b.b field is missing */ +struct core_reloc_nesting___err_missing_field { + struct { + struct { + int a; + } a; + } a; + struct { + struct { + int x; + } b; + } b; +}; + +/* b.b.b field is an array of integers instead of plain int */ +struct core_reloc_nesting___err_array_field { + struct { + struct { + int a; + } a; + } a; + struct { + struct { + int b[1]; + } b; + } b; +}; + +/* middle b container is missing */ +struct core_reloc_nesting___err_missing_container { + struct { + struct { + int a; + } a; + } a; + struct { + int x; + } b; +}; + +/* middle b container is referenced through pointer instead of being embedded */ +struct core_reloc_nesting___err_nonstruct_container { + struct { + struct { + int a; + } a; + } a; + struct { + struct { + int b; + } *b; + } b; +}; + +/* middle b container is an array of structs instead of plain struct */ +struct core_reloc_nesting___err_array_container { + struct { + struct { + int a; + } a; + } a; + struct { + struct { + int b; + } b[1]; + } b; +}; + +/* two flavors of same struct with incompatible layout for b.b.b */ +struct core_reloc_nesting___err_dup_incompat_types__1 { + struct { + struct { + int a; /* offset 0 */ + } a; + } a; + struct { + struct { + int b; /* offset 4 */ + } b; + } b; +}; + +struct core_reloc_nesting___err_dup_incompat_types__2 { + struct { + struct { + int a; /* offset 0 */ + } a; + } a; + int __extra_padding; + struct { + struct { + int b; /* offset 8 (!) */ + } b; + } b; +}; + +/* two flavors of same struct having one of a.a.a and b.b.b, but not both */ +struct core_reloc_nesting___err_partial_match_dups__a { + struct { + struct { + int a; + } a; + } a; +}; + +struct core_reloc_nesting___err_partial_match_dups__b { + struct { + struct { + int b; + } b; + } b; +}; + +struct core_reloc_nesting___err_too_deep { + struct { + struct { + int a; + } a; + } a; + /* 65 levels of nestedness for b.b.b */ + struct { + struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + struct { struct { struct { struct { struct { + /* this one is one too much */ + struct { + int b; + }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + }; }; }; }; }; + } b; + } b; +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c b/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c new file mode 100644 index 000000000000..aa59fa435982 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_nesting_substruct { + int a; +}; + +union core_reloc_nesting_subunion { + int b; +}; + +/* int a.a.a and b.b.b accesses */ +struct core_reloc_nesting { + union { + struct core_reloc_nesting_substruct a; + } a; + struct { + union core_reloc_nesting_subunion b; + } b; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_nesting(void *ctx) +{ + struct core_reloc_nesting *in = (void *)&data.in; + struct core_reloc_nesting *out = (void *)&data.out; + + if (BPF_CORE_READ(&out->a.a.a, &in->a.a.a)) + return 1; + if (BPF_CORE_READ(&out->b.b.b, &in->b.b.b)) + return 1; + + return 0; +} -- Gitee From 380802c4e352c65590e16487824578a988119721 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:56 -0700 Subject: [PATCH 27/43] selftests/bpf: add CO-RE relocs array tests ANBZ: #5530 commit 20a9ad2e71368da8e831317f0a545e4bfb31cce1 upstream. Add tests for various array handling/relocation scenarios. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 41 ++++++++++ .../bpf/progs/btf__core_reloc_arrays.c | 3 + .../btf__core_reloc_arrays___diff_arr_dim.c | 3 + ...btf__core_reloc_arrays___diff_arr_val_sz.c | 3 + .../btf__core_reloc_arrays___err_non_array.c | 3 + ...btf__core_reloc_arrays___err_too_shallow.c | 3 + .../btf__core_reloc_arrays___err_too_small.c | 3 + ..._core_reloc_arrays___err_wrong_val_type1.c | 3 + ..._core_reloc_arrays___err_wrong_val_type2.c | 3 + .../selftests/bpf/progs/core_reloc_types.h | 81 +++++++++++++++++++ .../bpf/progs/test_core_reloc_arrays.c | 54 +++++++++++++ 11 files changed, 200 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 3e9344e41556..3b40160c0837 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -51,6 +51,36 @@ .fails = true, \ } +#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = { [2] = 1 }, \ + .b = { [1] = { [2] = { [3] = 2 } } }, \ + .c = { [1] = { .c = 3 } }, \ + .d = { [0] = { [0] = { .d = 4 } } }, \ +} + +#define ARRAYS_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_arrays.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define ARRAYS_CASE(name) { \ + ARRAYS_CASE_COMMON(name), \ + .input = ARRAYS_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \ + .a2 = 1, \ + .b123 = 2, \ + .c1c = 3, \ + .d00d = 4, \ + }, \ + .output_len = sizeof(struct core_reloc_arrays_output) \ +} + +#define ARRAYS_ERR_CASE(name) { \ + ARRAYS_CASE_COMMON(name), \ + .fails = true, \ +} + struct core_reloc_test_case { const char *case_name; const char *bpf_obj_file; @@ -96,6 +126,17 @@ static struct core_reloc_test_case test_cases[] = { NESTING_ERR_CASE(nesting___err_dup_incompat_types), NESTING_ERR_CASE(nesting___err_partial_match_dups), NESTING_ERR_CASE(nesting___err_too_deep), + + /* various array access relocation scenarios */ + ARRAYS_CASE(arrays), + ARRAYS_CASE(arrays___diff_arr_dim), + ARRAYS_CASE(arrays___diff_arr_val_sz), + + ARRAYS_ERR_CASE(arrays___err_too_small), + ARRAYS_ERR_CASE(arrays___err_too_shallow), + ARRAYS_ERR_CASE(arrays___err_non_array), + ARRAYS_ERR_CASE(arrays___err_wrong_val_type1), + ARRAYS_ERR_CASE(arrays___err_wrong_val_type2), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c new file mode 100644 index 000000000000..018ed7fbba3a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c new file mode 100644 index 000000000000..13d662c57014 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___diff_arr_dim x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c new file mode 100644 index 000000000000..a351f418c85d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___diff_arr_val_sz x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c new file mode 100644 index 000000000000..a8735009becc --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___err_non_array x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c new file mode 100644 index 000000000000..2a67c28b1e75 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___err_too_shallow x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c new file mode 100644 index 000000000000..1142c08c925f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___err_too_small x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c new file mode 100644 index 000000000000..795a5b729176 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___err_wrong_val_type1 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c new file mode 100644 index 000000000000..3af74b837c4d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___err_wrong_val_type2 x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 340ee2bcd463..45de7986ea2e 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -306,3 +306,84 @@ struct core_reloc_nesting___err_too_deep { } b; } b; }; + +/* + * ARRAYS + */ +struct core_reloc_arrays_output { + int a2; + char b123; + int c1c; + int d00d; +}; + +struct core_reloc_arrays_substruct { + int c; + int d; +}; + +struct core_reloc_arrays { + int a[5]; + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; +}; + +/* bigger array dimensions */ +struct core_reloc_arrays___diff_arr_dim { + int a[7]; + char b[3][4][5]; + struct core_reloc_arrays_substruct c[4]; + struct core_reloc_arrays_substruct d[2][3]; +}; + +/* different size of array's value (struct) */ +struct core_reloc_arrays___diff_arr_val_sz { + int a[5]; + char b[2][3][4]; + struct { + int __padding1; + int c; + int __padding2; + } c[3]; + struct { + int __padding1; + int d; + int __padding2; + } d[1][2]; +}; + +struct core_reloc_arrays___err_too_small { + int a[2]; /* this one is too small */ + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; +}; + +struct core_reloc_arrays___err_too_shallow { + int a[5]; + char b[2][3]; /* this one lacks one dimension */ + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; +}; + +struct core_reloc_arrays___err_non_array { + int a; /* not an array */ + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; +}; + +struct core_reloc_arrays___err_wrong_val_type1 { + char a[5]; /* char instead of int */ + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; +}; + +struct core_reloc_arrays___err_wrong_val_type2 { + int a[5]; + char b[2][3][4]; + int c[3]; /* value is not a struct */ + struct core_reloc_arrays_substruct d[1][2]; +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c b/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c new file mode 100644 index 000000000000..72603bfdce5c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_arrays_output { + int a2; + char b123; + int c1c; + int d00d; +}; + +struct core_reloc_arrays_substruct { + int c; + int d; +}; + +struct core_reloc_arrays { + int a[5]; + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_arrays(void *ctx) +{ + struct core_reloc_arrays *in = (void *)&data.in; + struct core_reloc_arrays_output *out = (void *)&data.out; + + /* in->a[2] */ + if (BPF_CORE_READ(&out->a2, &in->a[2])) + return 1; + /* in->b[1][2][3] */ + if (BPF_CORE_READ(&out->b123, &in->b[1][2][3])) + return 1; + /* in->c[1].c */ + if (BPF_CORE_READ(&out->c1c, &in->c[1].c)) + return 1; + /* in->d[0][0].d */ + if (BPF_CORE_READ(&out->d00d, &in->d[0][0].d)) + return 1; + + return 0; +} -- Gitee From e8dd6f035dfc28a7f7bf9c89a2d0f2d71ec9a2cb Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:57 -0700 Subject: [PATCH 28/43] selftests/bpf: add CO-RE relocs enum/ptr/func_proto tests ANBZ: #5530 commit d9db3550300f4390e457c79189e2601c107f9fe6 upstream. Test CO-RE relocation handling of ints, enums, pointers, func protos, etc. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 36 ++++++++++ .../bpf/progs/btf__core_reloc_primitives.c | 3 + ...f__core_reloc_primitives___diff_enum_def.c | 3 + ..._core_reloc_primitives___diff_func_proto.c | 3 + ...f__core_reloc_primitives___diff_ptr_type.c | 3 + ...tf__core_reloc_primitives___err_non_enum.c | 3 + ...btf__core_reloc_primitives___err_non_int.c | 3 + ...btf__core_reloc_primitives___err_non_ptr.c | 3 + .../selftests/bpf/progs/core_reloc_types.h | 67 +++++++++++++++++++ .../bpf/progs/test_core_reloc_primitives.c | 42 ++++++++++++ 10 files changed, 166 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 3b40160c0837..37b36df93ded 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -81,6 +81,32 @@ .fails = true, \ } +#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = 1, \ + .b = 2, \ + .c = 3, \ + .d = (void *)4, \ + .f = (void *)5, \ +} + +#define PRIMITIVES_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_primitives.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define PRIMITIVES_CASE(name) { \ + PRIMITIVES_CASE_COMMON(name), \ + .input = PRIMITIVES_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = PRIMITIVES_DATA(core_reloc_primitives), \ + .output_len = sizeof(struct core_reloc_primitives), \ +} + +#define PRIMITIVES_ERR_CASE(name) { \ + PRIMITIVES_CASE_COMMON(name), \ + .fails = true, \ +} + struct core_reloc_test_case { const char *case_name; const char *bpf_obj_file; @@ -137,6 +163,16 @@ static struct core_reloc_test_case test_cases[] = { ARRAYS_ERR_CASE(arrays___err_non_array), ARRAYS_ERR_CASE(arrays___err_wrong_val_type1), ARRAYS_ERR_CASE(arrays___err_wrong_val_type2), + + /* enum/ptr/int handling scenarios */ + PRIMITIVES_CASE(primitives), + PRIMITIVES_CASE(primitives___diff_enum_def), + PRIMITIVES_CASE(primitives___diff_func_proto), + PRIMITIVES_CASE(primitives___diff_ptr_type), + + PRIMITIVES_ERR_CASE(primitives___err_non_enum), + PRIMITIVES_ERR_CASE(primitives___err_non_int), + PRIMITIVES_ERR_CASE(primitives___err_non_ptr), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c new file mode 100644 index 000000000000..96b90e39242a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c new file mode 100644 index 000000000000..6e87233a3ed0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives___diff_enum_def x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c new file mode 100644 index 000000000000..d9f48e80b9d9 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives___diff_func_proto x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c new file mode 100644 index 000000000000..c718f75f8f3b --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives___diff_ptr_type x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c new file mode 100644 index 000000000000..b8a120830891 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives___err_non_enum x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c new file mode 100644 index 000000000000..ad8b3c9aa76f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives___err_non_int x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c new file mode 100644 index 000000000000..e20bc1d42d0a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_primitives___err_non_ptr x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 45de7986ea2e..7526a5f5755b 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -387,3 +387,70 @@ struct core_reloc_arrays___err_wrong_val_type2 { int c[3]; /* value is not a struct */ struct core_reloc_arrays_substruct d[1][2]; }; + +/* + * PRIMITIVES + */ +enum core_reloc_primitives_enum { + A = 0, + B = 1, +}; + +struct core_reloc_primitives { + char a; + int b; + enum core_reloc_primitives_enum c; + void *d; + int (*f)(const char *); +}; + +struct core_reloc_primitives___diff_enum_def { + char a; + int b; + void *d; + int (*f)(const char *); + enum { + X = 100, + Y = 200, + } c; /* inline enum def with differing set of values */ +}; + +struct core_reloc_primitives___diff_func_proto { + void (*f)(int); /* incompatible function prototype */ + void *d; + enum core_reloc_primitives_enum c; + int b; + char a; +}; + +struct core_reloc_primitives___diff_ptr_type { + const char * const d; /* different pointee type + modifiers */ + char a; + int b; + enum core_reloc_primitives_enum c; + int (*f)(const char *); +}; + +struct core_reloc_primitives___err_non_enum { + char a[1]; + int b; + int c; /* int instead of enum */ + void *d; + int (*f)(const char *); +}; + +struct core_reloc_primitives___err_non_int { + char a[1]; + int *b; /* ptr instead of int */ + enum core_reloc_primitives_enum c; + void *d; + int (*f)(const char *); +}; + +struct core_reloc_primitives___err_non_ptr { + char a[1]; + int b; + enum core_reloc_primitives_enum c; + int d; /* int instead of ptr */ + int (*f)(const char *); +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c b/tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c new file mode 100644 index 000000000000..1f094e7d5288 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +enum core_reloc_primitives_enum { + A = 0, + B = 1, +}; + +struct core_reloc_primitives { + char a; + int b; + enum core_reloc_primitives_enum c; + void *d; + int (*f)(const char *); +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_primitives(void *ctx) +{ + struct core_reloc_primitives *in = (void *)&data.in; + struct core_reloc_primitives *out = (void *)&data.out; + + if (BPF_CORE_READ(&out->a, &in->a) || + BPF_CORE_READ(&out->b, &in->b) || + BPF_CORE_READ(&out->c, &in->c) || + BPF_CORE_READ(&out->d, &in->d) || + BPF_CORE_READ(&out->f, &in->f)) + return 1; + + return 0; +} -- Gitee From 3583b2d55583fd8ff92f996b83f1e424e5f53a2a Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:58 -0700 Subject: [PATCH 29/43] selftests/bpf: add CO-RE relocs modifiers/typedef tests ANBZ: #5530 commit 9654e2ae908eb0d51b0b79c7c50df0754ed38edd upstream. Add tests validating correct handling of various combinations of typedefs and const/volatile/restrict modifiers. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 27 +++++++ .../bpf/progs/btf__core_reloc_mods.c | 3 + .../progs/btf__core_reloc_mods___mod_swap.c | 3 + .../progs/btf__core_reloc_mods___typedefs.c | 3 + .../selftests/bpf/progs/core_reloc_types.h | 72 +++++++++++++++++++ .../bpf/progs/test_core_reloc_mods.c | 61 ++++++++++++++++ 6 files changed, 169 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_mods.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 37b36df93ded..9dadf462a951 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -107,6 +107,28 @@ .fails = true, \ } +#define MODS_CASE(name) { \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_mods.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o", \ + .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \ + .a = 1, \ + .b = 2, \ + .c = (void *)3, \ + .d = (void *)4, \ + .e = { [2] = 5 }, \ + .f = { [1] = 6 }, \ + .g = { .x = 7 }, \ + .h = { .y = 8 }, \ + }, \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \ + .a = 1, .b = 2, .c = 3, .d = 4, \ + .e = 5, .f = 6, .g = 7, .h = 8, \ + }, \ + .output_len = sizeof(struct core_reloc_mods_output), \ +} + struct core_reloc_test_case { const char *case_name; const char *bpf_obj_file; @@ -173,6 +195,11 @@ static struct core_reloc_test_case test_cases[] = { PRIMITIVES_ERR_CASE(primitives___err_non_enum), PRIMITIVES_ERR_CASE(primitives___err_non_int), PRIMITIVES_ERR_CASE(primitives___err_non_ptr), + + /* const/volatile/restrict and typedefs scenarios */ + MODS_CASE(mods), + MODS_CASE(mods___mod_swap), + MODS_CASE(mods___typedefs), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c new file mode 100644 index 000000000000..124197a2e813 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_mods x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c new file mode 100644 index 000000000000..f8a6592ca75f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_mods___mod_swap x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c new file mode 100644 index 000000000000..5c0d73687247 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_mods___typedefs x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 7526a5f5755b..3401e8342e57 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -454,3 +454,75 @@ struct core_reloc_primitives___err_non_ptr { int d; /* int instead of ptr */ int (*f)(const char *); }; + +/* + * MODS + */ +struct core_reloc_mods_output { + int a, b, c, d, e, f, g, h; +}; + +typedef const int int_t; +typedef const char *char_ptr_t; +typedef const int arr_t[7]; + +struct core_reloc_mods_substruct { + int x; + int y; +}; + +typedef struct { + int x; + int y; +} core_reloc_mods_substruct_t; + +struct core_reloc_mods { + int a; + int_t b; + char *c; + char_ptr_t d; + int e[3]; + arr_t f; + struct core_reloc_mods_substruct g; + core_reloc_mods_substruct_t h; +}; + +/* a/b, c/d, e/f, and g/h pairs are swapped */ +struct core_reloc_mods___mod_swap { + int b; + int_t a; + char *d; + char_ptr_t c; + int f[3]; + arr_t e; + struct { + int y; + int x; + } h; + core_reloc_mods_substruct_t g; +}; + +typedef int int1_t; +typedef int1_t int2_t; +typedef int2_t int3_t; + +typedef int arr1_t[5]; +typedef arr1_t arr2_t; +typedef arr2_t arr3_t; +typedef arr3_t arr4_t; + +typedef const char * const volatile restrict fancy_char_ptr_t; + +typedef core_reloc_mods_substruct_t core_reloc_mods_substruct_tt; + +/* we need more typedefs */ +struct core_reloc_mods___typedefs { + core_reloc_mods_substruct_tt g; + core_reloc_mods_substruct_tt h; + arr4_t f; + arr4_t e; + fancy_char_ptr_t d; + fancy_char_ptr_t c; + int3_t b; + int3_t a; +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_mods.c b/tools/testing/selftests/bpf/progs/test_core_reloc_mods.c new file mode 100644 index 000000000000..b6cc51ca7019 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_mods.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_mods_output { + int a, b, c, d, e, f, g, h; +}; + +typedef const int int_t; +typedef const char *char_ptr_t; +typedef const int arr_t[7]; + +struct core_reloc_mods_substruct { + int x; + int y; +}; + +typedef struct { + int x; + int y; +} core_reloc_mods_substruct_t; + +struct core_reloc_mods { + int a; + int_t b; + char *c; + char_ptr_t d; + int e[3]; + arr_t f; + struct core_reloc_mods_substruct g; + core_reloc_mods_substruct_t h; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_mods(void *ctx) +{ + struct core_reloc_mods *in = (void *)&data.in; + struct core_reloc_mods_output *out = (void *)&data.out; + + if (BPF_CORE_READ(&out->a, &in->a) || + BPF_CORE_READ(&out->b, &in->b) || + BPF_CORE_READ(&out->c, &in->c) || + BPF_CORE_READ(&out->d, &in->d) || + BPF_CORE_READ(&out->e, &in->e[2]) || + BPF_CORE_READ(&out->f, &in->f[1]) || + BPF_CORE_READ(&out->g, &in->g.x) || + BPF_CORE_READ(&out->h, &in->h.y)) + return 1; + + return 0; +} -- Gitee From 51d3ccd70e6cba282e4e1e0e0aa5f80353e8f54f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:39:59 -0700 Subject: [PATCH 30/43] selftests/bpf: add CO-RE relocs ptr-as-array tests ANBZ: #5530 commit d698f9dbdbed036ef28a96cd34a1b5d7fe58750e upstream. Add test validating correct relocation handling for cases where pointer to something is used as an array. E.g.: int *ptr = ...; int x = ptr[42]; Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 20 +++++++++++++ .../bpf/progs/btf__core_reloc_ptr_as_arr.c | 3 ++ .../btf__core_reloc_ptr_as_arr___diff_sz.c | 3 ++ .../selftests/bpf/progs/core_reloc_types.h | 13 +++++++++ .../bpf/progs/test_core_reloc_ptr_as_arr.c | 29 +++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 9dadf462a951..2cfe0bdc53f8 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -129,6 +129,22 @@ .output_len = sizeof(struct core_reloc_mods_output), \ } +#define PTR_AS_ARR_CASE(name) { \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o", \ + .input = (const char *)&(struct core_reloc_##name []){ \ + { .a = 1 }, \ + { .a = 2 }, \ + { .a = 3 }, \ + }, \ + .input_len = 3 * sizeof(struct core_reloc_##name), \ + .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \ + .a = 3, \ + }, \ + .output_len = sizeof(struct core_reloc_ptr_as_arr), \ +} + struct core_reloc_test_case { const char *case_name; const char *bpf_obj_file; @@ -200,6 +216,10 @@ static struct core_reloc_test_case test_cases[] = { MODS_CASE(mods), MODS_CASE(mods___mod_swap), MODS_CASE(mods___typedefs), + + /* handling "ptr is an array" semantics */ + PTR_AS_ARR_CASE(ptr_as_arr), + PTR_AS_ARR_CASE(ptr_as_arr___diff_sz), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c new file mode 100644 index 000000000000..8da52432ba17 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ptr_as_arr x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c new file mode 100644 index 000000000000..003acfc9a3e7 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ptr_as_arr___diff_sz x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 3401e8342e57..c17c9279deae 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -526,3 +526,16 @@ struct core_reloc_mods___typedefs { int3_t b; int3_t a; }; + +/* + * PTR_AS_ARR + */ +struct core_reloc_ptr_as_arr { + int a; +}; + +struct core_reloc_ptr_as_arr___diff_sz { + int :32; /* padding */ + char __some_more_padding; + int a; +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c b/tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c new file mode 100644 index 000000000000..4f258c30499b --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_ptr_as_arr { + int a; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_ptr_as_arr(void *ctx) +{ + struct core_reloc_ptr_as_arr *in = (void *)&data.in; + struct core_reloc_ptr_as_arr *out = (void *)&data.out; + + if (BPF_CORE_READ(&out->a, &in[2].a)) + return 1; + + return 0; +} -- Gitee From 3770729f87f96fa37694353ff38c40a9b4baa49d Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:40:00 -0700 Subject: [PATCH 31/43] selftests/bpf: add CO-RE relocs ints tests ANBZ: #5530 commit c1f5e7dd19e71cd3607572bb957def618a33519a upstream. Add various tests validating handling compatible/incompatible integer types. Signed-off-by: Andrii Nakryiko Acked-by: Song Liu Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 40 +++++++ .../bpf/progs/btf__core_reloc_ints.c | 3 + .../bpf/progs/btf__core_reloc_ints___bool.c | 3 + .../btf__core_reloc_ints___err_bitfield.c | 3 + .../btf__core_reloc_ints___err_wrong_sz_16.c | 3 + .../btf__core_reloc_ints___err_wrong_sz_32.c | 3 + .../btf__core_reloc_ints___err_wrong_sz_64.c | 3 + .../btf__core_reloc_ints___err_wrong_sz_8.c | 3 + .../btf__core_reloc_ints___reverse_sign.c | 3 + .../selftests/bpf/progs/core_reloc_types.h | 101 ++++++++++++++++++ .../bpf/progs/test_core_reloc_ints.c | 43 ++++++++ 11 files changed, 208 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_ints.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 2cfe0bdc53f8..251ef8c518f0 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -145,6 +145,35 @@ .output_len = sizeof(struct core_reloc_ptr_as_arr), \ } +#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .u8_field = 1, \ + .s8_field = 2, \ + .u16_field = 3, \ + .s16_field = 4, \ + .u32_field = 5, \ + .s32_field = 6, \ + .u64_field = 7, \ + .s64_field = 8, \ +} + +#define INTS_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_ints.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define INTS_CASE(name) { \ + INTS_CASE_COMMON(name), \ + .input = INTS_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = INTS_DATA(core_reloc_ints), \ + .output_len = sizeof(struct core_reloc_ints), \ +} + +#define INTS_ERR_CASE(name) { \ + INTS_CASE_COMMON(name), \ + .fails = true, \ +} + struct core_reloc_test_case { const char *case_name; const char *bpf_obj_file; @@ -220,6 +249,17 @@ static struct core_reloc_test_case test_cases[] = { /* handling "ptr is an array" semantics */ PTR_AS_ARR_CASE(ptr_as_arr), PTR_AS_ARR_CASE(ptr_as_arr___diff_sz), + + /* int signedness/sizing/bitfield handling */ + INTS_CASE(ints), + INTS_CASE(ints___bool), + INTS_CASE(ints___reverse_sign), + + INTS_ERR_CASE(ints___err_bitfield), + INTS_ERR_CASE(ints___err_wrong_sz_8), + INTS_ERR_CASE(ints___err_wrong_sz_16), + INTS_ERR_CASE(ints___err_wrong_sz_32), + INTS_ERR_CASE(ints___err_wrong_sz_64), }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c new file mode 100644 index 000000000000..7d0f041042c5 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c new file mode 100644 index 000000000000..f9359450186e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___bool x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c new file mode 100644 index 000000000000..50369e8320a0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___err_bitfield x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c new file mode 100644 index 000000000000..823bac13d641 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___err_wrong_sz_16 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c new file mode 100644 index 000000000000..b44f3be18535 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___err_wrong_sz_32 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c new file mode 100644 index 000000000000..9a3dd2099c0f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___err_wrong_sz_64 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c new file mode 100644 index 000000000000..9f11ef5f6e88 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___err_wrong_sz_8 x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c new file mode 100644 index 000000000000..aafb1c5819d7 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_ints___reverse_sign x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index c17c9279deae..5f3ebd4f6dc3 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -1,3 +1,6 @@ +#include +#include + /* * FLAVORS */ @@ -539,3 +542,101 @@ struct core_reloc_ptr_as_arr___diff_sz { char __some_more_padding; int a; }; + +/* + * INTS + */ +struct core_reloc_ints { + uint8_t u8_field; + int8_t s8_field; + uint16_t u16_field; + int16_t s16_field; + uint32_t u32_field; + int32_t s32_field; + uint64_t u64_field; + int64_t s64_field; +}; + +/* signed/unsigned types swap */ +struct core_reloc_ints___reverse_sign { + int8_t u8_field; + uint8_t s8_field; + int16_t u16_field; + uint16_t s16_field; + int32_t u32_field; + uint32_t s32_field; + int64_t u64_field; + uint64_t s64_field; +}; + +struct core_reloc_ints___bool { + bool u8_field; /* bool instead of uint8 */ + int8_t s8_field; + uint16_t u16_field; + int16_t s16_field; + uint32_t u32_field; + int32_t s32_field; + uint64_t u64_field; + int64_t s64_field; +}; + +struct core_reloc_ints___err_bitfield { + uint8_t u8_field; + int8_t s8_field; + uint16_t u16_field; + int16_t s16_field; + uint32_t u32_field: 32; /* bitfields are not supported */ + int32_t s32_field; + uint64_t u64_field; + int64_t s64_field; +}; + +struct core_reloc_ints___err_wrong_sz_8 { + uint16_t u8_field; /* not 8-bit anymore */ + int16_t s8_field; /* not 8-bit anymore */ + + uint16_t u16_field; + int16_t s16_field; + uint32_t u32_field; + int32_t s32_field; + uint64_t u64_field; + int64_t s64_field; +}; + +struct core_reloc_ints___err_wrong_sz_16 { + uint8_t u8_field; + int8_t s8_field; + + uint32_t u16_field; /* not 16-bit anymore */ + int32_t s16_field; /* not 16-bit anymore */ + + uint32_t u32_field; + int32_t s32_field; + uint64_t u64_field; + int64_t s64_field; +}; + +struct core_reloc_ints___err_wrong_sz_32 { + uint8_t u8_field; + int8_t s8_field; + uint16_t u16_field; + int16_t s16_field; + + uint64_t u32_field; /* not 32-bit anymore */ + int64_t s32_field; /* not 32-bit anymore */ + + uint64_t u64_field; + int64_t s64_field; +}; + +struct core_reloc_ints___err_wrong_sz_64 { + uint8_t u8_field; + int8_t s8_field; + uint16_t u16_field; + int16_t s16_field; + uint32_t u32_field; + int32_t s32_field; + + uint32_t u64_field; /* not 64-bit anymore */ + int32_t s64_field; /* not 64-bit anymore */ +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_ints.c b/tools/testing/selftests/bpf/progs/test_core_reloc_ints.c new file mode 100644 index 000000000000..8ea5355f6e44 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_ints.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_ints { + uint8_t u8_field; + int8_t s8_field; + uint16_t u16_field; + int16_t s16_field; + uint32_t u32_field; + int32_t s32_field; + uint64_t u64_field; + int64_t s64_field; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_ints(void *ctx) +{ + struct core_reloc_ints *in = (void *)&data.in; + struct core_reloc_ints *out = (void *)&data.out; + + if (BPF_CORE_READ(&out->u8_field, &in->u8_field) || + BPF_CORE_READ(&out->s8_field, &in->s8_field) || + BPF_CORE_READ(&out->u16_field, &in->u16_field) || + BPF_CORE_READ(&out->s16_field, &in->s16_field) || + BPF_CORE_READ(&out->u32_field, &in->u32_field) || + BPF_CORE_READ(&out->s32_field, &in->s32_field) || + BPF_CORE_READ(&out->u64_field, &in->u64_field) || + BPF_CORE_READ(&out->s64_field, &in->s64_field)) + return 1; + + return 0; +} -- Gitee From 6a39b5a8bd3fb151b10fb211009b499fe31e08db Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 7 Aug 2019 14:40:01 -0700 Subject: [PATCH 32/43] selftests/bpf: add CO-RE relocs misc tests ANBZ: #5530 commit 29e1c66872450adba0ad552ff6019932168676f3 upstream. Add tests validating few edge-cases of capturing offset relocations. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov --- .../selftests/bpf/prog_tests/core_reloc.c | 19 +++++++ .../bpf/progs/btf__core_reloc_misc.c | 5 ++ .../selftests/bpf/progs/core_reloc_types.h | 25 +++++++++ .../bpf/progs/test_core_reloc_misc.c | 56 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_misc.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 251ef8c518f0..7d775046fcd1 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -260,6 +260,25 @@ static struct core_reloc_test_case test_cases[] = { INTS_ERR_CASE(ints___err_wrong_sz_16), INTS_ERR_CASE(ints___err_wrong_sz_32), INTS_ERR_CASE(ints___err_wrong_sz_64), + + /* validate edge cases of capturing relocations */ + { + .case_name = "misc", + .bpf_obj_file = "test_core_reloc_misc.o", + .btf_src_file = "btf__core_reloc_misc.o", + .input = (const char *)&(struct core_reloc_misc_extensible[]){ + { .a = 1 }, + { .a = 2 }, /* not read */ + { .a = 3 }, + }, + .input_len = 4 * sizeof(int), + .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) { + .a = 1, + .b = 1, + .c = 0, /* BUG in clang, should be 3 */ + }, + .output_len = sizeof(struct core_reloc_misc_output), + }, }; struct data { diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c new file mode 100644 index 000000000000..ed9ad8b5b4f8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c @@ -0,0 +1,5 @@ +#include "core_reloc_types.h" + +void f1(struct core_reloc_misc___a x) {} +void f2(struct core_reloc_misc___b x) {} +void f3(struct core_reloc_misc_extensible x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 5f3ebd4f6dc3..10a252b6da55 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -640,3 +640,28 @@ struct core_reloc_ints___err_wrong_sz_64 { uint32_t u64_field; /* not 64-bit anymore */ int32_t s64_field; /* not 64-bit anymore */ }; + +/* + * MISC + */ +struct core_reloc_misc_output { + int a, b, c; +}; + +struct core_reloc_misc___a { + int a1; + int a2; +}; + +struct core_reloc_misc___b { + int b1; + int b2; +}; + +/* this one extends core_reloc_misc_extensible struct from BPF prog */ +struct core_reloc_misc_extensible { + int a; + int b; + int c; + int d; +}; diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_misc.c b/tools/testing/selftests/bpf/progs/test_core_reloc_misc.c new file mode 100644 index 000000000000..2665df538b2d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_misc.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook + +#include +#include +#include "bpf_helpers.h" + +char _license[] SEC("license") = "GPL"; + +static volatile struct data { + char in[256]; + char out[256]; +} data; + +struct core_reloc_misc_output { + int a, b, c; +}; + +struct core_reloc_misc___a { + int a1; + int a2; +}; + +struct core_reloc_misc___b { + int b1; + int b2; +}; + +/* fixed two first members, can be extended with new fields */ +struct core_reloc_misc_extensible { + int a; + int b; +}; + +SEC("raw_tracepoint/sys_enter") +int test_core_misc(void *ctx) +{ + struct core_reloc_misc___a *in_a = (void *)&data.in; + struct core_reloc_misc___b *in_b = (void *)&data.in; + struct core_reloc_misc_extensible *in_ext = (void *)&data.in; + struct core_reloc_misc_output *out = (void *)&data.out; + + /* record two different relocations with the same accessor string */ + if (BPF_CORE_READ(&out->a, &in_a->a1) || /* accessor: 0:0 */ + BPF_CORE_READ(&out->b, &in_b->b1)) /* accessor: 0:0 */ + return 1; + + /* Validate relocations capture array-only accesses for structs with + * fixed header, but with potentially extendable tail. This will read + * first 4 bytes of 2nd element of in_ext array of potentially + * variably sized struct core_reloc_misc_extensible. */ + if (BPF_CORE_READ(&out->c, &in_ext[2])) /* accessor: 2 */ + return 1; + + return 0; +} -- Gitee From 8d112e1b4c79cc4123428c69cf75bdcd916144aa Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 8 Oct 2019 16:10:06 -0700 Subject: [PATCH 33/43] libbpf: Fix struct end padding in btf_dump ANBZ: #5530 commit b4099769f3321a8d258a47a8b4b9d278dad28a73 upstream. Fix a case where explicit padding at the end of a struct is necessary due to non-standart alignment requirements of fields (which BTF doesn't capture explicitly). Fixes: 351131b51c7a ("libbpf: add btf_dump API for BTF-to-C conversion") Reported-by: John Fastabend Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Tested-by: John Fastabend Link: https://lore.kernel.org/bpf/20191008231009.2991130-2-andriin@fb.com --- tools/lib/bpf/btf_dump.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 715967762312..115d973583c4 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -795,7 +795,6 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, __u16 vlen = btf_vlen(t); packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0; - align = packed ? 1 : btf_align_of(d->btf, id); btf_dump_printf(d, "%s%s%s {", is_struct ? "struct" : "union", @@ -825,6 +824,13 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, btf_dump_printf(d, ";"); } + /* pad at the end, if necessary */ + if (is_struct) { + align = packed ? 1 : btf_align_of(d->btf, id); + btf_dump_emit_bit_padding(d, off, t->size * 8, 0, align, + lvl + 1); + } + if (vlen) btf_dump_printf(d, "\n"); btf_dump_printf(d, "%s}", pfx(lvl)); -- Gitee From 9578f4a7606b5e0b22c029336357d350e951a9c4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 24 Jan 2020 12:18:46 -0800 Subject: [PATCH 34/43] libbpf: Fix realloc usage in bpf_core_find_cands ANBZ: #5530 commit 35b9211c0a2427e8f39e534f442f43804fc8d5ca upstream. Fix bug requesting invalid size of reallocated array when constructing CO-RE relocation candidate list. This can cause problems if there are many potential candidates and a very fine-grained memory allocator bucket sizes are used. Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm") Reported-by: William Smith Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20200124201847.212528-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a83be6c939ac..d7bf6f28c1dc 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2573,7 +2573,9 @@ static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf, if (strncmp(local_name, targ_name, local_essent_len) == 0) { pr_debug("[%d] %s: found candidate [%d] %s\n", local_type_id, local_name, i, targ_name); - new_ids = realloc(cand_ids->data, cand_ids->len + 1); + new_ids = reallocarray(cand_ids->data, + cand_ids->len + 1, + sizeof(*cand_ids->data)); if (!new_ids) { err = -ENOMEM; goto err_out; -- Gitee From b293dc70162f070c94195bfa1cff88cec198ccf2 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Mar 2020 10:23:34 -0700 Subject: [PATCH 35/43] libbpf: Ignore incompatible types with matching name during CO-RE relocation ANBZ: #5530 commit d121e1d34b72c4975ff0340901d926c0aaf98174 upstream. When finding target type candidates, ignore forward declarations, functions, and other named types of incompatible kind. Not doing this can cause false errors. See [0] for one such case (due to struct pt_regs forward declaration). [0] https://github.com/iovisor/bcc/pull/2806#issuecomment-598543645 Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm") Reported-by: Wenbo Zhang Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20200313172336.1879637-3-andriin@fb.com --- tools/lib/bpf/libbpf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index d7bf6f28c1dc..7d84b35e3514 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2566,6 +2566,10 @@ static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf, if (str_is_empty(targ_name)) continue; + t = skip_mods_and_typedefs(targ_btf, i, NULL); + if (!btf_is_composite(t) && !btf_is_array(t)) + continue; + targ_essent_len = bpf_core_essential_name_len(targ_name); if (targ_essent_len != local_essent_len) continue; -- Gitee From 8af520db7ef4280d13e3ace4997594d56499c4cc Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 9 Jun 2020 22:23:35 -0700 Subject: [PATCH 36/43] libbpf: Handle GCC noreturn-turned-volatile quirk ANBZ: #5530 commit 32022fd97ed34f6812802bf1288db27c313576f4 upstream. Handle a GCC quirk of emitting extra volatile modifier in DWARF (and subsequently preserved in BTF by pahole) for function pointers marked as __attribute__((noreturn)). This was the way to mark such functions before GCC 2.5 added noreturn attribute. Drop such func_proto modifiers, similarly to how it's done for array (also to handle GCC quirk/bug). Such volatile attribute is emitted by GCC only, so existing selftests can't express such test. Simple repro is like this (compiled with GCC + BTF generated by pahole): struct my_struct { void __attribute__((noreturn)) (*fn)(int); }; struct my_struct a; Without this fix, output will be: struct my_struct { voidvolatile (*fn)(int); }; With the fix: struct my_struct { void (*fn)(int); }; Fixes: 351131b51c7a ("libbpf: add btf_dump API for BTF-to-C conversion") Reported-by: Jean-Philippe Brucker Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Tested-by: Jean-Philippe Brucker Link: https://lore.kernel.org/bpf/20200610052335.2862559-1-andriin@fb.com --- tools/lib/bpf/btf_dump.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 115d973583c4..696acd03af81 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -1060,6 +1060,20 @@ static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack) } } +static void btf_dump_drop_mods(struct btf_dump *d, struct id_stack *decl_stack) +{ + const struct btf_type *t; + __u32 id; + + while (decl_stack->cnt) { + id = decl_stack->ids[decl_stack->cnt - 1]; + t = btf__type_by_id(d->btf, id); + if (!btf_is_mod(t)) + return; + decl_stack->cnt--; + } +} + static void btf_dump_emit_name(const struct btf_dump *d, const char *name, bool last_was_ptr) { @@ -1158,14 +1172,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, * a const/volatile modifier for array, so we are * going to silently skip them here. */ - while (decls->cnt) { - next_id = decls->ids[decls->cnt - 1]; - next_t = btf__type_by_id(d->btf, next_id); - if (btf_is_mod(next_t)) - decls->cnt--; - else - break; - } + btf_dump_drop_mods(d, decls); if (decls->cnt == 0) { btf_dump_emit_name(d, fname, last_was_ptr); @@ -1192,7 +1199,15 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, __u16 vlen = btf_vlen(t); int i; - btf_dump_emit_mods(d, decls); + /* + * GCC emits extra volatile qualifier for + * __attribute__((noreturn)) function pointers. Clang + * doesn't do it. It's a GCC quirk for backwards + * compatibility with code written for GCC <2.5. So, + * similarly to extra qualifiers for array, just drop + * them, instead of handling them. + */ + btf_dump_drop_mods(d, decls); if (decls->cnt) { btf_dump_printf(d, " ("); btf_dump_emit_type_chain(d, decls, fname, lvl); -- Gitee From 786c2317b8236cfb39fe04dde8cc2fcc1194cc50 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 19 Jun 2020 16:04:22 -0700 Subject: [PATCH 37/43] libbpf: Fix CO-RE relocs against .text section ANBZ: #5530 commit 9c82a63cf3701b78cd092c69c3e75ff806837194 upstream. bpf_object__find_program_by_title(), used by CO-RE relocation code, doesn't return .text "BPF program", if it is a function storage for sub-programs. Because of that, any CO-RE relocation in helper non-inlined functions will fail. Fix this by searching for .text-corresponding BPF program manually. Adjust one of bpf_iter selftest to exhibit this pattern. [BackportNotes] Discard modifications in tools/testing/selftests/bpf/progs/bpf_iter_netlink.c since it do not exists. Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm") Reported-by: Yonghong Song Signed-off-by: Andrii Nakryiko Signed-off-by: Yonghong Song Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20200619230423.691274-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 7d84b35e3514..29bd99d69c84 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -3134,7 +3134,13 @@ bpf_core_reloc_offsets(struct bpf_object *obj, const char *targ_btf_path) err = -EINVAL; goto out; } - prog = bpf_object__find_program_by_title(obj, sec_name); + prog = NULL; + for (i = 0; i < obj->nr_programs; i++) { + if (!strcmp(obj->programs[i].section_name, sec_name)) { + prog = &obj->programs[i]; + break; + } + } if (!prog) { pr_warning("failed to find program '%s' for CO-RE offset relocation\n", sec_name); -- Gitee From c89f0df61dbf711cfffb9aed8bc935a111cabae4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 21 Aug 2020 15:55:56 -0700 Subject: [PATCH 38/43] libbpf: Avoid false unuinitialized variable warning in bpf_core_apply_relo ANBZ: #5530 commit 3418c56de81fd73f2265c8915f4b910bcc141cb7 upstream. Some versions of GCC report uninitialized targ_spec usage. GCC is wrong, but let's avoid unnecessary warnings. Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm") Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200821225556.2178419-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 29bd99d69c84..7e084f1ac35b 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2997,7 +2997,7 @@ static int bpf_core_reloc_offset(struct bpf_program *prog, struct hashmap *cand_cache) { const char *prog_name = bpf_program__title(prog, false); - struct bpf_core_spec local_spec, cand_spec, targ_spec; + struct bpf_core_spec local_spec, cand_spec, targ_spec = {}; const void *type_key = u32_as_hash_key(relo->type_id); const struct btf_type *local_type, *cand_type; const char *local_name, *cand_name; -- Gitee From fac5dcf29d2269483271bbea493c28ae4e1a64b5 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Fri, 19 Mar 2021 12:25:54 +0100 Subject: [PATCH 39/43] libbpf: Fix BTF dump of pointer-to-array-of-struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #5530 commit 901ee1d750f29a335423eeb9463c3ca461ca18c2 upstream. The vmlinux.h generated from BTF is invalid when building drivers/phy/ti/phy-gmii-sel.c with clang: vmlinux.h:61702:27: error: array type has incomplete element type ‘struct reg_field’ 61702 | const struct reg_field (*regfields)[3]; | ^~~~~~~~~ bpftool generates a forward declaration for this struct regfield, which compilers aren't happy about. Here's a simplified reproducer: struct inner { int val; }; struct outer { struct inner (*ptr_to_array)[2]; } A; After build with clang -> bpftool btf dump c -> clang/gcc: ./def-clang.h:11:23: error: array has incomplete element type 'struct inner' struct inner (*ptr_to_array)[2]; Member ptr_to_array of struct outer is a pointer to an array of struct inner. In the DWARF generated by clang, struct outer appears before struct inner, so when converting BTF of struct outer into C, bpftool issues a forward declaration to struct inner. With GCC the DWARF info is reversed so struct inner gets fully defined. That forward declaration is not sufficient when compilers handle an array of the struct, even when it's only used through a pointer. Note that we can trigger the same issue with an intermediate typedef: struct inner { int val; }; typedef struct inner inner2_t[2]; struct outer { inner2_t *ptr_to_array; } A; Becomes: struct inner; typedef struct inner inner2_t[2]; And causes: ./def-clang.h:10:30: error: array has incomplete element type 'struct inner' typedef struct inner inner2_t[2]; To fix this, clear through_ptr whenever we encounter an intermediate array, to make the inner struct part of a strong link and force full declaration. Fixes: 351131b51c7a ("libbpf: add btf_dump API for BTF-to-C conversion") Signed-off-by: Jean-Philippe Brucker Signed-off-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20210319112554.794552-2-jean-philippe@linaro.org --- tools/lib/bpf/btf_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 696acd03af81..202fb48ed97f 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -362,7 +362,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) return err; case BTF_KIND_ARRAY: - return btf_dump_order_type(d, btf_array(t)->type, through_ptr); + return btf_dump_order_type(d, btf_array(t)->type, false); case BTF_KIND_STRUCT: case BTF_KIND_UNION: { -- Gitee From 40782972198b5663b6fdfd7a45d4b44b55c69745 Mon Sep 17 00:00:00 2001 From: Xu Kuohai Date: Tue, 1 Mar 2022 00:32:49 -0500 Subject: [PATCH 40/43] libbpf: Skip forward declaration when counting duplicated type names ANBZ: #5530 commit 4226961b0019b2e1612029e8950a9e911affc995 upstream. Currently if a declaration appears in the BTF before the definition, the definition is dumped as a conflicting name, e.g.: $ bpftool btf dump file vmlinux format raw | grep "'unix_sock'" [81287] FWD 'unix_sock' fwd_kind=struct [89336] STRUCT 'unix_sock' size=1024 vlen=14 $ bpftool btf dump file vmlinux format c | grep "struct unix_sock" struct unix_sock; struct unix_sock___2 { <--- conflict, the "___2" is unexpected struct unix_sock___2 *unix_sk; This causes a compilation error if the dump output is used as a header file. Fix it by skipping declaration when counting duplicated type names. Fixes: 351131b51c7a ("libbpf: add btf_dump API for BTF-to-C conversion") Signed-off-by: Xu Kuohai Signed-off-by: Daniel Borkmann Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20220301053250.1464204-2-xukuohai@huawei.com --- tools/lib/bpf/btf_dump.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 202fb48ed97f..2cb4e297cc9f 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -1284,6 +1284,11 @@ static const char *btf_dump_resolve_name(struct btf_dump *d, __u32 id, if (s->name_resolved) return *cached_name ? *cached_name : orig_name; + if (btf_is_fwd(t) || (btf_is_enum(t) && btf_vlen(t) == 0)) { + s->name_resolved = 1; + return orig_name; + } + dup_cnt = btf_dump_name_dups(d, name_map, orig_name); if (dup_cnt > 1) { const size_t max_len = 256; -- Gitee From 947b9944986bf8d2d6370250d9f787612268f657 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 25 Sep 2019 11:30:38 -0700 Subject: [PATCH 41/43] libbpf: fix false uninitialized variable warning ANBZ: #5530 commit aef70a1f44c0b570e6345c02c2d240471859f0a4 upstream. Some compilers emit warning for potential uninitialized next_id usage. The code is correct, but control flow is too complicated for some compilers to figure this out. Re-initialize next_id to satisfy compiler. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann --- tools/lib/bpf/btf_dump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 2cb4e297cc9f..fc236a8b757c 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -1180,6 +1180,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d, return; } + next_id = decls->ids[decls->cnt - 1]; next_t = btf__type_by_id(d->btf, next_id); multidim = btf_is_array(next_t); /* we need space if we have named non-pointer */ -- Gitee From baaddf73859c18f9c6352349c23957d952319d25 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Wed, 7 Aug 2019 17:38:56 -0700 Subject: [PATCH 42/43] tools/bpf: fix core_reloc.c compilation error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #5530 commit b707659213d3c70f2c704ec950df6263b4bffe84 upstream. On my local machine, I have the following compilation errors: ===== In file included from prog_tests/core_reloc.c:3:0: ./progs/core_reloc_types.h:517:46: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘fancy_char_ptr_t’ typedef const char * const volatile restrict fancy_char_ptr_t; ^ ./progs/core_reloc_types.h:527:2: error: unknown type name ‘fancy_char_ptr_t’ fancy_char_ptr_t d; ^ ===== I am using gcc 4.8.5. Later compilers may change their behavior not emitting the error. Nevertheless, let us fix the issue. "restrict" can be tested without typedef. Fixes: 9654e2ae908e ("selftests/bpf: add CO-RE relocs modifiers/typedef tests") Cc: Andrii Nakryiko Signed-off-by: Yonghong Song Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/progs/core_reloc_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 10a252b6da55..f686a8138d90 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -514,7 +514,7 @@ typedef arr1_t arr2_t; typedef arr2_t arr3_t; typedef arr3_t arr4_t; -typedef const char * const volatile restrict fancy_char_ptr_t; +typedef const char * const volatile fancy_char_ptr_t; typedef core_reloc_mods_substruct_t core_reloc_mods_substruct_tt; -- Gitee From d3e38438deb49ffde52bf39272793ee524667fa7 Mon Sep 17 00:00:00 2001 From: Qiao Ma Date: Mon, 29 Jan 2024 20:44:22 +0800 Subject: [PATCH 43/43] anolis: selftests/bpf: merge core_reloc.c into test_progs.c ANBZ: #5530 The testcase inside test_progs is not broken up into prog_test/ now because of the commit is missing: 3f30658830f3 ("selftests: bpf: break up test_progs - preparation") To make testcases of core_reloc.c work, merge them into test_progs.c. Signed-off-by: Qiao Ma --- .../selftests/bpf/prog_tests/core_reloc.c | 385 ------------------ tools/testing/selftests/bpf/test_progs.c | 382 +++++++++++++++++ 2 files changed, 382 insertions(+), 385 deletions(-) delete mode 100644 tools/testing/selftests/bpf/prog_tests/core_reloc.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c deleted file mode 100644 index 7d775046fcd1..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ /dev/null @@ -1,385 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include "progs/core_reloc_types.h" - -#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name) - -#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ - .a = 42, \ - .b = 0xc001, \ - .c = 0xbeef, \ -} - -#define FLAVORS_CASE_COMMON(name) \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_flavors.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o" \ - -#define FLAVORS_CASE(name) { \ - FLAVORS_CASE_COMMON(name), \ - .input = FLAVORS_DATA(core_reloc_##name), \ - .input_len = sizeof(struct core_reloc_##name), \ - .output = FLAVORS_DATA(core_reloc_flavors), \ - .output_len = sizeof(struct core_reloc_flavors), \ -} - -#define FLAVORS_ERR_CASE(name) { \ - FLAVORS_CASE_COMMON(name), \ - .fails = true, \ -} - -#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ - .a = { .a = { .a = 42 } }, \ - .b = { .b = { .b = 0xc001 } }, \ -} - -#define NESTING_CASE_COMMON(name) \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_nesting.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o" - -#define NESTING_CASE(name) { \ - NESTING_CASE_COMMON(name), \ - .input = NESTING_DATA(core_reloc_##name), \ - .input_len = sizeof(struct core_reloc_##name), \ - .output = NESTING_DATA(core_reloc_nesting), \ - .output_len = sizeof(struct core_reloc_nesting) \ -} - -#define NESTING_ERR_CASE(name) { \ - NESTING_CASE_COMMON(name), \ - .fails = true, \ -} - -#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ - .a = { [2] = 1 }, \ - .b = { [1] = { [2] = { [3] = 2 } } }, \ - .c = { [1] = { .c = 3 } }, \ - .d = { [0] = { [0] = { .d = 4 } } }, \ -} - -#define ARRAYS_CASE_COMMON(name) \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_arrays.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o" - -#define ARRAYS_CASE(name) { \ - ARRAYS_CASE_COMMON(name), \ - .input = ARRAYS_DATA(core_reloc_##name), \ - .input_len = sizeof(struct core_reloc_##name), \ - .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \ - .a2 = 1, \ - .b123 = 2, \ - .c1c = 3, \ - .d00d = 4, \ - }, \ - .output_len = sizeof(struct core_reloc_arrays_output) \ -} - -#define ARRAYS_ERR_CASE(name) { \ - ARRAYS_CASE_COMMON(name), \ - .fails = true, \ -} - -#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ - .a = 1, \ - .b = 2, \ - .c = 3, \ - .d = (void *)4, \ - .f = (void *)5, \ -} - -#define PRIMITIVES_CASE_COMMON(name) \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_primitives.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o" - -#define PRIMITIVES_CASE(name) { \ - PRIMITIVES_CASE_COMMON(name), \ - .input = PRIMITIVES_DATA(core_reloc_##name), \ - .input_len = sizeof(struct core_reloc_##name), \ - .output = PRIMITIVES_DATA(core_reloc_primitives), \ - .output_len = sizeof(struct core_reloc_primitives), \ -} - -#define PRIMITIVES_ERR_CASE(name) { \ - PRIMITIVES_CASE_COMMON(name), \ - .fails = true, \ -} - -#define MODS_CASE(name) { \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_mods.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o", \ - .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \ - .a = 1, \ - .b = 2, \ - .c = (void *)3, \ - .d = (void *)4, \ - .e = { [2] = 5 }, \ - .f = { [1] = 6 }, \ - .g = { .x = 7 }, \ - .h = { .y = 8 }, \ - }, \ - .input_len = sizeof(struct core_reloc_##name), \ - .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \ - .a = 1, .b = 2, .c = 3, .d = 4, \ - .e = 5, .f = 6, .g = 7, .h = 8, \ - }, \ - .output_len = sizeof(struct core_reloc_mods_output), \ -} - -#define PTR_AS_ARR_CASE(name) { \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o", \ - .input = (const char *)&(struct core_reloc_##name []){ \ - { .a = 1 }, \ - { .a = 2 }, \ - { .a = 3 }, \ - }, \ - .input_len = 3 * sizeof(struct core_reloc_##name), \ - .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \ - .a = 3, \ - }, \ - .output_len = sizeof(struct core_reloc_ptr_as_arr), \ -} - -#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ - .u8_field = 1, \ - .s8_field = 2, \ - .u16_field = 3, \ - .s16_field = 4, \ - .u32_field = 5, \ - .s32_field = 6, \ - .u64_field = 7, \ - .s64_field = 8, \ -} - -#define INTS_CASE_COMMON(name) \ - .case_name = #name, \ - .bpf_obj_file = "test_core_reloc_ints.o", \ - .btf_src_file = "btf__core_reloc_" #name ".o" - -#define INTS_CASE(name) { \ - INTS_CASE_COMMON(name), \ - .input = INTS_DATA(core_reloc_##name), \ - .input_len = sizeof(struct core_reloc_##name), \ - .output = INTS_DATA(core_reloc_ints), \ - .output_len = sizeof(struct core_reloc_ints), \ -} - -#define INTS_ERR_CASE(name) { \ - INTS_CASE_COMMON(name), \ - .fails = true, \ -} - -struct core_reloc_test_case { - const char *case_name; - const char *bpf_obj_file; - const char *btf_src_file; - const char *input; - int input_len; - const char *output; - int output_len; - bool fails; -}; - -static struct core_reloc_test_case test_cases[] = { - /* validate we can find kernel image and use its BTF for relocs */ - { - .case_name = "kernel", - .bpf_obj_file = "test_core_reloc_kernel.o", - .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */ - .input = "", - .input_len = 0, - .output = "\1", /* true */ - .output_len = 1, - }, - - /* validate BPF program can use multiple flavors to match against - * single target BTF type - */ - FLAVORS_CASE(flavors), - - FLAVORS_ERR_CASE(flavors__err_wrong_name), - - /* various struct/enum nesting and resolution scenarios */ - NESTING_CASE(nesting), - NESTING_CASE(nesting___anon_embed), - NESTING_CASE(nesting___struct_union_mixup), - NESTING_CASE(nesting___extra_nesting), - NESTING_CASE(nesting___dup_compat_types), - - NESTING_ERR_CASE(nesting___err_missing_field), - NESTING_ERR_CASE(nesting___err_array_field), - NESTING_ERR_CASE(nesting___err_missing_container), - NESTING_ERR_CASE(nesting___err_nonstruct_container), - NESTING_ERR_CASE(nesting___err_array_container), - NESTING_ERR_CASE(nesting___err_dup_incompat_types), - NESTING_ERR_CASE(nesting___err_partial_match_dups), - NESTING_ERR_CASE(nesting___err_too_deep), - - /* various array access relocation scenarios */ - ARRAYS_CASE(arrays), - ARRAYS_CASE(arrays___diff_arr_dim), - ARRAYS_CASE(arrays___diff_arr_val_sz), - - ARRAYS_ERR_CASE(arrays___err_too_small), - ARRAYS_ERR_CASE(arrays___err_too_shallow), - ARRAYS_ERR_CASE(arrays___err_non_array), - ARRAYS_ERR_CASE(arrays___err_wrong_val_type1), - ARRAYS_ERR_CASE(arrays___err_wrong_val_type2), - - /* enum/ptr/int handling scenarios */ - PRIMITIVES_CASE(primitives), - PRIMITIVES_CASE(primitives___diff_enum_def), - PRIMITIVES_CASE(primitives___diff_func_proto), - PRIMITIVES_CASE(primitives___diff_ptr_type), - - PRIMITIVES_ERR_CASE(primitives___err_non_enum), - PRIMITIVES_ERR_CASE(primitives___err_non_int), - PRIMITIVES_ERR_CASE(primitives___err_non_ptr), - - /* const/volatile/restrict and typedefs scenarios */ - MODS_CASE(mods), - MODS_CASE(mods___mod_swap), - MODS_CASE(mods___typedefs), - - /* handling "ptr is an array" semantics */ - PTR_AS_ARR_CASE(ptr_as_arr), - PTR_AS_ARR_CASE(ptr_as_arr___diff_sz), - - /* int signedness/sizing/bitfield handling */ - INTS_CASE(ints), - INTS_CASE(ints___bool), - INTS_CASE(ints___reverse_sign), - - INTS_ERR_CASE(ints___err_bitfield), - INTS_ERR_CASE(ints___err_wrong_sz_8), - INTS_ERR_CASE(ints___err_wrong_sz_16), - INTS_ERR_CASE(ints___err_wrong_sz_32), - INTS_ERR_CASE(ints___err_wrong_sz_64), - - /* validate edge cases of capturing relocations */ - { - .case_name = "misc", - .bpf_obj_file = "test_core_reloc_misc.o", - .btf_src_file = "btf__core_reloc_misc.o", - .input = (const char *)&(struct core_reloc_misc_extensible[]){ - { .a = 1 }, - { .a = 2 }, /* not read */ - { .a = 3 }, - }, - .input_len = 4 * sizeof(int), - .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) { - .a = 1, - .b = 1, - .c = 0, /* BUG in clang, should be 3 */ - }, - .output_len = sizeof(struct core_reloc_misc_output), - }, -}; - -struct data { - char in[256]; - char out[256]; -}; - -void test_core_reloc(void) -{ - const char *probe_name = "raw_tracepoint/sys_enter"; - struct bpf_object_load_attr load_attr = {}; - struct core_reloc_test_case *test_case; - int err, duration = 0, i, equal; - struct bpf_link *link = NULL; - struct bpf_map *data_map; - struct bpf_program *prog; - struct bpf_object *obj; - const int zero = 0; - struct data data; - - for (i = 0; i < ARRAY_SIZE(test_cases); i++) { - test_case = &test_cases[i]; - - if (!test__start_subtest(test_case->case_name)) - continue; - - obj = bpf_object__open(test_case->bpf_obj_file); - if (CHECK(IS_ERR_OR_NULL(obj), "obj_open", - "failed to open '%s': %ld\n", - test_case->bpf_obj_file, PTR_ERR(obj))) - continue; - - prog = bpf_object__find_program_by_title(obj, probe_name); - if (CHECK(!prog, "find_probe", - "prog '%s' not found\n", probe_name)) - goto cleanup; - bpf_program__set_type(prog, BPF_PROG_TYPE_RAW_TRACEPOINT); - - load_attr.obj = obj; - load_attr.log_level = 0; - load_attr.target_btf_path = test_case->btf_src_file; - err = bpf_object__load_xattr(&load_attr); - if (test_case->fails) { - CHECK(!err, "obj_load_fail", - "should fail to load prog '%s'\n", probe_name); - goto cleanup; - } else { - if (CHECK(err, "obj_load", - "failed to load prog '%s': %d\n", - probe_name, err)) - goto cleanup; - } - - link = bpf_program__attach_raw_tracepoint(prog, "sys_enter"); - if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", - PTR_ERR(link))) - goto cleanup; - - data_map = bpf_object__find_map_by_name(obj, "test_cor.bss"); - if (CHECK(!data_map, "find_data_map", "data map not found\n")) - goto cleanup; - - memset(&data, 0, sizeof(data)); - memcpy(data.in, test_case->input, test_case->input_len); - - err = bpf_map_update_elem(bpf_map__fd(data_map), - &zero, &data, 0); - if (CHECK(err, "update_data_map", - "failed to update .data map: %d\n", err)) - goto cleanup; - - /* trigger test run */ - usleep(1); - - err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &data); - if (CHECK(err, "get_result", - "failed to get output data: %d\n", err)) - goto cleanup; - - equal = memcmp(data.out, test_case->output, - test_case->output_len) == 0; - if (CHECK(!equal, "check_result", - "input/output data don't match\n")) { - int j; - - for (j = 0; j < test_case->input_len; j++) { - printf("input byte #%d: 0x%02hhx\n", - j, test_case->input[j]); - } - for (j = 0; j < test_case->output_len; j++) { - printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n", - j, test_case->output[j], data.out[j]); - } - goto cleanup; - } - -cleanup: - if (!IS_ERR_OR_NULL(link)) { - bpf_link__destroy(link); - link = NULL; - } - bpf_object__close(obj); - } -} diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 6bb6d4ca3e16..0eb70d9245f5 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -3318,6 +3318,387 @@ static void test_perf_buffer(void) bpf_object__close(obj); } +#include "progs/core_reloc_types.h" + +#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name) + +#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = 42, \ + .b = 0xc001, \ + .c = 0xbeef, \ +} + +#define FLAVORS_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_flavors.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" \ + +#define FLAVORS_CASE(name) { \ + FLAVORS_CASE_COMMON(name), \ + .input = FLAVORS_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = FLAVORS_DATA(core_reloc_flavors), \ + .output_len = sizeof(struct core_reloc_flavors), \ +} + +#define FLAVORS_ERR_CASE(name) { \ + FLAVORS_CASE_COMMON(name), \ + .fails = true, \ +} + +#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = { .a = { .a = 42 } }, \ + .b = { .b = { .b = 0xc001 } }, \ +} + +#define NESTING_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_nesting.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define NESTING_CASE(name) { \ + NESTING_CASE_COMMON(name), \ + .input = NESTING_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = NESTING_DATA(core_reloc_nesting), \ + .output_len = sizeof(struct core_reloc_nesting) \ +} + +#define NESTING_ERR_CASE(name) { \ + NESTING_CASE_COMMON(name), \ + .fails = true, \ +} + +#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = { [2] = 1 }, \ + .b = { [1] = { [2] = { [3] = 2 } } }, \ + .c = { [1] = { .c = 3 } }, \ + .d = { [0] = { [0] = { .d = 4 } } }, \ +} + +#define ARRAYS_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_arrays.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define ARRAYS_CASE(name) { \ + ARRAYS_CASE_COMMON(name), \ + .input = ARRAYS_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \ + .a2 = 1, \ + .b123 = 2, \ + .c1c = 3, \ + .d00d = 4, \ + }, \ + .output_len = sizeof(struct core_reloc_arrays_output) \ +} + +#define ARRAYS_ERR_CASE(name) { \ + ARRAYS_CASE_COMMON(name), \ + .fails = true, \ +} + +#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .a = 1, \ + .b = 2, \ + .c = 3, \ + .d = (void *)4, \ + .f = (void *)5, \ +} + +#define PRIMITIVES_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_primitives.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define PRIMITIVES_CASE(name) { \ + PRIMITIVES_CASE_COMMON(name), \ + .input = PRIMITIVES_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = PRIMITIVES_DATA(core_reloc_primitives), \ + .output_len = sizeof(struct core_reloc_primitives), \ +} + +#define PRIMITIVES_ERR_CASE(name) { \ + PRIMITIVES_CASE_COMMON(name), \ + .fails = true, \ +} + +#define MODS_CASE(name) { \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_mods.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o", \ + .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \ + .a = 1, \ + .b = 2, \ + .c = (void *)3, \ + .d = (void *)4, \ + .e = { [2] = 5 }, \ + .f = { [1] = 6 }, \ + .g = { .x = 7 }, \ + .h = { .y = 8 }, \ + }, \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \ + .a = 1, .b = 2, .c = 3, .d = 4, \ + .e = 5, .f = 6, .g = 7, .h = 8, \ + }, \ + .output_len = sizeof(struct core_reloc_mods_output), \ +} + +#define PTR_AS_ARR_CASE(name) { \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o", \ + .input = (const char *)&(struct core_reloc_##name []){ \ + { .a = 1 }, \ + { .a = 2 }, \ + { .a = 3 }, \ + }, \ + .input_len = 3 * sizeof(struct core_reloc_##name), \ + .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \ + .a = 3, \ + }, \ + .output_len = sizeof(struct core_reloc_ptr_as_arr), \ +} + +#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ + .u8_field = 1, \ + .s8_field = 2, \ + .u16_field = 3, \ + .s16_field = 4, \ + .u32_field = 5, \ + .s32_field = 6, \ + .u64_field = 7, \ + .s64_field = 8, \ +} + +#define INTS_CASE_COMMON(name) \ + .case_name = #name, \ + .bpf_obj_file = "test_core_reloc_ints.o", \ + .btf_src_file = "btf__core_reloc_" #name ".o" + +#define INTS_CASE(name) { \ + INTS_CASE_COMMON(name), \ + .input = INTS_DATA(core_reloc_##name), \ + .input_len = sizeof(struct core_reloc_##name), \ + .output = INTS_DATA(core_reloc_ints), \ + .output_len = sizeof(struct core_reloc_ints), \ +} + +#define INTS_ERR_CASE(name) { \ + INTS_CASE_COMMON(name), \ + .fails = true, \ +} + +struct core_reloc_test_case { + const char *case_name; + const char *bpf_obj_file; + const char *btf_src_file; + const char *input; + int input_len; + const char *output; + int output_len; + bool fails; +}; + +static struct core_reloc_test_case test_cases[] = { + /* validate we can find kernel image and use its BTF for relocs */ + { + .case_name = "kernel", + .bpf_obj_file = "test_core_reloc_kernel.o", + .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */ + .input = "", + .input_len = 0, + .output = "\1", /* true */ + .output_len = 1, + }, + + /* validate BPF program can use multiple flavors to match against + * single target BTF type + */ + FLAVORS_CASE(flavors), + + FLAVORS_ERR_CASE(flavors__err_wrong_name), + + /* various struct/enum nesting and resolution scenarios */ + NESTING_CASE(nesting), + NESTING_CASE(nesting___anon_embed), + NESTING_CASE(nesting___struct_union_mixup), + NESTING_CASE(nesting___extra_nesting), + NESTING_CASE(nesting___dup_compat_types), + + NESTING_ERR_CASE(nesting___err_missing_field), + NESTING_ERR_CASE(nesting___err_array_field), + NESTING_ERR_CASE(nesting___err_missing_container), + NESTING_ERR_CASE(nesting___err_nonstruct_container), + NESTING_ERR_CASE(nesting___err_array_container), + NESTING_ERR_CASE(nesting___err_dup_incompat_types), + NESTING_ERR_CASE(nesting___err_partial_match_dups), + NESTING_ERR_CASE(nesting___err_too_deep), + + /* various array access relocation scenarios */ + ARRAYS_CASE(arrays), + ARRAYS_CASE(arrays___diff_arr_dim), + ARRAYS_CASE(arrays___diff_arr_val_sz), + + ARRAYS_ERR_CASE(arrays___err_too_small), + ARRAYS_ERR_CASE(arrays___err_too_shallow), + ARRAYS_ERR_CASE(arrays___err_non_array), + ARRAYS_ERR_CASE(arrays___err_wrong_val_type1), + ARRAYS_ERR_CASE(arrays___err_wrong_val_type2), + + /* enum/ptr/int handling scenarios */ + PRIMITIVES_CASE(primitives), + PRIMITIVES_CASE(primitives___diff_enum_def), + PRIMITIVES_CASE(primitives___diff_func_proto), + PRIMITIVES_CASE(primitives___diff_ptr_type), + + PRIMITIVES_ERR_CASE(primitives___err_non_enum), + PRIMITIVES_ERR_CASE(primitives___err_non_int), + PRIMITIVES_ERR_CASE(primitives___err_non_ptr), + + /* const/volatile/restrict and typedefs scenarios */ + MODS_CASE(mods), + MODS_CASE(mods___mod_swap), + MODS_CASE(mods___typedefs), + + /* handling "ptr is an array" semantics */ + PTR_AS_ARR_CASE(ptr_as_arr), + PTR_AS_ARR_CASE(ptr_as_arr___diff_sz), + + /* int signedness/sizing/bitfield handling */ + INTS_CASE(ints), + INTS_CASE(ints___bool), + INTS_CASE(ints___reverse_sign), + + INTS_ERR_CASE(ints___err_bitfield), + INTS_ERR_CASE(ints___err_wrong_sz_8), + INTS_ERR_CASE(ints___err_wrong_sz_16), + INTS_ERR_CASE(ints___err_wrong_sz_32), + INTS_ERR_CASE(ints___err_wrong_sz_64), + + /* validate edge cases of capturing relocations */ + { + .case_name = "misc", + .bpf_obj_file = "test_core_reloc_misc.o", + .btf_src_file = "btf__core_reloc_misc.o", + .input = (const char *)&(struct core_reloc_misc_extensible[]){ + { .a = 1 }, + { .a = 2 }, /* not read */ + { .a = 3 }, + }, + .input_len = 4 * sizeof(int), + .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) { + .a = 1, + .b = 1, + .c = 0, /* BUG in clang, should be 3 */ + }, + .output_len = sizeof(struct core_reloc_misc_output), + }, +}; + +struct data { + char in[256]; + char out[256]; +}; + +static void test_core_reloc(void) +{ + const char *probe_name = "raw_tracepoint/sys_enter"; + struct bpf_object_load_attr load_attr = {}; + struct core_reloc_test_case *test_case; + int err, duration = 0, i, equal; + struct bpf_link *link = NULL; + struct bpf_map *data_map; + struct bpf_program *prog; + struct bpf_object *obj; + const int zero = 0; + struct data data; + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + test_case = &test_cases[i]; + + obj = bpf_object__open(test_case->bpf_obj_file); + if (CHECK(IS_ERR_OR_NULL(obj), "obj_open", + "failed to open '%s': %ld\n", + test_case->bpf_obj_file, PTR_ERR(obj))) + continue; + + prog = bpf_object__find_program_by_title(obj, probe_name); + if (CHECK(!prog, "find_probe", + "prog '%s' not found\n", probe_name)) + goto cleanup; + bpf_program__set_type(prog, BPF_PROG_TYPE_RAW_TRACEPOINT); + + load_attr.obj = obj; + load_attr.log_level = 0; + load_attr.target_btf_path = test_case->btf_src_file; + err = bpf_object__load_xattr(&load_attr); + if (test_case->fails) { + CHECK(!err, "obj_load_fail", + "should fail to load prog '%s'\n", probe_name); + goto cleanup; + } else { + if (CHECK(err, "obj_load", + "failed to load prog '%s': %d\n", + probe_name, err)) + goto cleanup; + } + + link = bpf_program__attach_raw_tracepoint(prog, "sys_enter"); + if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", + PTR_ERR(link))) + goto cleanup; + + data_map = bpf_object__find_map_by_name(obj, "test_cor.bss"); + if (CHECK(!data_map, "find_data_map", "data map not found\n")) + goto cleanup; + + memset(&data, 0, sizeof(data)); + memcpy(data.in, test_case->input, test_case->input_len); + + err = bpf_map_update_elem(bpf_map__fd(data_map), + &zero, &data, 0); + if (CHECK(err, "update_data_map", + "failed to update .data map: %d\n", err)) + goto cleanup; + + /* trigger test run */ + usleep(1); + + err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &data); + if (CHECK(err, "get_result", + "failed to get output data: %d\n", err)) + goto cleanup; + + equal = memcmp(data.out, test_case->output, + test_case->output_len) == 0; + if (CHECK(!equal, "check_result", + "input/output data don't match\n")) { + int j; + + for (j = 0; j < test_case->input_len; j++) { + printf("input byte #%d: 0x%02hhx\n", + j, test_case->input[j]); + } + for (j = 0; j < test_case->output_len; j++) { + printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n", + j, test_case->output[j], data.out[j]); + } + goto cleanup; + } + +cleanup: + if (!IS_ERR_OR_NULL(link)) { + bpf_link__destroy(link); + link = NULL; + } + bpf_object__close(obj); + } +} + int main(int ac, char **av) { srand(time(NULL)); @@ -3360,6 +3741,7 @@ int main(int ac, char **av) test_raw_tp_writable_test_run(); test_send_signal(); test_perf_buffer(); + test_core_reloc(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; -- Gitee