From: "Maciej Żenczykowski" <maze@google.com>
To: "Maciej Żenczykowski" <zenczykowski@gmail.com>,
"Alexei Starovoitov" <ast@kernel.org>,
"Daniel Borkmann" <daniel@iogearbox.net>
Cc: "Linux Network Development Mailing List" <netdev@vger.kernel.org>,
"Linux Kernel Mailing List" <linux-kernel@vger.kernel.org>,
"BPF Mailing List" <bpf@vger.kernel.org>,
"Maciej Żenczykowski" <maze@google.com>
Subject: [PATCH bpf-next 1/2] bpf: Support cgroup {get,set}sockopt hooks for compat syscalls
Date: Thu, 13 Aug 2026 04:20:38 -0700 [thread overview]
Message-ID: <20260813112039.247862-1-maze@google.com> (raw)
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
next reply other threads:[~2026-08-13 11:20 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-13 11:20 Maciej Żenczykowski [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260813112039.247862-1-maze@google.com \
--to=maze@google.com \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=zenczykowski@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox