tcp: restrict SO_ATTACH_BPF to privileged users
Subsystems (5)
PATCH 1/1 tcp: restrict SO_ATTACH_BPF to privileged users
Commit 5d39580f68e6 ("tcp: restrict SO_ATTACH_FILTER to priv users") requires CAP_NET_ADMIN to attach a classic BPF filter to a TCP socket. However, a caller can still attach an eBPF socket filter through SO_ATTACH_BPF without this capability. Both options install a filter that runs on incoming TCP packets.
Disabling unprivileged BPF loading does not close this gap: a caller that already holds a socket-filter program file descriptor can still use SO_ATTACH_BPF.
Use one CAP_NET_ADMIN check for both attachment options before taking the socket lock. Use the same namespace-aware capability helper already used by SO_ATTACH_FILTER. Leave other socket types unchanged.
--- net/core/sock.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
net/core/sock.c +6 -5
@@ -1310,6 +1310,12 @@ int sk_setsockopt(struct sock *sk, int level, int optname, optlen, optname == SO_RCVTIMEO_OLD); } +/* Restrict TCP socket filters to prevent sequence/ack-number probing. */ +if ((optname ==SO_ATTACH_FILTER || optname ==SO_ATTACH_BPF ) && +sk_is_tcp (sk) && + !sockopt_ns_capable (sock_net (sk)->user_ns,CAP_NET_ADMIN )) +return -EPERM ; +sockopt_lock_sock (sk);switch (optname) {
@@ -1466,11 +1472,6 @@ int sk_setsockopt(struct sock *sk, int level, int optname,case SO_ATTACH_FILTER : {struct sock_fprog fprog; -if (sk_is_tcp (sk) && - !sockopt_ns_capable (sock_net (sk)->user_ns,CAP_NET_ADMIN )) { - ret = -EPERM ; -break ; - } ret =copy_bpf_fprog_from_user (&fprog, optval, optlen);if (!ret) ret =sk_attach_filter (&fprog, sk);
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
Signature · 1 line
2.52.0
Quoted 5 lines · Manjunath Patil
Commit
5d39580f68e6("tcp: restrictSO_ATTACH_FILTERto priv users") requiresCAP_NET_ADMINto attach a classic BPF filter to a TCP socket. However, a caller can still attach an eBPF socket filter throughSO_ATTACH_BPFwithout this capability. Both options install a filter that runs on incoming TCP packets.
Nack.SO_ATTACH_FILTER takes insns that the caller wrote. SO_ATTACH_BPF takes a prog FD, and BPF_PROG_LOAD is the gate for it. unprivileged_bpf_disabled is 2 by default, so the load needs CAP_BPF. An admin who sets it to 0 allows every user to load socket filters.
Quoted 3 lines
Disabling unprivileged BPF loading does not close this gap: a caller that already holds a socket-filter program file descriptor can still use
SO_ATTACH_BPF.
That's not a gap. The FD is the permission.
A process with CAP_BPF loads the prog and passes the FD to a process without caps. See the comment in bpf_prog_load() and
commit c8644cd0efe7 ("bpf: refine kernel.unprivileged_bpf_disabled behaviour"). This patch breaks it. It also breaks a process that has CAP_BPF and doesn't have CAP_NET_ADMIN.
pw-bot: cr
Hi,
Quoted 10 lines · Alexei Starovoitov
On Wed, Oct 28, 2026 at 02: 58 PM Manjunath Patil <manjunath. b. patil@ oracle. com> wrote: > Commit
5d39580f68e6("tcp: restrictSO_ATTACH_FILTERto priv users") > requiresCAP_NET_ADMINto attach a classic BPF filter to a TCP socket.Quoted 5 lines · Manjunath Patil
Commit
5d39580f68e6("tcp: restrictSO_ATTACH_FILTERto priv users") requiresCAP_NET_ADMINto attach a classic BPF filter to a TCP socket. However, a caller can still attach an eBPF socket filter throughSO_ATTACH_BPFwithout this capability. Both options install a filter that runs on incoming TCP packets.
Quoted 3 lines
Nack.
SO_ATTACH_FILTERtakes insns that the caller wrote.SO_ATTACH_BPFtakes a prog FD, andBPF_PROG_LOADis the gate for it.
Thanks for the explanation. I overlooked this distinction and treated the different attachment checks as a missing permission check.
Quoted 10 lines
unprivileged_bpf_disabledis 2 by default, so the load needsCAP_BPF. An admin who sets it to 0 allows every user to load socket filters.Quoted 3 lines
Disabling unprivileged BPF loading does not close this gap: a caller that already holds a socket-filter program file descriptor can still use
SO_ATTACH_BPF.That's not a gap. The FD is the permission.
A process withCAP_BPFloads the prog and passes the FD to a process without caps.
My test loaded the socket-filter program as root, then retained its FD in a worker that dropped its capabilities. That exercises supported delegation; it does not establish a security bypass.
See the comment in bpf_prog_load() and
Quoted 3 lines
commit
c8644cd0efe7("bpf: refine kernel.unprivileged_bpf_disabled behaviour"). This patch breaks it. It also breaks a process that hasCAP_BPFand doesn't haveCAP_NET_ADMIN.
I agree. I am withdrawing this patch.
-Thanks,
Manjunath>
Quoted 2 lines
pw-bot: cr