mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
bpf: fix BPF_PROG_QUERY OOB write and cgroup backward compat
BPF_PROG_QUERY writes back the 'query.revision' field unconditionally to userspace. If userspace passes a smaller 'bpf_attr' structure (e.g. 40 bytes, which was the layout before the addition of 'query.revision'), the kernel performs an out-of-bounds write. Fix this by propagating the user-provided attribute size 'uattr_size' down to the cgroup query handlers, and conditionally skipping writing the revision field to userspace when the provided buffer size is insufficient. query.revision in bpf_mprog_query is structurally identical to the cgroup case: a late tail field, written unconditionally. But the backward-compat hazard is not the same. The min-historical-size test is per command, and bpf_mprog_query only serves attach types that were born with revision in the struct: - tcx_prog_query -> BPF_TCX_INGRESS/EGRESS - netkit_prog_query -> BPF_NETKIT_PRIMARY/PEER tcx, netkit, the revision field, and bpf_mprog_query itself all landed in the same v6.6 merge window (053c8e1f23added the mprog query API + revision; tcx ine420bed025, netkit in35dfaad718). There has never been a tcx/netkit BPF_PROG_QUERY userspace that doesn't know about revision. So for these commands the minimum legitimate struct already covers offset 56-64 — no old binary can be broken here. Contrast with cgroup: BPF_PROG_QUERY on cgroup attach types shipped in 2017; revision write-back was bolted on years later (1209339844). That path has a real population of pre-revision callers. Fixes:1209339844("bpf: Implement mprog API on top of existing cgroup progs") Cc: Maciej Żenczykowski <maze@google.com> Cc: Lorenzo Colitti <lorenzo@google.com> Signed-off-by: Yuyang Huang <yuyanghuang@google.com> Link: https://lore.kernel.org/r/20260531075600.4058207-2-yuyanghuang@google.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
committed by
Alexei Starovoitov
parent
9a720e090e
commit
21c4b99b27
@@ -421,7 +421,7 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr,
|
||||
enum bpf_prog_type ptype);
|
||||
int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
|
||||
int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr);
|
||||
union bpf_attr __user *uattr, u32 uattr_size);
|
||||
|
||||
const struct bpf_func_proto *
|
||||
cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog);
|
||||
@@ -452,7 +452,8 @@ static inline int cgroup_bpf_link_attach(const union bpf_attr *attr,
|
||||
}
|
||||
|
||||
static inline int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
union bpf_attr __user *uattr,
|
||||
u32 uattr_size)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1208,7 +1208,7 @@ static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
|
||||
|
||||
/* Must be called with cgroup_mutex held to avoid races. */
|
||||
static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
union bpf_attr __user *uattr, u32 uattr_size)
|
||||
{
|
||||
__u32 __user *prog_attach_flags = u64_to_user_ptr(attr->query.prog_attach_flags);
|
||||
bool effective_query = attr->query.query_flags & BPF_F_QUERY_EFFECTIVE;
|
||||
@@ -1259,7 +1259,8 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
return -EFAULT;
|
||||
if (!effective_query && from_atype == to_atype)
|
||||
revision = cgrp->bpf.revisions[from_atype];
|
||||
if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
|
||||
if (uattr_size >= offsetofend(union bpf_attr, query.revision) &&
|
||||
copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
|
||||
return -EFAULT;
|
||||
if (attr->query.prog_cnt == 0 || !prog_ids || !total_cnt)
|
||||
/* return early if user requested only program count + flags */
|
||||
@@ -1312,12 +1313,12 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
}
|
||||
|
||||
static int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
union bpf_attr __user *uattr, u32 uattr_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cgroup_lock();
|
||||
ret = __cgroup_bpf_query(cgrp, attr, uattr);
|
||||
ret = __cgroup_bpf_query(cgrp, attr, uattr, uattr_size);
|
||||
cgroup_unlock();
|
||||
return ret;
|
||||
}
|
||||
@@ -1520,7 +1521,7 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
|
||||
}
|
||||
|
||||
int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
union bpf_attr __user *uattr, u32 uattr_size)
|
||||
{
|
||||
struct cgroup *cgrp;
|
||||
int ret;
|
||||
@@ -1529,7 +1530,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
if (IS_ERR(cgrp))
|
||||
return PTR_ERR(cgrp);
|
||||
|
||||
ret = cgroup_bpf_query(cgrp, attr, uattr);
|
||||
ret = cgroup_bpf_query(cgrp, attr, uattr, uattr_size);
|
||||
|
||||
cgroup_put(cgrp);
|
||||
return ret;
|
||||
|
||||
@@ -4719,7 +4719,7 @@ static int bpf_prog_detach(const union bpf_attr *attr)
|
||||
#define BPF_PROG_QUERY_LAST_FIELD query.revision
|
||||
|
||||
static int bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
union bpf_attr __user *uattr, u32 uattr_size)
|
||||
{
|
||||
if (!bpf_net_capable())
|
||||
return -EPERM;
|
||||
@@ -4758,7 +4758,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
|
||||
case BPF_CGROUP_GETSOCKOPT:
|
||||
case BPF_CGROUP_SETSOCKOPT:
|
||||
case BPF_LSM_CGROUP:
|
||||
return cgroup_bpf_prog_query(attr, uattr);
|
||||
return cgroup_bpf_prog_query(attr, uattr, uattr_size);
|
||||
case BPF_LIRC_MODE2:
|
||||
return lirc_prog_query(attr, uattr);
|
||||
case BPF_FLOW_DISSECTOR:
|
||||
@@ -6376,7 +6376,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
|
||||
err = bpf_prog_detach(&attr);
|
||||
break;
|
||||
case BPF_PROG_QUERY:
|
||||
err = bpf_prog_query(&attr, uattr.user);
|
||||
err = bpf_prog_query(&attr, uattr.user, size);
|
||||
break;
|
||||
case BPF_PROG_TEST_RUN:
|
||||
err = bpf_prog_test_run(&attr, uattr.user);
|
||||
|
||||
Reference in New Issue
Block a user