bpf: fix verification errors

This commit is contained in:
Beniamino Galvani 2025-09-28 13:49:00 +02:00
parent d184c21994
commit efe324eda7

View file

@ -61,6 +61,24 @@ struct {
#define DBG(fmt, ...)
#endif
/* Macros to read the sk_buff data* pointers, preventing the compiler
* from generating a 32bit register access, which fails during verification.
* See https://github.com/cilium/cilium/pull/25336
*/
#define SKB_ACCESS_MEMBER_32(_skb, member) \
({ \
void *ptr; \
\
asm volatile("%0 = *(u32 *)(%1 + %2)" \
: "=r"(ptr) \
: "r"(_skb), "i"(offsetof(struct __sk_buff, member))); \
\
ptr; \
})
#define SKB_DATA(_skb) SKB_ACCESS_MEMBER_32(_skb, data)
#define SKB_DATA_END(_skb) SKB_ACCESS_MEMBER_32(_skb, data_end)
struct icmpv6_pseudo {
struct in6_addr saddr;
struct in6_addr daddr;
@ -76,24 +94,22 @@ update_l4_checksum(struct __sk_buff *skb,
int ip_type,
bool v4to6)
{
void *data = (void *) (unsigned long long) skb->data;
void *data = SKB_DATA(skb);
int flags = BPF_F_PSEUDO_HDR;
__u16 offset;
__u32 csum;
if (v4to6) {
csum = bpf_csum_diff((__be32 *) &iph->saddr,
2 * sizeof(__u32),
(__be32 *) &ip6h->saddr,
2 * sizeof(struct in6_addr),
0);
void *from_ptr = (__be32 *) &iph->saddr;
void *to_ptr = (__be32 *) &ip6h->saddr;
csum = bpf_csum_diff(from_ptr, 2 * sizeof(__u32), to_ptr, 2 * sizeof(struct in6_addr), 0);
offset = (void *) (iph + 1) - data;
} else {
csum = bpf_csum_diff((__be32 *) &ip6h->saddr,
2 * sizeof(struct in6_addr),
(__be32 *) &iph->saddr,
2 * sizeof(__u32),
0);
void *from_ptr = (__be32 *) &ip6h->saddr;
void *to_ptr = (__be32 *) &iph->saddr;
csum = bpf_csum_diff(from_ptr, 2 * sizeof(struct in6_addr), to_ptr, 2 * sizeof(__u32), 0);
offset = (void *) (ip6h + 1) - data;
}
@ -119,14 +135,14 @@ update_icmp_checksum(struct __sk_buff *skb,
void *icmp_after,
bool add)
{
void *data = (void *) (unsigned long long) skb->data;
struct icmpv6_pseudo ph = {.nh = IPPROTO_ICMPV6,
.saddr = ip6h->saddr,
.daddr = ip6h->daddr,
.len = ip6h->payload_len};
void *data = SKB_DATA(skb);
struct icmpv6_pseudo ph = {.nh = IPPROTO_ICMPV6, .len = ip6h->payload_len};
__u16 h_before, h_after, offset;
__u32 csum, u_before, u_after;
__builtin_memcpy(&ph.saddr, &ip6h->saddr, sizeof(struct in6_addr));
__builtin_memcpy(&ph.daddr, &ip6h->daddr, sizeof(struct in6_addr));
/* Do checksum update in two passes: first compute the incremental
* checksum update of the ICMPv6 pseudo header, update the checksum
* using bpf_l4_csum_replace(), and then do a separate update for the
@ -161,8 +177,7 @@ update_icmp_checksum(struct __sk_buff *skb,
static int
rewrite_icmp(struct iphdr *iph, struct ipv6hdr *ip6h, struct __sk_buff *skb)
{
void *data_end = (void *) (unsigned long long) skb->data_end;
void *data_end = SKB_DATA_END(skb);
struct icmphdr old_icmp, *icmp = (void *) (iph + 1);
struct icmp6hdr icmp6, *new_icmp6;
__u32 mtu;
@ -278,8 +293,8 @@ static __attribute__((always_inline)) inline int
clat_handle_v4(struct __sk_buff *skb, struct hdr_cursor *nh)
{
int ret = TC_ACT_OK;
void *data_end = (void *) (unsigned long long) skb->data_end;
void *data = (void *) (unsigned long long) skb->data;
void *data_end = SKB_DATA_END(skb);
void *data = SKB_DATA(skb);
int ip_type, iphdr_len, ip_offset;
struct in6_addr *dst_v6;
@ -363,8 +378,8 @@ clat_handle_v4(struct __sk_buff *skb, struct hdr_cursor *nh)
if (bpf_skb_change_proto(skb, bpf_htons(ETH_P_IPV6), 0))
goto out;
data = (void *) (unsigned long long) skb->data;
data_end = (void *) (unsigned long long) skb->data_end;
data = SKB_DATA(skb);
data_end = SKB_DATA_END(skb);
eth = data;
ip6h = data + ip_offset;
@ -400,8 +415,7 @@ rewrite_icmpv6(struct ipv6hdr *ip6h,
struct icmphdr **new_icmp_out,
struct hdr_cursor *nh)
{
void *data_end = (void *) (unsigned long long) skb->data_end;
void *data_end = SKB_DATA_END(skb);
struct icmp6hdr old_icmp6, *icmp6 = (void *) (ip6h + 1);
struct icmphdr icmp, *new_icmp;
__u32 mtu, ptr;
@ -563,7 +577,10 @@ clat_translate_v6(struct __sk_buff *skb,
switch (dst_hdr.protocol) {
case IPPROTO_ICMPV6:
new_icmp = (void *) (ip6h + 1);
new_icmp = (void *) (ip6h + 1);
if ((void *) (new_icmp + 1) > data_end)
goto out;
old_icmp6 = *((struct icmp6hdr *) (void *) new_icmp);
if (rewrite_icmpv6(ip6h, skb, &new_icmp, nh))
goto out;
@ -631,8 +648,8 @@ static __attribute__((always_inline)) inline int
clat_handle_v6(struct __sk_buff *skb, struct hdr_cursor *nh)
{
int ret = TC_ACT_OK;
void *data_end = (void *) (unsigned long long) skb->data_end;
void *data = (void *) (unsigned long long) skb->data;
void *data_end = SKB_DATA_END(skb);
void *data = SKB_DATA(skb);
struct ethhdr *eth;
struct iphdr *iph;
@ -648,8 +665,8 @@ clat_handle_v6(struct __sk_buff *skb, struct hdr_cursor *nh)
if (bpf_skb_change_proto(skb, bpf_htons(ETH_P_IP), 0))
goto out;
data = (void *) (unsigned long long) skb->data;
data_end = (void *) (unsigned long long) skb->data_end;
data = SKB_DATA(skb);
data_end = SKB_DATA_END(skb);
eth = data;
iph = data + ip_offset;
@ -667,8 +684,8 @@ out:
static __attribute__((always_inline)) inline int
clat_handler(struct __sk_buff *skb, bool egress)
{
void *data_end = (void *) (unsigned long long) skb->data_end;
void *data = (void *) (unsigned long long) skb->data;
void *data_end = SKB_DATA_END(skb);
void *data = SKB_DATA(skb);
struct hdr_cursor nh = {.pos = data};
struct ethhdr *eth;
int eth_type;