Netdev List
 help / color / mirror / Atom feed
* [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls
@ 2026-08-13 11:20 Maciej Żenczykowski
  2026-08-13 11:20 ` [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_sockopt is_compat field Maciej Żenczykowski
  2026-08-13 12:59 ` [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls bot+bpf-ci
  0 siblings, 2 replies; 4+ messages in thread
From: Maciej Żenczykowski @ 2026-08-13 11:20 UTC (permalink / raw)
  To: Maciej Żenczykowski, Alexei Starovoitov, Daniel Borkmann
  Cc: Linux Network Development Mailing List, Linux Kernel Mailing List,
	BPF Mailing List, Maciej Żenczykowski

Currently, BPF cgroup socket option programs (BPF_PROG_TYPE_CGROUP_SOCKOPT)
are skipped for 32-bit compat tasks on 64-bit kernels via explicit
!compat checks in do_sock_setsockopt() and do_sock_getsockopt().

While this originally avoided issues with programs assuming native 64-bit
sockopt structure layouts, completely bypassing BPF sockopt hooks allows
32-bit binaries to bypass cgroup network policies and prevents BPF
programs from managing sockopts for compat applications.

Enable BPF cgroup sockopt hooks for compat syscalls by:
1. Adding a read-only `is_compat` field to `struct bpf_sockopt` (and
   `struct bpf_sockopt_kern`) which is 1 for compat syscalls and 0
   otherwise.
2. Passing the `compat` parameter through BPF_CGROUP_RUN_PROG_SETSOCKOPT
   and BPF_CGROUP_RUN_PROG_GETSOCKOPT to populate `ctx.is_compat`.
3. Removing the `!compat` guards in do_sock_setsockopt() and
   do_sock_getsockopt().

BPF programs can now inspect `ctx->is_compat` to handle any 32-bit vs
64-bit layout differences for the very rare cases where it is needed.

Behavioral Note for BPF Programs:
  Previously, compat sockopt calls were completely invisible to BPF.
  Now, attached BPF sockopt programs will run on compat calls.
  For options with arch-dependent structures (e.g. SO_RCVTIMEO with 32-bit
  vs 64-bit struct timeval), BPF programs must now check ctx->is_compat to
  correctly parse/modify the buffer.
  Due to 32-bit userspace being near dead and there being very few such
  options that one would want to overwrite in eBpf, I'm judging this to
  be acceptable (there's probably very few programs that would be affected).

Signed-off-by: Maciej Żenczykowski <maze@google.com>
---
 include/linux/bpf-cgroup.h | 35 ++++++++++++++++++-----------------
 include/linux/filter.h     |  1 +
 include/uapi/linux/bpf.h   |  1 +
 kernel/bpf/cgroup.c        |  9 +++++++--
 net/socket.c               | 17 +++++++----------
 5 files changed, 34 insertions(+), 29 deletions(-)

diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index 4d0cc65976a1..943eb023b52b 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -138,14 +138,15 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
 				   char **buf, size_t *pcount, loff_t *ppos,
 				   enum cgroup_bpf_attach_type atype);
 
-int __cgroup_bpf_run_filter_setsockopt(struct sock *sock, int *level,
-				       int *optname, sockptr_t optval,
-				       int *optlen, char **kernel_optval);
+int __cgroup_bpf_run_filter_setsockopt(struct sock *sock, bool compat,
+				       int *level, int *optname,
+				       sockptr_t optval, int *optlen,
+				       char **kernel_optval);
 
-int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
-				       int optname, sockptr_t optval,
-				       sockptr_t optlen, int max_optlen,
-				       int retval);
+int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, bool compat,
+				       int level, int optname,
+				       sockptr_t optval, sockptr_t optlen,
+				       int max_optlen, int retval);
 
 int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level,
 					    int optname, void *optval,
@@ -376,21 +377,21 @@ static inline bool cgroup_bpf_sock_enabled(struct sock *sk,
 	__ret;								       \
 })
 
-#define BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock, level, optname, optval, optlen,   \
-				       kernel_optval)			       \
+#define BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock, compat, level, optname, optval,   \
+				       optlen, kernel_optval)		       \
 ({									       \
 	int __ret = 0;							       \
 	if (cgroup_bpf_enabled(CGROUP_SETSOCKOPT) &&			       \
 	    cgroup_bpf_sock_enabled(sock, CGROUP_SETSOCKOPT))		       \
-		__ret = __cgroup_bpf_run_filter_setsockopt(sock, level,	       \
-							   optname, optval,    \
-							   optlen,	       \
+		__ret = __cgroup_bpf_run_filter_setsockopt(sock, compat,       \
+							   level, optname,     \
+							   optval, optlen,     \
 							   kernel_optval);     \
 	__ret;								       \
 })
 
-#define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, level, optname, optval, optlen,   \
-				       max_optlen, retval)		       \
+#define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, compat, level, optname, optval,   \
+				       optlen, max_optlen, retval)	       \
 ({									       \
 	int __ret = retval;						       \
 	if (cgroup_bpf_enabled(CGROUP_GETSOCKOPT) &&			       \
@@ -400,7 +401,7 @@ static inline bool cgroup_bpf_sock_enabled(struct sock *sk,
 					tcp_bpf_bypass_getsockopt,	       \
 					level, optname))		       \
 			__ret = __cgroup_bpf_run_filter_getsockopt(	       \
-				sock, level, optname, optval, optlen,	       \
+				sock, compat, level, optname, optval, optlen,  \
 				max_optlen, retval);			       \
 	__ret;								       \
 })
@@ -504,11 +505,11 @@ static inline int bpf_percpu_cgroup_storage_update(struct bpf_map *map,
 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(atype, major, minor, access) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,pos) ({ 0; })
-#define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, level, optname, optval, \
+#define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, compat, level, optname, optval, \
 				       optlen, max_optlen, retval) ({ retval; })
 #define BPF_CGROUP_RUN_PROG_GETSOCKOPT_KERN(sock, level, optname, optval, \
 					    optlen, retval) ({ retval; })
-#define BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock, level, optname, optval, optlen, \
+#define BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock, compat, level, optname, optval, optlen, \
 				       kernel_optval) ({ 0; })
 
 #endif /* CONFIG_CGROUP_BPF */
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 4a9bc6a848f2..5ab7d606aea5 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1702,6 +1702,7 @@ struct bpf_sockopt_kern {
 	s32		level;
 	s32		optname;
 	s32		optlen;
+	s32		is_compat;
 	/* for retval in struct bpf_cg_run_ctx */
 	struct task_struct *current_task;
 	/* Temporary "register" for indirect stores to ppos. */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index ffd96e8b920b..15f712b5b164 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -7616,6 +7616,7 @@ struct bpf_sockopt {
 	__s32	optname;
 	__s32	optlen;
 	__s32	retval;
+	__s32	is_compat;
 };
 
 struct bpf_pidns_info {
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 8fbc942a1cc3..910878e991a0 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -2055,7 +2055,7 @@ static bool sockopt_buf_allocated(struct bpf_sockopt_kern *ctx,
 	return ctx->optval != buf->data;
 }
 
-int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
+int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, bool compat, int *level,
 				       int *optname, sockptr_t optval,
 				       int *optlen, char **kernel_optval)
 {
@@ -2065,6 +2065,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
 		.sk = sk,
 		.level = *level,
 		.optname = *optname,
+		.is_compat = compat,
 	};
 	int ret, max_optlen;
 
@@ -2146,7 +2147,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
 	return ret;
 }
 
-int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
+int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, bool compat, int level,
 				       int optname, sockptr_t optval,
 				       sockptr_t optlen, int max_optlen,
 				       int retval)
@@ -2157,6 +2158,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
 		.sk = sk,
 		.level = level,
 		.optname = optname,
+		.is_compat = compat,
 		.current_task = current,
 	};
 	int orig_optlen;
@@ -2705,6 +2707,9 @@ static u32 cg_sockopt_convert_ctx_access(enum bpf_access_type type,
 		else
 			*insn++ = CG_SOCKOPT_READ_FIELD(optlen);
 		break;
+	case offsetof(struct bpf_sockopt, is_compat):
+		*insn++ = CG_SOCKOPT_READ_FIELD(is_compat);
+		break;
 	case offsetof(struct bpf_sockopt, retval):
 		BUILD_BUG_ON(offsetof(struct bpf_cg_run_ctx, run_ctx) != 0);
 
diff --git a/net/socket.c b/net/socket.c
index 63c69a0fa74e..f4549975f98b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2346,10 +2346,9 @@ int do_sock_setsockopt(struct socket *sock, bool compat, int level,
 	if (err)
 		goto out_put;
 
-	if (!compat)
-		err = BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock->sk, &level, &optname,
-						     optval, &optlen,
-						     &kernel_optval);
+	err = BPF_CGROUP_RUN_PROG_SETSOCKOPT(sock->sk, compat, &level, &optname,
+					     optval, &optlen,
+					     &kernel_optval);
 	if (err < 0)
 		goto out_put;
 	if (err > 0) {
@@ -2447,8 +2446,7 @@ int do_sock_getsockopt(struct socket *sock, bool compat, int level,
 	if (err)
 		return err;
 
-	if (!compat)
-		copy_from_sockptr(&max_optlen, optlen, sizeof(int));
+	copy_from_sockptr(&max_optlen, optlen, sizeof(int));
 
 	ops = READ_ONCE(sock->ops);
 	if (level == SOL_SOCKET) {
@@ -2477,10 +2475,9 @@ int do_sock_getsockopt(struct socket *sock, bool compat, int level,
 		err = -EOPNOTSUPP;
 	}
 
-	if (!compat)
-		err = BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock->sk, level, optname,
-						     optval, optlen, max_optlen,
-						     err);
+	err = BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock->sk, compat, level, optname,
+					     optval, optlen, max_optlen,
+					     err);
 
 	return err;
 }
-- 
2.55.0.691.gc56d675ccc-goog


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

* [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_sockopt is_compat field
  2026-08-13 11:20 [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls Maciej Żenczykowski
@ 2026-08-13 11:20 ` Maciej Żenczykowski
  2026-08-13 12:14   ` bot+bpf-ci
  2026-08-13 12:59 ` [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls bot+bpf-ci
  1 sibling, 1 reply; 4+ messages in thread
From: Maciej Żenczykowski @ 2026-08-13 11:20 UTC (permalink / raw)
  To: Maciej Żenczykowski, Alexei Starovoitov, Daniel Borkmann
  Cc: Linux Network Development Mailing List, Linux Kernel Mailing List,
	BPF Mailing List, Maciej Żenczykowski

Sync tools/include/uapi/linux/bpf.h with the addition of the 'is_compat'
field in 'struct bpf_sockopt' and add test cases to 'prog_tests/sockopt.c'
to verify context access permissions:

 - Test reading 'ctx->is_compat' in 'cgroup/{g,s}etsockopt'
   (expecting 0 in native execution).
 - Check verifier rejects writes to 'ctx->is_compat'
   in 'cgroup/{g,s}etsockopt'.

Tested via:
  tools/testing/selftests/bpf/vmtest.sh -- ./test_progs -t sockopt

Signed-off-by: Maciej Żenczykowski <maze@google.com>
---
 tools/include/uapi/linux/bpf.h                |  1 +
 .../selftests/bpf/prog_tests/sockopt.c        | 86 +++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index ffd96e8b920b..15f712b5b164 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -7616,6 +7616,7 @@ struct bpf_sockopt {
 	__s32	optname;
 	__s32	optlen;
 	__s32	retval;
+	__s32	is_compat;
 };
 
 struct bpf_pidns_info {
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt.c b/tools/testing/selftests/bpf/prog_tests/sockopt.c
index eaac83a7f388..c7dd22d0e3f8 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockopt.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt.c
@@ -215,6 +215,49 @@ static struct sockopt_test {
 		.get_optname = IP_TOS,
 		.get_optlen = 1,
 	},
+	{
+		.descr = "getsockopt: read ctx->is_compat",
+		.insns = {
+			/* r6 = ctx->is_compat */
+			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+				    offsetof(struct bpf_sockopt, is_compat)),
+
+			/* if (ctx->is_compat == 0) { */
+			BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 4),
+			/* ctx->retval = 0 */
+			BPF_MOV64_IMM(BPF_REG_0, 0),
+			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+				    offsetof(struct bpf_sockopt, retval)),
+			/* return 1 */
+			BPF_MOV64_IMM(BPF_REG_0, 1),
+			BPF_JMP_A(1),
+			/* } else { */
+			/* return 0 */
+			BPF_MOV64_IMM(BPF_REG_0, 0),
+			/* } */
+			BPF_EXIT_INSN(),
+		},
+		.attach_type = BPF_CGROUP_GETSOCKOPT,
+		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+		.get_level = SOL_IP,
+		.get_optname = IP_TOS,
+		.get_optlen = 1,
+	},
+	{
+		.descr = "getsockopt: deny writing to ctx->is_compat",
+		.insns = {
+			/* ctx->is_compat = 1 */
+			BPF_MOV64_IMM(BPF_REG_0, 1),
+			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+				    offsetof(struct bpf_sockopt, is_compat)),
+			BPF_EXIT_INSN(),
+		},
+		.attach_type = BPF_CGROUP_GETSOCKOPT,
+		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+		.error = DENY_LOAD,
+	},
 	{
 		.descr = "getsockopt: deny writing to ctx->optname",
 		.insns = {
@@ -609,6 +652,49 @@ static struct sockopt_test {
 		.get_optval = { 1 << 3 },
 		.get_optlen = 1,
 	},
+	{
+		.descr = "setsockopt: read ctx->is_compat",
+		.insns = {
+			/* r6 = ctx->is_compat */
+			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+				    offsetof(struct bpf_sockopt, is_compat)),
+
+			/* if (ctx->is_compat == 0) { */
+			BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 4),
+			/* 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_JMP_A(1),
+			/* } else { */
+			/* return 0 */
+			BPF_MOV64_IMM(BPF_REG_0, 0),
+			/* } */
+			BPF_EXIT_INSN(),
+		},
+		.attach_type = BPF_CGROUP_SETSOCKOPT,
+		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+		.set_optname = 123,
+		.set_optlen = 1,
+		.io_uring_support = true,
+	},
+	{
+		.descr = "setsockopt: deny writing to ctx->is_compat",
+		.insns = {
+			/* ctx->is_compat = 1 */
+			BPF_MOV64_IMM(BPF_REG_0, 1),
+			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+				    offsetof(struct bpf_sockopt, is_compat)),
+			BPF_EXIT_INSN(),
+		},
+		.attach_type = BPF_CGROUP_SETSOCKOPT,
+		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+		.error = DENY_LOAD,
+	},
 	{
 		.descr = "setsockopt: read ctx->optlen",
 		.insns = {
-- 
2.55.0.691.gc56d675ccc-goog


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

* Re: [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_sockopt is_compat field
  2026-08-13 11:20 ` [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_sockopt is_compat field Maciej Żenczykowski
@ 2026-08-13 12:14   ` bot+bpf-ci
  0 siblings, 0 replies; 4+ messages in thread
From: bot+bpf-ci @ 2026-08-13 12:14 UTC (permalink / raw)
  To: maze, zenczykowski, ast, daniel
  Cc: netdev, linux-kernel, bpf, maze, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai

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

> selftests/bpf: Add tests for bpf_sockopt is_compat field
>
> Sync tools/include/uapi/linux/bpf.h with the addition of the 'is_compat'
> field in 'struct bpf_sockopt' and add test cases to 'prog_tests/sockopt.c'
> to verify context access permissions:
>
>  - Test reading 'ctx->is_compat' in 'cgroup/{g,s}etsockopt'
>    (expecting 0 in native execution).
>  - Check verifier rejects writes to 'ctx->is_compat'
>    in 'cgroup/{g,s}etsockopt'.
>
> Tested via:
>   tools/testing/selftests/bpf/vmtest.sh -- ./test_progs -t sockopt

This isn't a bug, but would it be worth separating the 'Tested via:'
line with a blank line and saying explicitly that the compat==1 path is
not reachable from a native test_progs build, so readers know what the
new cases do and do not cover?


---
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/31696027015

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

* Re: [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls
  2026-08-13 11:20 [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls Maciej Żenczykowski
  2026-08-13 11:20 ` [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_sockopt is_compat field Maciej Żenczykowski
@ 2026-08-13 12:59 ` bot+bpf-ci
  1 sibling, 0 replies; 4+ messages in thread
From: bot+bpf-ci @ 2026-08-13 12:59 UTC (permalink / raw)
  To: maze, zenczykowski, ast, daniel
  Cc: netdev, linux-kernel, bpf, maze, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai

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

> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index ffd96e8b920bd..15f712b5b1645 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -7616,6 +7616,7 @@ struct bpf_sockopt {
>  	__s32	optname;
>  	__s32	optlen;
>  	__s32	retval;
> +	__s32	is_compat;
>  };

[ ... ]

> diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
> index 8fbc942a1cc3a..910878e991a04 100644
> --- a/kernel/bpf/cgroup.c
> +++ b/kernel/bpf/cgroup.c

[ ... ]

> @@ -2705,6 +2707,9 @@ static u32 cg_sockopt_convert_ctx_access(enum bpf_access_type type,
>  		else
>  			*insn++ = CG_SOCKOPT_READ_FIELD(optlen);
>  		break;
> +	case offsetof(struct bpf_sockopt, is_compat):
> +		*insn++ = CG_SOCKOPT_READ_FIELD(is_compat);
> +		break;
>  	case offsetof(struct bpf_sockopt, retval):
>  		BUILD_BUG_ON(offsetof(struct bpf_cg_run_ctx, run_ctx) != 0);

Appending __s32 is_compat to struct bpf_sockopt grows the UAPI structure
from 40 to 48 bytes due to 8-byte alignment required by the __bpf_md_ptr()
unions. This creates 4 bytes of unoccupied tail padding at offsets 44-47.

Can the verifier accept a 4-byte load at ctx offset 44?

Looking at cg_sockopt_is_valid_access() in kernel/bpf/cgroup.c, the bounds
check allows any offset under sizeof(struct bpf_sockopt), with alignment:

	if (off < 0 || off >= sizeof(struct bpf_sockopt))
		return false;
	if (off % size != 0)
		return false;

A 4-byte load at offset 44 passes (44 < 48, 44 % 4 == 0), but
cg_sockopt_convert_ctx_access() above has no case for offset 44 and no
default arm. When bpf_convert_ctx_accesses() sees zero instructions
returned, it calls verifier_bug() and triggers WARN_ONCE under
CONFIG_DEBUG_KERNEL, letting any user with cgroup-sockopt load permission
trigger a kernel warning via a BPF_PROG_TYPE_CGROUP_SOCKOPT program with
r0 = *(u32 *)(r1 + 44).

Before this patch every accepted offset had a matching conversion case:
offsets 0-23 (pointer fields) require size 8, and 24, 28, 32, 36 (level,
optname, optlen, retval) were all handled; sizeof was exactly 40 with no
tail padding.

Should the hole be reserved in the UAPI struct (e.g. __u32 :32 or a named
reserved field) and rejected in is_valid_access, or should
cg_sockopt_convert_ctx_access() add an explicit case list or default arm
to reject non-enumerated offsets?

[ ... ]

The patch updates __cgroup_bpf_run_filter_getsockopt() to accept compat
and populate ctx.is_compat, but __cgroup_bpf_run_filter_getsockopt_kern()
was not updated.

This matters because __cgroup_bpf_run_filter_getsockopt_kern() is the only
hook that runs for getsockopt(TCP_ZEROCOPY_RECEIVE). The outer
BPF_CGROUP_RUN_PROG_GETSOCKOPT in do_sock_getsockopt() is bypassed for
that optname via tcp_bpf_bypass_getsockopt(), and do_tcp_getsockopt()
instead calls BPF_CGROUP_RUN_PROG_GETSOCKOPT_KERN() at net/ipv4/tcp.c.
Unlike the two hooks this patch fixes, that kernel-side hook was never
gated on compat, so it already ran for 32-bit callers.

Now it reports ctx->is_compat == 0 for them, violating the documented
contract that the field is "1 for compat syscalls and 0 otherwise." A BPF
program that keys its optval layout decisions off ctx->is_compat will
misparse the compat struct tcp_zerocopy_receive request from a 32-bit task.

Should compat be threaded into
__cgroup_bpf_run_filter_getsockopt_kern() as well, or at minimum should the
documentation clarify that is_compat is unreliable on the kern hook?

[ ... ]

This looks like a bug fix for code introduced in 55db9c0e8534 ("net: remove
compat_sys_{get,set}sockopt"). Should this include:

  Fixes: 55db9c0e8534 ("net: remove compat_sys_{get,set}sockopt")


---
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/31696027015

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

end of thread, other threads:[~2026-08-13 12:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 11:20 [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls Maciej Żenczykowski
2026-08-13 11:20 ` [PATCH bpf-next 2/2] selftests/bpf: Add tests for bpf_sockopt is_compat field Maciej Żenczykowski
2026-08-13 12:14   ` bot+bpf-ci
2026-08-13 12:59 ` [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls bot+bpf-ci

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