Merge branch 'Fix wrong cgroup attach flags being assigned to effective progs'
authorMartin KaFai Lau <martin.lau@kernel.org>
Wed, 21 Sep 2022 17:57:13 +0000 (10:57 -0700)
committerMartin KaFai Lau <martin.lau@kernel.org>
Wed, 21 Sep 2022 17:57:13 +0000 (10:57 -0700)
Pu Lehui says:

====================

From: Pu Lehui <pulehui@huawei.com>

When root-cgroup attach multi progs and sub-cgroup attach a
override prog, bpftool will display incorrectly for the attach
flags of the sub-cgroup’s effective progs:

$ bpftool cgroup tree /sys/fs/cgroup effective
CgroupPath
ID       AttachType      AttachFlags     Name
/sys/fs/cgroup
6        cgroup_sysctl   multi           sysctl_tcp_mem
13       cgroup_sysctl   multi           sysctl_tcp_mem
/sys/fs/cgroup/cg1
20       cgroup_sysctl   override        sysctl_tcp_mem
6        cgroup_sysctl   override        sysctl_tcp_mem <- wrong
13       cgroup_sysctl   override        sysctl_tcp_mem <- wrong
/sys/fs/cgroup/cg1/cg2
20       cgroup_sysctl                   sysctl_tcp_mem
6        cgroup_sysctl                   sysctl_tcp_mem
13       cgroup_sysctl                   sysctl_tcp_mem

For cg1, obviously, the attach flags of prog6 and prog13 can not be
OVERRIDE. And for query with EFFECTIVE flags, exporting attach flags
does not make sense. So let's remove the AttachFlags field and the
associated logic. After these patches, the above effective cgroup
tree will show as bellow:

# bpftool cgroup tree /sys/fs/cgroup effective
CgroupPath
ID       AttachType      Name
/sys/fs/cgroup
6        cgroup_sysctl   sysctl_tcp_mem
13       cgroup_sysctl   sysctl_tcp_mem
/sys/fs/cgroup/cg1
20       cgroup_sysctl   sysctl_tcp_mem
6        cgroup_sysctl   sysctl_tcp_mem
13       cgroup_sysctl   sysctl_tcp_mem
/sys/fs/cgroup/cg1/cg2
20       cgroup_sysctl   sysctl_tcp_mem
6        cgroup_sysctl   sysctl_tcp_mem
13       cgroup_sysctl   sysctl_tcp_mem

v5:
- Adapt selftests for effective query uapi change.

v4:
https://lore.kernel.org/bpf/20220920154233.1494352-1-pulehui@huaweicloud.com
- Reject prog_attach_flags array when effective query. (Martin)
- Target to bpf tree. (Martin)

v3:
https://lore.kernel.org/bpf/20220914161742.3180731-1-pulehui@huaweicloud.com
- Don't show attach flags when effective query. (John, sdf, Martin)

v2:
https://lore.kernel.org/bpf/20220908145304.3436139-1-pulehui@huaweicloud.com
- Limit prog_cnt to avoid overflow. (John)
- Add more detail message.

v1:
https://lore.kernel.org/bpf/20220820120234.2121044-1-pulehui@huawei.com
====================

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
include/uapi/linux/bpf.h
kernel/bpf/cgroup.c
tools/bpf/bpftool/cgroup.c
tools/include/uapi/linux/bpf.h
tools/testing/selftests/bpf/prog_tests/cgroup_link.c

index 59a217c..4eff7fc 100644 (file)
@@ -1233,7 +1233,7 @@ enum {
 
 /* Query effective (directly attached + inherited from ancestor cgroups)
  * programs that will be executed for events within a cgroup.
- * attach_flags with this flag are returned only for directly attached programs.
+ * attach_flags with this flag are always returned 0.
  */
 #define BPF_F_QUERY_EFFECTIVE  (1U << 0)
 
@@ -1432,7 +1432,10 @@ union bpf_attr {
                __u32           attach_flags;
                __aligned_u64   prog_ids;
                __u32           prog_cnt;
-               __aligned_u64   prog_attach_flags; /* output: per-program attach_flags */
+               /* output: per-program attach_flags.
+                * not allowed to be set during effective query.
+                */
+               __aligned_u64   prog_attach_flags;
        } query;
 
        struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */
index 4a400cd..22888aa 100644 (file)
@@ -1020,6 +1020,7 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
                              union bpf_attr __user *uattr)
 {
        __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;
        __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
        enum bpf_attach_type type = attr->query.attach_type;
        enum cgroup_bpf_attach_type from_atype, to_atype;
@@ -1029,8 +1030,12 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
        int total_cnt = 0;
        u32 flags;
 
+       if (effective_query && prog_attach_flags)
+               return -EINVAL;
+
        if (type == BPF_LSM_CGROUP) {
-               if (attr->query.prog_cnt && prog_ids && !prog_attach_flags)
+               if (!effective_query && attr->query.prog_cnt &&
+                   prog_ids && !prog_attach_flags)
                        return -EINVAL;
 
                from_atype = CGROUP_LSM_START;
@@ -1045,7 +1050,7 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
        }
 
        for (atype = from_atype; atype <= to_atype; atype++) {
-               if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) {
+               if (effective_query) {
                        effective = rcu_dereference_protected(cgrp->bpf.effective[atype],
                                                              lockdep_is_held(&cgroup_mutex));
                        total_cnt += bpf_prog_array_length(effective);
@@ -1054,6 +1059,8 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
                }
        }
 
+       /* always output uattr->query.attach_flags as 0 during effective query */
+       flags = effective_query ? 0 : flags;
        if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
                return -EFAULT;
        if (copy_to_user(&uattr->query.prog_cnt, &total_cnt, sizeof(total_cnt)))
@@ -1068,7 +1075,7 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
        }
 
        for (atype = from_atype; atype <= to_atype && total_cnt; atype++) {
-               if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) {
+               if (effective_query) {
                        effective = rcu_dereference_protected(cgrp->bpf.effective[atype],
                                                              lockdep_is_held(&cgroup_mutex));
                        cnt = min_t(int, bpf_prog_array_length(effective), total_cnt);
@@ -1090,15 +1097,16 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
                                if (++i == cnt)
                                        break;
                        }
-               }
 
-               if (prog_attach_flags) {
-                       flags = cgrp->bpf.flags[atype];
+                       if (prog_attach_flags) {
+                               flags = cgrp->bpf.flags[atype];
 
-                       for (i = 0; i < cnt; i++)
-                               if (copy_to_user(prog_attach_flags + i, &flags, sizeof(flags)))
-                                       return -EFAULT;
-                       prog_attach_flags += cnt;
+                               for (i = 0; i < cnt; i++)
+                                       if (copy_to_user(prog_attach_flags + i,
+                                                        &flags, sizeof(flags)))
+                                               return -EFAULT;
+                               prog_attach_flags += cnt;
+                       }
                }
 
                prog_ids += cnt;
index cced668..b46a998 100644 (file)
@@ -136,8 +136,8 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
                        jsonw_string_field(json_wtr, "attach_type", attach_type_str);
                else
                        jsonw_uint_field(json_wtr, "attach_type", attach_type);
-               jsonw_string_field(json_wtr, "attach_flags",
-                                  attach_flags_str);
+               if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
+                       jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
                jsonw_string_field(json_wtr, "name", prog_name);
                if (attach_btf_name)
                        jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
@@ -150,7 +150,10 @@ static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
                        printf("%-15s", attach_type_str);
                else
                        printf("type %-10u", attach_type);
-               printf(" %-15s %-15s", attach_flags_str, prog_name);
+               if (query_flags & BPF_F_QUERY_EFFECTIVE)
+                       printf(" %-15s", prog_name);
+               else
+                       printf(" %-15s %-15s", attach_flags_str, prog_name);
                if (attach_btf_name)
                        printf(" %-15s", attach_btf_name);
                else if (info.attach_btf_id)
@@ -195,6 +198,32 @@ static int cgroup_has_attached_progs(int cgroup_fd)
 
        return no_prog ? 0 : 1;
 }
+
+static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
+                                   int level)
+{
+       LIBBPF_OPTS(bpf_prog_query_opts, p);
+       __u32 prog_ids[1024] = {0};
+       __u32 iter;
+       int ret;
+
+       p.query_flags = query_flags;
+       p.prog_cnt = ARRAY_SIZE(prog_ids);
+       p.prog_ids = prog_ids;
+
+       ret = bpf_prog_query_opts(cgroup_fd, type, &p);
+       if (ret)
+               return ret;
+
+       if (p.prog_cnt == 0)
+               return 0;
+
+       for (iter = 0; iter < p.prog_cnt; iter++)
+               show_bpf_prog(prog_ids[iter], type, NULL, level);
+
+       return 0;
+}
+
 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
                                   int level)
 {
@@ -245,6 +274,14 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
        return 0;
 }
 
+static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
+                         int level)
+{
+       return query_flags & BPF_F_QUERY_EFFECTIVE ?
+              show_effective_bpf_progs(cgroup_fd, type, level) :
+              show_attached_bpf_progs(cgroup_fd, type, level);
+}
+
 static int do_show(int argc, char **argv)
 {
        enum bpf_attach_type type;
@@ -292,6 +329,8 @@ static int do_show(int argc, char **argv)
 
        if (json_output)
                jsonw_start_array(json_wtr);
+       else if (query_flags & BPF_F_QUERY_EFFECTIVE)
+               printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
        else
                printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
                       "AttachFlags", "Name");
@@ -304,7 +343,7 @@ static int do_show(int argc, char **argv)
                 * If we were able to get the show for at least one
                 * attach type, let's return 0.
                 */
-               if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
+               if (show_bpf_progs(cgroup_fd, type, 0) == 0)
                        ret = 0;
        }
 
@@ -362,7 +401,7 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb,
 
        btf_vmlinux = libbpf_find_kernel_btf();
        for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
-               show_attached_bpf_progs(cgroup_fd, type, ftw->level);
+               show_bpf_progs(cgroup_fd, type, ftw->level);
 
        if (errno == EINVAL)
                /* Last attach type does not support query.
@@ -436,6 +475,11 @@ static int do_show_tree(int argc, char **argv)
 
        if (json_output)
                jsonw_start_array(json_wtr);
+       else if (query_flags & BPF_F_QUERY_EFFECTIVE)
+               printf("%s\n"
+                      "%-8s %-15s %-15s\n",
+                      "CgroupPath",
+                      "ID", "AttachType", "Name");
        else
                printf("%s\n"
                       "%-8s %-15s %-15s %-15s\n",
index 59a217c..4eff7fc 100644 (file)
@@ -1233,7 +1233,7 @@ enum {
 
 /* Query effective (directly attached + inherited from ancestor cgroups)
  * programs that will be executed for events within a cgroup.
- * attach_flags with this flag are returned only for directly attached programs.
+ * attach_flags with this flag are always returned 0.
  */
 #define BPF_F_QUERY_EFFECTIVE  (1U << 0)
 
@@ -1432,7 +1432,10 @@ union bpf_attr {
                __u32           attach_flags;
                __aligned_u64   prog_ids;
                __u32           prog_cnt;
-               __aligned_u64   prog_attach_flags; /* output: per-program attach_flags */
+               /* output: per-program attach_flags.
+                * not allowed to be set during effective query.
+                */
+               __aligned_u64   prog_attach_flags;
        } query;
 
        struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */
index 9e6e6aa..15093a6 100644 (file)
@@ -71,10 +71,9 @@ void serial_test_cgroup_link(void)
 
        ping_and_check(cg_nr, 0);
 
-       /* query the number of effective progs and attach flags in root cg */
+       /* query the number of attached progs and attach flags in root cg */
        err = bpf_prog_query(cgs[0].fd, BPF_CGROUP_INET_EGRESS,
-                            BPF_F_QUERY_EFFECTIVE, &attach_flags, NULL,
-                            &prog_cnt);
+                            0, &attach_flags, NULL, &prog_cnt);
        CHECK_FAIL(err);
        CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
        if (CHECK(prog_cnt != 1, "effect_cnt", "exp %d, got %d\n", 1, prog_cnt))
@@ -85,17 +84,15 @@ void serial_test_cgroup_link(void)
                             BPF_F_QUERY_EFFECTIVE, NULL, NULL,
                             &prog_cnt);
        CHECK_FAIL(err);
-       CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
        if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
                  cg_nr, prog_cnt))
                goto cleanup;
 
        /* query the effective prog IDs in last cg */
        err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
-                            BPF_F_QUERY_EFFECTIVE, &attach_flags,
-                            prog_ids, &prog_cnt);
+                            BPF_F_QUERY_EFFECTIVE, NULL, prog_ids,
+                            &prog_cnt);
        CHECK_FAIL(err);
-       CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
        if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
                  cg_nr, prog_cnt))
                goto cleanup;