All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf v2 0/2] bpf: Reject negative optlen in cgroup getsockopt hook
@ 2026-08-11 14:19 Junseo Lim
  2026-08-11 14:19 ` [PATCH bpf v2 1/2] " Junseo Lim
  2026-08-11 14:19 ` [PATCH bpf v2 2/2] selftests/bpf: Exercise " Junseo Lim
  0 siblings, 2 replies; 4+ messages in thread
From: Junseo Lim @ 2026-08-11 14:19 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau
  Cc: Stanislav Fomichev, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, bpf, linux-kernel, Sechang Lim

A cgroup getsockopt BPF program can set a negative optlen after the
kernel getsockopt handler has run. For TCP_ZEROCOPY_RECEIVE, the
kernel-buffer hook did not validate the lower bound and the negative
value could reach copy_to_sockptr() as a size_t.

Patch 1 rejects negative optlen values in the kernel-buffer hook.
Patch 2 adds a test_progs selftest that reproduces the issue through
TCP_ZEROCOPY_RECEIVE.

For patch 2, the userspace-visible errno is EFAULT on both fixed and
unfixed kernels, so the broken behavior cannot be detected from the
getsockopt() return value alone. The selftest exercises the problematic
path and can be used to reproduce the hardened usercopy warning on
unpatched kernels.

Changelog:
v1 -> v2:
- Add the selftest for negative optlen.
- Add Emil's Reviewed-by tag.
- Add Reported-by tag.

v1: https://lore.kernel.org/bpf/20260726070122.2407344-1-zirajs7@gmail.com/T/

Junseo Lim (2):
  bpf: Reject negative optlen in cgroup getsockopt hook
  selftests/bpf: Exercise negative optlen in cgroup getsockopt hook


 kernel/bpf/cgroup.c                           |  2 +-
 .../selftests/bpf/prog_tests/sockopt.c        | 22 +++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

-- 
2.55.0

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

* [PATCH bpf v2 1/2] bpf: Reject negative optlen in cgroup getsockopt hook
  2026-08-11 14:19 [PATCH bpf v2 0/2] bpf: Reject negative optlen in cgroup getsockopt hook Junseo Lim
@ 2026-08-11 14:19 ` Junseo Lim
  2026-08-11 14:19 ` [PATCH bpf v2 2/2] selftests/bpf: Exercise " Junseo Lim
  1 sibling, 0 replies; 4+ messages in thread
From: Junseo Lim @ 2026-08-11 14:19 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau
  Cc: Stanislav Fomichev, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, bpf, linux-kernel, Sechang Lim

A cgroup getsockopt BPF program can shrink ctx->optlen after the
kernel getsockopt handler has run. The kernel-buffer variant, used by
TCP_ZEROCOPY_RECEIVE, only rejects values larger than the original
length.

If BPF writes a negative optlen, that value is accepted and propagated
back to the TCP getsockopt code. It can then be passed to
copy_to_sockptr() as a size_t and trigger the hardened usercopy
bytes > INT_MAX warning.

Reject negative ctx.optlen in __cgroup_bpf_run_filter_getsockopt_kern(),
matching the lower-bound validation already present in the sockptr-based
getsockopt hook.

Fixes: 9cacf81f8161 ("bpf: Remove extra lock_sock for TCP_ZEROCOPY_RECEIVE")
Reported-by: Sechang Lim <rhkrqnwk98@gmail.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Signed-off-by: Junseo Lim <zirajs7@gmail.com>
---
This issue was found by a custom fuzzer developed by
Sechang Lim <rhkrqnwk98@gmail.com>.

 kernel/bpf/cgroup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 4355ccb78a9c..c04a244fe2e6 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -2235,7 +2235,7 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level,
 	if (ret < 0)
 		return ret;
 
-	if (ctx.optlen > *optlen)
+	if (ctx.optlen > *optlen || ctx.optlen < 0)
 		return -EFAULT;
 
 	/* BPF programs can shrink the buffer, export the modifications.
-- 
2.55.0

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

* [PATCH bpf v2 2/2] selftests/bpf: Exercise negative optlen in cgroup getsockopt hook
  2026-08-11 14:19 [PATCH bpf v2 0/2] bpf: Reject negative optlen in cgroup getsockopt hook Junseo Lim
  2026-08-11 14:19 ` [PATCH bpf v2 1/2] " Junseo Lim
@ 2026-08-11 14:19 ` Junseo Lim
  2026-08-11 15:10   ` bot+bpf-ci
  1 sibling, 1 reply; 4+ messages in thread
From: Junseo Lim @ 2026-08-11 14:19 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau
  Cc: Stanislav Fomichev, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, bpf, linux-kernel, Sechang Lim

Add a cgroup getsockopt selftest that sets ctx->optlen to -1. Use
TCP_ZEROCOPY_RECEIVE to exercise the kernel-buffer getsockopt hook.

The userspace-visible result is -EFAULT on both patched and unpatched
kernels, so the return value alone cannot distinguish the bug. The test
still exercises the kernel-buffer getsockopt path with a negative
ctx->optlen, which reproduces the hardened usercopy warning on unpatched
kernels.

Signed-off-by: Junseo Lim <zirajs7@gmail.com>
---
Adding the selftest to sockopt_test triggered the problematic path, but
detecting the broken behavior was not trivial because getsockopt()
returned -EFAULT on both patched and unpatched kernels. The selftest
mainly serves as a reproducer for the hardened usercopy warning.

 .../selftests/bpf/prog_tests/sockopt.c        | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt.c b/tools/testing/selftests/bpf/prog_tests/sockopt.c
index eaac83a7f388..3834dcf0f9dd 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockopt.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt.c
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/tcp.h>
 #include <test_progs.h>
 #include <io_uring/mini_liburing.h>
 #include "cgroup_helpers.h"
@@ -283,6 +284,27 @@ static struct sockopt_test {
 		.error = EFAULT_GETSOCKOPT,
 		.io_uring_support = true,
 	},
+	{
+		.descr = "getsockopt: deny negative ctx->optlen in TCP_ZEROCOPY_RECEIVE",
+		.insns = {
+			/* ctx->optlen = -1 */
+			BPF_MOV64_IMM(BPF_REG_0, -1),
+			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+				    offsetof(struct bpf_sockopt, optlen)),
+
+			/* return 1 */
+			BPF_MOV64_IMM(BPF_REG_0, 1),
+			BPF_EXIT_INSN(),
+		},
+		.attach_type = BPF_CGROUP_GETSOCKOPT,
+		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+		.get_level = IPPROTO_TCP,
+		.get_optname = TCP_ZEROCOPY_RECEIVE,
+		.get_optlen = sizeof(struct tcp_zerocopy_receive),
+
+		.error = EFAULT_GETSOCKOPT,
+	},
 	{
 		.descr = "getsockopt: ignore >PAGE_SIZE optlen",
 		.insns = {
-- 
2.55.0

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

* Re: [PATCH bpf v2 2/2] selftests/bpf: Exercise negative optlen in cgroup getsockopt hook
  2026-08-11 14:19 ` [PATCH bpf v2 2/2] selftests/bpf: Exercise " Junseo Lim
@ 2026-08-11 15:10   ` bot+bpf-ci
  0 siblings, 0 replies; 4+ messages in thread
From: bot+bpf-ci @ 2026-08-11 15:10 UTC (permalink / raw)
  To: zirajs7, ast, daniel, andrii, eddyz87, memxor, martin.lau
  Cc: sdf, song, yonghong.song, jolsa, emil, bpf, linux-kernel,
	rhkrqnwk98, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3352 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt.c b/tools/testing/selftests/bpf/prog_tests/sockopt.c
> index eaac83a7f388..6c96f2d9fccf 100644
> --- a/tools/testing/selftests/bpf/prog_tests/sockopt.c
> +++ b/tools/testing/selftests/bpf/prog_tests/sockopt.c

[ ... ]

> @@ -283,6 +284,27 @@ static struct sockopt_test {
>  		.error = EFAULT_GETSOCKOPT,
>  		.io_uring_support = true,
>  	},
> +	{
> +		.descr = "getsockopt: deny negative ctx->optlen in TCP_ZEROCOPY_RECEIVE",
> +		.insns = {
> +			/* ctx->optlen = -1 */
> +			BPF_MOV64_IMM(BPF_REG_0, -1),
> +			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
> +				    offsetof(struct bpf_sockopt, optlen)),
> +
> +			/* return 1 */
> +			BPF_MOV64_IMM(BPF_REG_0, 1),
> +			BPF_EXIT_INSN(),
> +		},
> +		.attach_type = BPF_CGROUP_GETSOCKOPT,
> +		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
> +
> +		.get_level = IPPROTO_TCP,
> +		.get_optname = TCP_ZEROCOPY_RECEIVE,
> +		.get_optlen = sizeof(struct tcp_zerocopy_receive),
> +
> +		.error = EFAULT_GETSOCKOPT,
> +	},
>  	{
>  		.descr = "getsockopt: ignore >PAGE_SIZE optlen",
>  		.insns = {

A subsystem pattern flags this as potentially concerning: the test asserts
-EFAULT for negative ctx->optlen, but both a fixed kernel and an unfixed
kernel can return -EFAULT, so does the test provide regression protection?

The commit message notes this honestly: "the return value alone cannot
distinguish the bug." The test exercises the kernel-buffer getsockopt path
with ctx->optlen == -1, which triggers a hardened-usercopy WARN on
unpatched kernels.

Tracing it through:

  run_test() creates socket(AF_INET, SOCK_STREAM, 0), state is TCP_CLOSE
  and tcp_inq(sk) == 0.

  do_tcp_getsockopt() accepts len (40) and calls tcp_zerocopy_receive().
  With inq == 0 < PAGE_SIZE and SOCK_DONE unset:

    zc->length = 0;
    zc->recv_skip_hint = inq;
    return 0;

  so err == 0 going into the BPF hook.

  Without the fix: ctx.optlen becomes -1, back in tcp.c, switch (len) with
  len == -1 falls to default: goto zerocopy_rcv_out. The if (!err &&
  copy_to_sockptr(optval, &zc, len)) runs with size (size_t)-1.
  check_copy_size() WARNs and returns false, copy_to_user() returns n != 0,
  so err = -EFAULT.

  With the fix: ctx.optlen < 0 returns -EFAULT directly, all the !err
  guards skip the copy, and -EFAULT is returned.

Userspace sees errno == EFAULT in both cases; run_test() returns 0 (PASS)
either way. The only difference between kernels is a WARN in dmesg, and
test_progs has no dmesg/taint/WARN detection (no references to dmesg,
kmsg, tainted, or panic_on_warn in test_progs.c/.h).

Strengthening it by making the handler fail first doesn't work either,
because __cgroup_bpf_run_filter_getsockopt_kern() does if (ret < 0) return
ret; before applying ctx.optlen, so a non-zero retval short-circuits the
buggy assignment. The buggy copy is only reachable when err == 0, and tcp.c
always converts it into EFAULT.

Is the intent that this test relies on WARN/taint detection or
panic_on_warn, or is the value of the test primarily as a WARN-only
reproducer rather than a pass/fail assertion?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31501873750

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

end of thread, other threads:[~2026-08-11 15:10 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-11 14:19 [PATCH bpf v2 0/2] bpf: Reject negative optlen in cgroup getsockopt hook Junseo Lim
2026-08-11 14:19 ` [PATCH bpf v2 1/2] " Junseo Lim
2026-08-11 14:19 ` [PATCH bpf v2 2/2] selftests/bpf: Exercise " Junseo Lim
2026-08-11 15:10   ` bot+bpf-ci

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.