Netdev List
 help / color / mirror / Atom feed
* [PATCH bpf v3 0/2] bpf, sockmap: reject a packet-modifying SK_SKB stream parser
@ 2026-06-18 10:27 Sechang Lim
  2026-06-18 10:27 ` [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb Sechang Lim
  2026-06-18 10:27 ` [PATCH bpf v3 2/2] selftests/bpf: test rejection of a packet-modifying SK_SKB stream parser Sechang Lim
  0 siblings, 2 replies; 5+ messages in thread
From: Sechang Lim @ 2026-06-18 10:27 UTC (permalink / raw)
  To: John Fastabend, Jakub Sitnicki, Eric Dumazet, Kuniyuki Iwashima,
	Paolo Abeni, Willem de Bruijn, David S . Miller, Jakub Kicinski
  Cc: Simon Horman, Bobby Eshleman, Jiayuan Chen, netdev, bpf,
	linux-kernel

A BPF_PROG_TYPE_SK_SKB stream parser runs on strparser's message head,
which can chain skbs through frag_list. A parser that resizes the skb
frees the frag_list segments that strparser still tracks through
skb_nextp, leading to a use-after-free.

A stream parser is only meant to measure the next message, not to modify
the packet, so reject a packet-modifying parser at attach time rather
than working around the resize at runtime.

v3:
 - reject the parser at attach time instead of cloning the skb at
   runtime (Kuniyuki Iwashima, Jiayuan Chen)
 - add a selftest (Bobby Eshleman)

v2:
 - https://lore.kernel.org/all/20260612123553.2724240-1-rhkrqnwk98@gmail.com/

v1:
 - https://lore.kernel.org/all/20260609112316.3685738-1-rhkrqnwk98@gmail.com/

Sechang Lim (2):
  bpf, sockmap: fix use-after-free when the stream parser resizes the
    skb
  selftests/bpf: test rejection of a packet-modifying SK_SKB stream
    parser

 net/core/sock_map.c                           | 20 ++++++++++++
 .../selftests/bpf/prog_tests/sockmap_strp.c   | 31 +++++++++++++++++++
 .../selftests/bpf/progs/test_sockmap_strp.c   |  7 +++++
 3 files changed, 58 insertions(+)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb
  2026-06-18 10:27 [PATCH bpf v3 0/2] bpf, sockmap: reject a packet-modifying SK_SKB stream parser Sechang Lim
@ 2026-06-18 10:27 ` Sechang Lim
  2026-06-18 11:56   ` Jiayuan Chen
  2026-06-18 10:27 ` [PATCH bpf v3 2/2] selftests/bpf: test rejection of a packet-modifying SK_SKB stream parser Sechang Lim
  1 sibling, 1 reply; 5+ messages in thread
From: Sechang Lim @ 2026-06-18 10:27 UTC (permalink / raw)
  To: John Fastabend, Jakub Sitnicki, Eric Dumazet, Kuniyuki Iwashima,
	Paolo Abeni, Willem de Bruijn, David S . Miller, Jakub Kicinski
  Cc: Simon Horman, Bobby Eshleman, Jiayuan Chen, netdev, bpf,
	linux-kernel

sk_psock_strp_parse() runs the BPF_PROG_TYPE_SK_SKB stream-parser program
to find the length of the next message. strparser assembles a message out
of several received skbs by chaining them onto the head's frag_list and
recording where to append the next one in strp->skb_nextp:

	*strp->skb_nextp = skb;
	strp->skb_nextp = &skb->next;

and then calls the parser on the head:

	len = (*strp->cb.parse_msg)(strp, head);

The parser is only meant to inspect the skb, but the program may call
bpf_skb_change_tail() -- or the sibling bpf_skb_pull_data(),
bpf_skb_change_head(), bpf_skb_adjust_room(), all allowed for SK_SKB.
Once the head carries a frag_list these go

	... -> skb_ensure_writable -> pskb_may_pull -> __pskb_pull_tail

and __pskb_pull_tail() frees the frag_list skbs that strparser still
tracks through skb_nextp:

	while ((list = skb_shinfo(skb)->frag_list) != insp) {
		skb_shinfo(skb)->frag_list = list->next;
		consume_skb(list);
	}

strp->skb_nextp now points into a freed sk_buff. The next segment of
the same message arrives in __strp_recv(), which links it with
*strp->skb_nextp = skb, an 8-byte write into the freed skb. The free
and the write happen in different __strp_recv() calls, so the message
has to span at least three segments before it triggers.

  BUG: KASAN: slab-use-after-free in __strp_recv+0x447/0xda0
  Write of size 8 at addr ffff88810db86140 by task repro/349

  Call Trace:
   <IRQ>
   __strp_recv+0x447/0xda0
   __tcp_read_sock+0x13d/0x590
   tcp_bpf_strp_read_sock+0x195/0x320
   strp_data_ready+0x267/0x340
   sk_psock_strp_data_ready+0x1ce/0x350
   tcp_data_queue+0x1364/0x2fd0
   tcp_rcv_established+0xe07/0x1640
   [...]

  Allocated by task 349:
   skb_clone+0x17b/0x210
   __strp_recv+0x2c3/0xda0
   __tcp_read_sock+0x13d/0x590
   [...]

  Freed by task 349:
   kmem_cache_free+0x150/0x570
   __pskb_pull_tail+0x57b/0xc20
   skb_ensure_writable+0x236/0x260
   __bpf_skb_change_tail+0x1d4/0x590
   sk_skb_change_tail+0x2a/0x40
   bpf_prog_1b285dcd6c41373e+0x27/0x30
   bpf_prog_run_pin_on_cpu+0xf3/0x260
   sk_psock_strp_parse+0x118/0x1e0
   __strp_recv+0x4f6/0xda0
   [...]

The same resize also leaves the head's length inconsistent with its
frags, so a later __pskb_pull_tail() can instead hit the
BUG_ON(skb_copy_bits(...)) in net/core/skbuff.c.

A stream parser is only meant to measure the next message, not to modify
the packet. Reject a parser whose program can change packet data
(prog->aux->changes_pkt_data) at attach time. The check is shared by
sock_map_prog_update() and sock_map_link_update_prog(), which between them
cover prog attach, link create and link update. Verdict programs are
unaffected and may still modify the skb.

Fixes: 8a31db561566 ("bpf: add access to sock fields and pkt data from sk_skb programs")
Signed-off-by: Sechang Lim <rhkrqnwk98@gmail.com>
---
 net/core/sock_map.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index 99e3789492a0..c60ba6d292f9 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -1515,6 +1515,17 @@ static int sock_map_prog_link_lookup(struct bpf_map *map, struct bpf_prog ***ppr
 	return 0;
 }
 
+static int sock_map_prog_attach_check(enum bpf_attach_type attach_type,
+				      struct bpf_prog *prog)
+{
+	/* A stream parser must not modify the skb, only measure it. */
+	if (prog && attach_type == BPF_SK_SKB_STREAM_PARSER &&
+	    prog->aux->changes_pkt_data)
+		return -EINVAL;
+
+	return 0;
+}
+
 /* Handle the following four cases:
  * prog_attach: prog != NULL, old == NULL, link == NULL
  * prog_detach: prog == NULL, old != NULL, link == NULL
@@ -1533,6 +1544,10 @@ static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
 	if (ret)
 		return ret;
 
+	ret = sock_map_prog_attach_check(which, prog);
+	if (ret)
+		return ret;
+
 	/* for prog_attach/prog_detach/link_attach, return error if a bpf_link
 	 * exists for that prog.
 	 */
@@ -1776,6 +1791,11 @@ static int sock_map_link_update_prog(struct bpf_link *link,
 		ret = -EINVAL;
 		goto out;
 	}
+
+	ret = sock_map_prog_attach_check(link->attach_type, prog);
+	if (ret)
+		goto out;
+
 	if (!sockmap_link->map) {
 		ret = -ENOLINK;
 		goto out;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf v3 2/2] selftests/bpf: test rejection of a packet-modifying SK_SKB stream parser
  2026-06-18 10:27 [PATCH bpf v3 0/2] bpf, sockmap: reject a packet-modifying SK_SKB stream parser Sechang Lim
  2026-06-18 10:27 ` [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb Sechang Lim
@ 2026-06-18 10:27 ` Sechang Lim
  1 sibling, 0 replies; 5+ messages in thread
From: Sechang Lim @ 2026-06-18 10:27 UTC (permalink / raw)
  To: John Fastabend, Jakub Sitnicki, Eric Dumazet, Kuniyuki Iwashima,
	Paolo Abeni, Willem de Bruijn, David S . Miller, Jakub Kicinski
  Cc: Simon Horman, Bobby Eshleman, Jiayuan Chen, netdev, bpf,
	linux-kernel

Verify that attaching an SK_SKB stream parser that can modify the packet
is rejected, while a read-only parser still attaches.

Signed-off-by: Sechang Lim <rhkrqnwk98@gmail.com>
---
 .../selftests/bpf/prog_tests/sockmap_strp.c   | 31 +++++++++++++++++++
 .../selftests/bpf/progs/test_sockmap_strp.c   |  7 +++++
 2 files changed, 38 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c b/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
index 621b3b71888e..1d7231728eaf 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
@@ -431,6 +431,35 @@ static void test_sockmap_strp_verdict(int family, int sotype)
 	test_sockmap_strp__destroy(strp);
 }
 
+static void test_sockmap_strp_parser_reject(void)
+{
+	struct test_sockmap_strp *strp = NULL;
+	int parser_mod, parser_ro, link;
+	int err, map;
+
+	strp = test_sockmap_strp__open_and_load();
+	if (!ASSERT_OK_PTR(strp, "test_sockmap_strp__open_and_load"))
+		return;
+
+	map = bpf_map__fd(strp->maps.sock_map);
+	parser_mod = bpf_program__fd(strp->progs.prog_skb_parser_resize);
+	parser_ro = bpf_program__fd(strp->progs.prog_skb_parser);
+
+	err = bpf_prog_attach(parser_mod, map, BPF_SK_SKB_STREAM_PARSER, 0);
+	ASSERT_ERR(err, "bpf_prog_attach parser_mod");
+
+	link = bpf_link_create(parser_ro, map, BPF_SK_SKB_STREAM_PARSER, NULL);
+	if (!ASSERT_GE(link, 0, "bpf_link_create parser_ro"))
+		goto out;
+
+	err = bpf_link_update(link, parser_mod, NULL);
+	ASSERT_ERR(err, "bpf_link_update parser_mod");
+out:
+	if (link >= 0)
+		close(link);
+	test_sockmap_strp__destroy(strp);
+}
+
 void test_sockmap_strp(void)
 {
 	if (test__start_subtest("sockmap strp tcp pass"))
@@ -451,4 +480,6 @@ void test_sockmap_strp(void)
 		test_sockmap_strp_multiple_pkt(AF_INET, SOCK_STREAM);
 	if (test__start_subtest("sockmap strp tcp dispatch"))
 		test_sockmap_strp_dispatch_pkt(AF_INET, SOCK_STREAM);
+	if (test__start_subtest("sockmap strp parser reject pkt mod"))
+		test_sockmap_strp_parser_reject();
 }
diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_strp.c b/tools/testing/selftests/bpf/progs/test_sockmap_strp.c
index dde3d5bec515..fe88fa6d40bc 100644
--- a/tools/testing/selftests/bpf/progs/test_sockmap_strp.c
+++ b/tools/testing/selftests/bpf/progs/test_sockmap_strp.c
@@ -50,4 +50,11 @@ int prog_skb_parser_partial(struct __sk_buff *skb)
 	return 10;
 }
 
+SEC("sk_skb/stream_parser")
+int prog_skb_parser_resize(struct __sk_buff *skb)
+{
+	bpf_skb_change_tail(skb, skb->len, 0);
+	return skb->len;
+}
+
 char _license[] SEC("license") = "GPL";
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb
  2026-06-18 10:27 ` [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb Sechang Lim
@ 2026-06-18 11:56   ` Jiayuan Chen
  2026-06-18 18:01     ` John Fastabend
  0 siblings, 1 reply; 5+ messages in thread
From: Jiayuan Chen @ 2026-06-18 11:56 UTC (permalink / raw)
  To: John Fastabend; +Cc: netdev, bpf, linux-kernel, Jakub Kicinski, Sechang Lim


On 6/18/26 6:27 PM, Sechang Lim wrote:
> sk_psock_strp_parse() runs the BPF_PROG_TYPE_SK_SKB stream-parser program
> to find the length of the next message. strparser assembles a message out
> of several received skbs by chaining them onto the head's frag_list and
> recording where to append the next one in strp->skb_nextp:
>
> 	*strp->skb_nextp = skb;
> 	strp->skb_nextp = &skb->next;
>
> and then calls the parser on the head:
>
> 	len = (*strp->cb.parse_msg)(strp, head);

[...]

> unaffected and may still modify the skb.
>
> Fixes: 8a31db561566 ("bpf: add access to sock fields and pkt data from sk_skb programs")

Is the Fixes tag correct ?

Anyway, I don't think this patch is a fix; it's more of a hardening. So 
no Fixes tag needed, IMO.


> Signed-off-by: Sechang Lim <rhkrqnwk98@gmail.com>
> ---
>   net/core/sock_map.c | 20 ++++++++++++++++++++
>   1 file changed, 20 insertions(+)
>
> diff --git a/net/core/sock_map.c b/net/core/sock_map.c
> index 99e3789492a0..c60ba6d292f9 100644
> --- a/net/core/sock_map.c
> +++ b/net/core/sock_map.c
> @@ -1515,6 +1515,17 @@ static int sock_map_prog_link_lookup(struct bpf_map *map, struct bpf_prog ***ppr
>   	return 0;
>   }
>   
> +static int sock_map_prog_attach_check(enum bpf_attach_type attach_type,
> +				      struct bpf_prog *prog)
> +{
> +	/* A stream parser must not modify the skb, only measure it. */
> +	if (prog && attach_type == BPF_SK_SKB_STREAM_PARSER &&
> +	    prog->aux->changes_pkt_data)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
>   /* Handle the following four cases:
>    * prog_attach: prog != NULL, old == NULL, link == NULL
>    * prog_detach: prog == NULL, old != NULL, link == NULL
> @@ -1533,6 +1544,10 @@ static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
>   	if (ret)
>   		return ret;
>   
> +	ret = sock_map_prog_attach_check(which, prog);
> +	if (ret)
> +		return ret;
> +
>   	/* for prog_attach/prog_detach/link_attach, return error if a bpf_link
>   	 * exists for that prog.
>   	 */
> @@ -1776,6 +1791,11 @@ static int sock_map_link_update_prog(struct bpf_link *link,
>   		ret = -EINVAL;
>   		goto out;
>   	}
> +
> +	ret = sock_map_prog_attach_check(link->attach_type, prog);
> +	if (ret)
> +		goto out;
> +
>   	if (!sockmap_link->map) {
>   		ret = -ENOLINK;
>   		goto out;


CI failed:
https://github.com/kernel-patches/bpf/actions/runs/27754218839/job/82113319982
    Failed stream parser bpf prog attach

Hi John
I noticed that bpf_skb_pull_data was added to the skmsg test:
https://github.com/torvalds/linux/commit/82a8616889d506cb690cfc0afb2ccadda120461d

Can we drop bpf_skb_pull_data in parser prog(sockmap_parse_prog.c‎) ?
And are there any scenarios where we need to modify skb len when using 
strparser ?



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb
  2026-06-18 11:56   ` Jiayuan Chen
@ 2026-06-18 18:01     ` John Fastabend
  0 siblings, 0 replies; 5+ messages in thread
From: John Fastabend @ 2026-06-18 18:01 UTC (permalink / raw)
  To: Jiayuan Chen; +Cc: netdev, bpf, linux-kernel, Jakub Kicinski, Sechang Lim

On Thu, Jun 18, 2026 at 07:56:34PM +0800, Jiayuan Chen wrote:
>
>On 6/18/26 6:27 PM, Sechang Lim wrote:
>>sk_psock_strp_parse() runs the BPF_PROG_TYPE_SK_SKB stream-parser program
>>to find the length of the next message. strparser assembles a message out
>>of several received skbs by chaining them onto the head's frag_list and
>>recording where to append the next one in strp->skb_nextp:
>>
>>	*strp->skb_nextp = skb;
>>	strp->skb_nextp = &skb->next;
>>
>>and then calls the parser on the head:
>>
>>	len = (*strp->cb.parse_msg)(strp, head);
>
>[...]
>
>>unaffected and may still modify the skb.
>>
>>Fixes: 8a31db561566 ("bpf: add access to sock fields and pkt data from sk_skb programs")
>
>Is the Fixes tag correct ?
>
>Anyway, I don't think this patch is a fix; it's more of a hardening. 
>So no Fixes tag needed, IMO.
>
>
>>Signed-off-by: Sechang Lim <rhkrqnwk98@gmail.com>
>>---

[...]

>
>
>CI failed:
>https://github.com/kernel-patches/bpf/actions/runs/27754218839/job/82113319982
>   Failed stream parser bpf prog attach
>
>Hi John
>I noticed that bpf_skb_pull_data was added to the skmsg test:
>https://github.com/torvalds/linux/commit/82a8616889d506cb690cfc0afb2ccadda120461d
>
>Can we drop bpf_skb_pull_data in parser prog(sockmap_parse_prog.c‎) ?
>And are there any scenarios where we need to modify skb len when using 
>strparser ?

We should never modify the skb from strparser. Just remove any tests
that do this and state its not safe. We haven't used strparser progs
for a long time anyways.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-06-18 18:02 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-18 10:27 [PATCH bpf v3 0/2] bpf, sockmap: reject a packet-modifying SK_SKB stream parser Sechang Lim
2026-06-18 10:27 ` [PATCH bpf v3 1/2] bpf, sockmap: fix use-after-free when the stream parser resizes the skb Sechang Lim
2026-06-18 11:56   ` Jiayuan Chen
2026-06-18 18:01     ` John Fastabend
2026-06-18 10:27 ` [PATCH bpf v3 2/2] selftests/bpf: test rejection of a packet-modifying SK_SKB stream parser Sechang Lim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox