From: Josef Bacik The source of bpf_dynptr_write() may be a slice of the skb which is written to, that is a pointer into its head. bpf_dynptr_copy() does it by itself for a copy inside of one skb, if the source is linear and the destination is not. __bpf_skb_store_bytes() and __bpf_skb_meta_store_bytes() make the skb writable first. If that reallocates the head, the source points to the old one, and freed memory is copied into the packet. The offsets in the head do not change when it is reallocated here. Move the source along with the head. Fixes: 2012c867c800 ("bpf: verifier: Accept dynptr mem as mem in helpers") Fixes: f38499ff45f5 ("bpf: Unclone skb head on bpf_dynptr_write to skb metadata") Signed-off-by: Josef Bacik Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) 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); -- 2.55.0