diff --git a/net/core/filter.c b/net/core/filter.c index caa9c3ef0a78..0abedbb04ab7 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1715,6 +1715,24 @@ static inline void bpf_pull_mac_rcsum(struct sk_buff *skb) skb_postpull_rcsum(skb, skb_mac_header(skb), skb->mac_len); } +/* + * The source of a write through a dynptr may be a slice of the same skb. + * Making the skb writable can move its head: follow the move. + */ +static int bpf_try_make_writable_from(struct sk_buff *skb, u32 write_len, + const void **from) +{ + const void *head = skb->head; + bool in_head = *from >= head && + *from < (const void *)skb_end_pointer(skb); + int err; + + err = bpf_try_make_writable(skb, write_len); + if (!err && in_head) + *from = skb->head + (*from - head); + return err; +} + BPF_CALL_5(bpf_skb_store_bytes, struct sk_buff *, skb, u32, offset, const void *, from, u32, len, u64, flags) { @@ -1724,7 +1742,7 @@ BPF_CALL_5(bpf_skb_store_bytes, struct sk_buff *, skb, u32, offset, return -EINVAL; if (unlikely(offset > INT_MAX)) return -EFAULT; - if (unlikely(bpf_try_make_writable(skb, offset + len))) + if (unlikely(bpf_try_make_writable_from(skb, offset + len, &from))) return -EFAULT; ptr = skb->data + offset; @@ -12427,7 +12445,7 @@ int __bpf_skb_meta_store_bytes(struct sk_buff *skb, u32 offset, { if (unlikely(flags)) return -EINVAL; - if (unlikely(bpf_try_make_writable(skb, 0))) + if (unlikely(bpf_try_make_writable_from(skb, 0, &from))) return -EFAULT; memmove(bpf_skb_meta_pointer(skb, offset), from, len);