diff --git a/net/core/skmsg.c b/net/core/skmsg.c index e1850caf1a71a..d5b10f9b0ba8d 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1146,14 +1146,30 @@ static int sk_psock_strp_parse(struct strparser *strp, struct sk_buff *skb) struct bpf_prog *prog; int ret = skb->len; - rcu_read_lock(); + guard(rcu)(); prog = READ_ONCE(psock->progs.stream_parser); if (likely(prog)) { - skb->sk = psock->sk; - ret = bpf_prog_run_pin_on_cpu(prog, skb); - skb->sk = NULL; + struct sk_buff *parse_skb = skb; + + /* + * strparser chains the message skbs through skb->frag_list and + * keeps a pointer into that list in strp->skb_nextp. The parser + * program may call bpf_skb_change_tail() and friends, which go + * through __pskb_pull_tail() and free the frag_list skbs that + * strparser still tracks. Run the program on a clone when a + * frag_list is present so it cannot drop frags strparser owns. + */ + if (skb_has_frag_list(skb)) { + parse_skb = skb_clone(skb, GFP_ATOMIC); + if (!parse_skb) + return -ENOMEM; + } + parse_skb->sk = psock->sk; + ret = bpf_prog_run_pin_on_cpu(prog, parse_skb); + parse_skb->sk = NULL; + if (parse_skb != skb) + kfree_skb(parse_skb); } - rcu_read_unlock(); return ret; }