Skip to content

Commit 21c4b99

Browse files
Yuyang HuangAlexei Starovoitov
authored andcommitted
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 (053c8e1 added the mprog query API + revision; tcx in e420bed, netkit in 35dfaad). 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 (1209339). That path has a real population of pre-revision callers. Fixes: 1209339 ("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>
1 parent 9a720e0 commit 21c4b99

3 files changed

Lines changed: 13 additions & 11 deletions

File tree

include/linux/bpf-cgroup.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr,
421421
enum bpf_prog_type ptype);
422422
int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
423423
int cgroup_bpf_prog_query(const union bpf_attr *attr,
424-
union bpf_attr __user *uattr);
424+
union bpf_attr __user *uattr, u32 uattr_size);
425425

426426
const struct bpf_func_proto *
427427
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,
452452
}
453453

454454
static inline int cgroup_bpf_prog_query(const union bpf_attr *attr,
455-
union bpf_attr __user *uattr)
455+
union bpf_attr __user *uattr,
456+
u32 uattr_size)
456457
{
457458
return -EINVAL;
458459
}

kernel/bpf/cgroup.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
12081208

12091209
/* Must be called with cgroup_mutex held to avoid races. */
12101210
static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
1211-
union bpf_attr __user *uattr)
1211+
union bpf_attr __user *uattr, u32 uattr_size)
12121212
{
12131213
__u32 __user *prog_attach_flags = u64_to_user_ptr(attr->query.prog_attach_flags);
12141214
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,
12591259
return -EFAULT;
12601260
if (!effective_query && from_atype == to_atype)
12611261
revision = cgrp->bpf.revisions[from_atype];
1262-
if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
1262+
if (uattr_size >= offsetofend(union bpf_attr, query.revision) &&
1263+
copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
12631264
return -EFAULT;
12641265
if (attr->query.prog_cnt == 0 || !prog_ids || !total_cnt)
12651266
/* 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,
13121313
}
13131314

13141315
static int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
1315-
union bpf_attr __user *uattr)
1316+
union bpf_attr __user *uattr, u32 uattr_size)
13161317
{
13171318
int ret;
13181319

13191320
cgroup_lock();
1320-
ret = __cgroup_bpf_query(cgrp, attr, uattr);
1321+
ret = __cgroup_bpf_query(cgrp, attr, uattr, uattr_size);
13211322
cgroup_unlock();
13221323
return ret;
13231324
}
@@ -1520,7 +1521,7 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
15201521
}
15211522

15221523
int cgroup_bpf_prog_query(const union bpf_attr *attr,
1523-
union bpf_attr __user *uattr)
1524+
union bpf_attr __user *uattr, u32 uattr_size)
15241525
{
15251526
struct cgroup *cgrp;
15261527
int ret;
@@ -1529,7 +1530,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr,
15291530
if (IS_ERR(cgrp))
15301531
return PTR_ERR(cgrp);
15311532

1532-
ret = cgroup_bpf_query(cgrp, attr, uattr);
1533+
ret = cgroup_bpf_query(cgrp, attr, uattr, uattr_size);
15331534

15341535
cgroup_put(cgrp);
15351536
return ret;

kernel/bpf/syscall.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4719,7 +4719,7 @@ static int bpf_prog_detach(const union bpf_attr *attr)
47194719
#define BPF_PROG_QUERY_LAST_FIELD query.revision
47204720

47214721
static int bpf_prog_query(const union bpf_attr *attr,
4722-
union bpf_attr __user *uattr)
4722+
union bpf_attr __user *uattr, u32 uattr_size)
47234723
{
47244724
if (!bpf_net_capable())
47254725
return -EPERM;
@@ -4758,7 +4758,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
47584758
case BPF_CGROUP_GETSOCKOPT:
47594759
case BPF_CGROUP_SETSOCKOPT:
47604760
case BPF_LSM_CGROUP:
4761-
return cgroup_bpf_prog_query(attr, uattr);
4761+
return cgroup_bpf_prog_query(attr, uattr, uattr_size);
47624762
case BPF_LIRC_MODE2:
47634763
return lirc_prog_query(attr, uattr);
47644764
case BPF_FLOW_DISSECTOR:
@@ -6376,7 +6376,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,
63766376
err = bpf_prog_detach(&attr);
63776377
break;
63786378
case BPF_PROG_QUERY:
6379-
err = bpf_prog_query(&attr, uattr.user);
6379+
err = bpf_prog_query(&attr, uattr.user, size);
63806380
break;
63816381
case BPF_PROG_TEST_RUN:
63826382
err = bpf_prog_test_run(&attr, uattr.user);

0 commit comments

Comments
 (0)