CVE-2026-45932
Description
In the Linux kernel, the following vulnerability has been resolved:
bpf: Fix tcx/netkit detach permissions when prog fd isn't given
This commit fixes a security issue where BPF_PROG_DETACH on tcx or netkit devices could be executed by any user when no program fd was provided, bypassing permission checks. The fix adds a capability check for CAP_NET_ADMIN or CAP_SYS_ADMIN in this case.
Affected products
1Patches
63f04cc1e5374bpf: Fix tcx/netkit detach permissions when prog fd isn't given
3 files changed · +17 −6
include/linux/bpf.h+5 −0 modifieddiff --git a/include/linux/bpf.h b/include/linux/bpf.h index e5be698256d15a..7b2e51216e736a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3243,6 +3243,11 @@ static inline void bpf_prog_report_arena_violation(bool write, unsigned long add } #endif /* CONFIG_BPF_SYSCALL */ +static inline bool bpf_net_capable(void) +{ + return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); +} + static __always_inline int bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr) {
include/linux/bpf_mprog.h+10 −0 modifieddiff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h index 929225f7b09594..0b9f4caeeb0a32 100644 --- a/include/linux/bpf_mprog.h +++ b/include/linux/bpf_mprog.h @@ -340,4 +340,14 @@ static inline bool bpf_mprog_supported(enum bpf_prog_type type) return false; } } + +static inline bool bpf_mprog_detach_empty(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SCHED_CLS: + return bpf_net_capable(); + default: + return false; + } +} #endif /* __BPF_MPROG_H */
kernel/bpf/syscall.c+2 −6 modifieddiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ee116a3b7baf7d..763868d327b4a7 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1366,11 +1366,6 @@ free_map_tab: return ret; } -static bool bpf_net_capable(void) -{ - return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); -} - #define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size /* called via syscall */ static int map_create(union bpf_attr *attr, bpfptr_t uattr) @@ -4565,6 +4560,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); + } else if (!bpf_mprog_detach_empty(ptype)) { + return -EPERM; } } else if (is_cgroup_prog_type(ptype, 0, false)) { if (attr->attach_flags || attr->relative_fd) -- cgit 1.3-korg
4e0772cded10bpf: Fix tcx/netkit detach permissions when prog fd isn't given
3 files changed · +17 −6
include/linux/bpf.h+5 −0 modifieddiff --git a/include/linux/bpf.h b/include/linux/bpf.h index d808253f2e945d..e2dd3a6d495afd 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3200,6 +3200,11 @@ static inline void bpf_prog_report_arena_violation(bool write, unsigned long add } #endif /* CONFIG_BPF_SYSCALL */ +static inline bool bpf_net_capable(void) +{ + return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); +} + static __always_inline int bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr) {
include/linux/bpf_mprog.h+10 −0 modifieddiff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h index 929225f7b09594..0b9f4caeeb0a32 100644 --- a/include/linux/bpf_mprog.h +++ b/include/linux/bpf_mprog.h @@ -340,4 +340,14 @@ static inline bool bpf_mprog_supported(enum bpf_prog_type type) return false; } } + +static inline bool bpf_mprog_detach_empty(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SCHED_CLS: + return bpf_net_capable(); + default: + return false; + } +} #endif /* __BPF_MPROG_H */
kernel/bpf/syscall.c+2 −6 modifieddiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index e9cf69594824c2..f39367765f0c4f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1365,11 +1365,6 @@ free_map_tab: return ret; } -static bool bpf_net_capable(void) -{ - return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); -} - #define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size /* called via syscall */ static int map_create(union bpf_attr *attr, bpfptr_t uattr) @@ -4554,6 +4549,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); + } else if (!bpf_mprog_detach_empty(ptype)) { + return -EPERM; } } else if (is_cgroup_prog_type(ptype, 0, false)) { if (attr->attach_flags || attr->relative_fd) -- cgit 1.3-korg
ae23bc81ddf7bpf: Fix tcx/netkit detach permissions when prog fd isn't given
3 files changed · +17 −6
include/linux/bpf.h+5 −0 modifieddiff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4427c6e9833196..9272a237cced25 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3362,6 +3362,11 @@ static inline void bpf_prog_report_arena_violation(bool write, unsigned long add } #endif /* CONFIG_BPF_SYSCALL */ +static inline bool bpf_net_capable(void) +{ + return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); +} + static __always_inline int bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr) {
include/linux/bpf_mprog.h+10 −0 modifieddiff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h index 929225f7b09594..0b9f4caeeb0a32 100644 --- a/include/linux/bpf_mprog.h +++ b/include/linux/bpf_mprog.h @@ -340,4 +340,14 @@ static inline bool bpf_mprog_supported(enum bpf_prog_type type) return false; } } + +static inline bool bpf_mprog_detach_empty(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SCHED_CLS: + return bpf_net_capable(); + default: + return false; + } +} #endif /* __BPF_MPROG_H */
kernel/bpf/syscall.c+2 −6 modifieddiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b9184545c3fd09..5f59dd47a5b1c3 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1363,11 +1363,6 @@ free_map_tab: return ret; } -static bool bpf_net_capable(void) -{ - return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); -} - #define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size /* called via syscall */ static int map_create(union bpf_attr *attr, bpfptr_t uattr) @@ -4579,6 +4574,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); + } else if (!bpf_mprog_detach_empty(ptype)) { + return -EPERM; } } else if (is_cgroup_prog_type(ptype, 0, false)) { if (attr->attach_flags || attr->relative_fd) -- cgit 1.3-korg
3f04cc1e5374bpf: Fix tcx/netkit detach permissions when prog fd isn't given
3 files changed · +17 −6
include/linux/bpf.h+5 −0 modifieddiff --git a/include/linux/bpf.h b/include/linux/bpf.h index e5be698256d15a..7b2e51216e736a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3243,6 +3243,11 @@ static inline void bpf_prog_report_arena_violation(bool write, unsigned long add } #endif /* CONFIG_BPF_SYSCALL */ +static inline bool bpf_net_capable(void) +{ + return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); +} + static __always_inline int bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr) {
include/linux/bpf_mprog.h+10 −0 modifieddiff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h index 929225f7b09594..0b9f4caeeb0a32 100644 --- a/include/linux/bpf_mprog.h +++ b/include/linux/bpf_mprog.h @@ -340,4 +340,14 @@ static inline bool bpf_mprog_supported(enum bpf_prog_type type) return false; } } + +static inline bool bpf_mprog_detach_empty(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SCHED_CLS: + return bpf_net_capable(); + default: + return false; + } +} #endif /* __BPF_MPROG_H */
kernel/bpf/syscall.c+2 −6 modifieddiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ee116a3b7baf7d..763868d327b4a7 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1366,11 +1366,6 @@ free_map_tab: return ret; } -static bool bpf_net_capable(void) -{ - return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); -} - #define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size /* called via syscall */ static int map_create(union bpf_attr *attr, bpfptr_t uattr) @@ -4565,6 +4560,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); + } else if (!bpf_mprog_detach_empty(ptype)) { + return -EPERM; } } else if (is_cgroup_prog_type(ptype, 0, false)) { if (attr->attach_flags || attr->relative_fd) -- cgit 1.3-korg
ae23bc81ddf7bpf: Fix tcx/netkit detach permissions when prog fd isn't given
3 files changed · +17 −6
include/linux/bpf.h+5 −0 modifieddiff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4427c6e9833196..9272a237cced25 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3362,6 +3362,11 @@ static inline void bpf_prog_report_arena_violation(bool write, unsigned long add } #endif /* CONFIG_BPF_SYSCALL */ +static inline bool bpf_net_capable(void) +{ + return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); +} + static __always_inline int bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr) {
include/linux/bpf_mprog.h+10 −0 modifieddiff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h index 929225f7b09594..0b9f4caeeb0a32 100644 --- a/include/linux/bpf_mprog.h +++ b/include/linux/bpf_mprog.h @@ -340,4 +340,14 @@ static inline bool bpf_mprog_supported(enum bpf_prog_type type) return false; } } + +static inline bool bpf_mprog_detach_empty(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SCHED_CLS: + return bpf_net_capable(); + default: + return false; + } +} #endif /* __BPF_MPROG_H */
kernel/bpf/syscall.c+2 −6 modifieddiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b9184545c3fd09..5f59dd47a5b1c3 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1363,11 +1363,6 @@ free_map_tab: return ret; } -static bool bpf_net_capable(void) -{ - return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); -} - #define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size /* called via syscall */ static int map_create(union bpf_attr *attr, bpfptr_t uattr) @@ -4579,6 +4574,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); + } else if (!bpf_mprog_detach_empty(ptype)) { + return -EPERM; } } else if (is_cgroup_prog_type(ptype, 0, false)) { if (attr->attach_flags || attr->relative_fd) -- cgit 1.3-korg
4e0772cded10bpf: Fix tcx/netkit detach permissions when prog fd isn't given
3 files changed · +17 −6
include/linux/bpf.h+5 −0 modifieddiff --git a/include/linux/bpf.h b/include/linux/bpf.h index d808253f2e945d..e2dd3a6d495afd 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -3200,6 +3200,11 @@ static inline void bpf_prog_report_arena_violation(bool write, unsigned long add } #endif /* CONFIG_BPF_SYSCALL */ +static inline bool bpf_net_capable(void) +{ + return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); +} + static __always_inline int bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr) {
include/linux/bpf_mprog.h+10 −0 modifieddiff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h index 929225f7b09594..0b9f4caeeb0a32 100644 --- a/include/linux/bpf_mprog.h +++ b/include/linux/bpf_mprog.h @@ -340,4 +340,14 @@ static inline bool bpf_mprog_supported(enum bpf_prog_type type) return false; } } + +static inline bool bpf_mprog_detach_empty(enum bpf_prog_type type) +{ + switch (type) { + case BPF_PROG_TYPE_SCHED_CLS: + return bpf_net_capable(); + default: + return false; + } +} #endif /* __BPF_MPROG_H */
kernel/bpf/syscall.c+2 −6 modifieddiff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index e9cf69594824c2..f39367765f0c4f 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1365,11 +1365,6 @@ free_map_tab: return ret; } -static bool bpf_net_capable(void) -{ - return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN); -} - #define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size /* called via syscall */ static int map_create(union bpf_attr *attr, bpfptr_t uattr) @@ -4554,6 +4549,8 @@ static int bpf_prog_detach(const union bpf_attr *attr) prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); + } else if (!bpf_mprog_detach_empty(ptype)) { + return -EPERM; } } else if (is_cgroup_prog_type(ptype, 0, false)) { if (attr->attach_flags || attr->relative_fd) -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing permission check in BPF_PROG_DETACH for tcx/netkit when no program file descriptor is provided."
Attack vector
An unprivileged local user can invoke the BPF_PROG_DETACH operation on a tcx or netkit device without providing a program file descriptor (attr->attach_bpf_fd == 0). In the original code, when no fd was given, the kernel skipped the bpf_prog_get_type() call (which performs permission checks via the file descriptor) and proceeded directly to detachment without any capability check. This allowed any local user to detach BPF programs from tcx/netkit interfaces, bypassing the intended CAP_NET_ADMIN or CAP_SYS_ADMIN requirement [patch_id=2661231].
Affected code
The vulnerable code path is in kernel/bpf/syscall.c within the bpf_prog_detach() function. When attr->attach_bpf_fd is 0 (no program fd provided) and the program type is BPF_PROG_TYPE_SCHED_CLS (tcx/netkit), the original code performed no permission check before detaching. The fix adds a call to bpf_mprog_detach_empty() defined in include/linux/bpf_mprog.h, which invokes bpf_net_capable() (moved to include/linux/bpf.h) to enforce capability checks [patch_id=2661231].
What the fix does
The patch adds a new helper function bpf_mprog_detach_empty() in include/linux/bpf_mprog.h that returns bpf_net_capable() (which checks for CAP_NET_ADMIN or CAP_SYS_ADMIN) when the program type is BPF_PROG_TYPE_SCHED_CLS (used by tcx/netkit). In kernel/bpf/syscall.c, the bpf_prog_detach() function now calls this helper when no program fd is provided; if the check fails, it returns -EPERM. The bpf_net_capable() function was also moved from being a local static in syscall.c to an inline function in include/linux/bpf.h so it can be reused by the new mprog helper [patch_id=2661231][patch_id=2661228].
Preconditions
- inputThe attacker must have local access to the system and be able to invoke the BPF_PROG_DETACH syscall.
- configA tcx or netkit device must have at least one BPF program attached.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.