From: Ren Zhijie renzhijie2@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I5F6X6 CVE: NA
-------------------
* Load/attach a BPF program that hooks to functions: bpf_sched_cfs_check_preempt_tick, bpf_sched_cfs_check_preempt_wakeup, bpf_sched_cfs_wakeup_preempt_entity * Perform an action that triggers the hooks. * Verify 3 helpers can capture task with the specified tgidpid or belongs to specified cgroup.
Signed-off-by: Ren Zhijie renzhijie2@huawei.com --- .../selftests/bpf/prog_tests/test_sched.c | 161 +++++++++++++++++ tools/testing/selftests/bpf/progs/sched.c | 165 ++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/test_sched.c create mode 100644 tools/testing/selftests/bpf/progs/sched.c
diff --git a/tools/testing/selftests/bpf/prog_tests/test_sched.c b/tools/testing/selftests/bpf/prog_tests/test_sched.c new file mode 100644 index 000000000000..28af31b4386f --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/test_sched.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2022. Huawei Technologies Co., Ltd. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <test_progs.h> + +#include "sched.skel.h" +#include "cgroup_helpers.h" + +#define CHECK_TGIDPID_MODE(HOOKNAME, TGIDPID) \ + do { \ + if (skel->bss->HOOKNAME##_tgidpid_ret) { \ + CHECK(skel->bss->HOOKNAME##_tgidpid_ret != TGIDPID, \ + #HOOKNAME"_tgidpid", #HOOKNAME"_tgidpid_ret %lu\n", \ + skel->bss->HOOKNAME##_tgidpid_ret); \ + } \ + } while (0) + +#define CHECK_CGID_MODE(HOOKNAME, PID, CGID) \ + do { \ + if (skel->bss->HOOKNAME##_cgid_ret) { \ + if (skel->bss->HOOKNAME##_cgid_ret) { \ + CHECK(skel->bss->HOOKNAME##_cgid_pid_ret != PID, \ + #HOOKNAME"_cgid_pid", #HOOKNAME"_cgid_pid_ret %u\n", \ + skel->bss->HOOKNAME##_cgid_pid_ret); \ + } \ + if (skel->bss->HOOKNAME##_cgid_se_to_cgid_ret) { \ + CHECK(skel->bss->HOOKNAME##_cgid_se_to_cgid_ret != CGID, \ + #HOOKNAME"_cgid_se_to_cgid", \ + #HOOKNAME"_cgid_se_to_cgid_ret %lu\n", \ + skel->bss->HOOKNAME##_cgid_se_to_cgid_ret); \ + } \ + } \ + } while (0) + +static void work(void) +{ + int i; + + for (i = 0; i < 1000; i++) + usleep(1000); +} + +int create_prioritize_task(int *child_pid) +{ + int cpid; + + cpid = fork(); + if (cpid == -1) { + return -ECHILD; + } else if (cpid == 0) { + work(); + exit(0); + } else { + *child_pid = cpid; + return 0; + } + return -EINVAL; +} + +void test_sched_tgidpid_mode(void) +{ + struct sched *skel = NULL; + int err, duration = 0, child_pid = 0, tgid = 0, cgid = 0; + int status = 0; + + skel = sched__open(); + if (CHECK(!skel, "open", "sched open failed\n")) + goto close_prog; + + err = sched__load(skel); + if (CHECK(err, "load", "sched load failed: %d\n", err)) + goto close_prog; + + err = sched__attach(skel); + if (CHECK(err, "attach", "sched attach failed: %d\n", err)) + goto close_prog; + + err = create_prioritize_task(&child_pid); + if (CHECK(err < 0, "create_prior_task", "err %d errno %d\n", err, errno)) + goto close_prog; + + tgid = child_pid; + skel->bss->tgidpid = (unsigned long)tgid << 32 | child_pid; + skel->bss->cgid = cgid; + + if (child_pid) + err = waitpid(child_pid, &status, 0); + if (CHECK(err == -1 && errno != ECHILD, "waitpid", "failed %d", errno)) + goto close_prog; + + CHECK_TGIDPID_MODE(tick, skel->bss->tgidpid); + CHECK_TGIDPID_MODE(wakeup, skel->bss->tgidpid); + CHECK_TGIDPID_MODE(entity, skel->bss->tgidpid); + +close_prog: + sched__destroy(skel); +} + +#define TEST_CGROUP "/test-bpf-sched-cgid-mode/" + +void test_sched_cgid_mode(void) +{ + struct sched *skel = NULL; + int err, duration = 0, cgid = 0, cgroup_fd = 0, pid = 0; + + skel = sched__open(); + if (CHECK(!skel, "open", "sched open failed\n")) + goto close_prog; + + err = sched__load(skel); + if (CHECK(err, "load", "sched load failed: %d\n", err)) + goto close_prog; + + err = sched__attach(skel); + if (CHECK(err, "attach", "sched attach failed: %d\n", err)) + goto close_prog; + + cgroup_fd = cgroup_setup_and_join(TEST_CGROUP); + if (CHECK(cgroup_fd < 0, "cgroup_setup_and_join", "err %d errno %d\n", cgroup_fd, errno)) + goto cleanup_cgroup_env; + + cgid = get_cgroup_id(TEST_CGROUP); + if (CHECK(!cgid, "get_cgroup_id", "err %d", cgid)) + goto cleanup_cgroup_env; + + skel->bss->tgidpid = 0; + skel->bss->cgid = cgid; + + /* trigger sched hook */ + work(); + + pid = getpid(); + + CHECK_CGID_MODE(tick, pid, cgid); + CHECK_CGID_MODE(wakeup, pid, cgid); + CHECK_CGID_MODE(entity, pid, cgid); + +cleanup_cgroup_env: + cleanup_cgroup_environment(); +close_prog: + sched__destroy(skel); +} + +void test_test_sched(int argc, char **argv) +{ + if (test__start_subtest("sched_tgidpid_mode")) + test_sched_tgidpid_mode(); + if (test__start_subtest("sched_cgid_mode")) + test_sched_cgid_mode(); +} diff --git a/tools/testing/selftests/bpf/progs/sched.c b/tools/testing/selftests/bpf/progs/sched.c new file mode 100644 index 000000000000..e1e09345ca72 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/sched.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2022. Huawei Technologies Co., Ltd. All rights reserved. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include <errno.h> + +#ifndef NULL +#define NULL 0 +#endif + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} array SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} hash SEC(".maps"); + +char _license[] SEC("license") = "GPL"; + +unsigned long tgidpid; +unsigned long cgid; + +unsigned long tick_tgidpid_ret; +unsigned int tick_cgid_ret; +unsigned int tick_cgid_pid_ret; +unsigned long tick_cgid_se_to_cgid_ret; + +unsigned long wakeup_tgidpid_ret; +unsigned int wakeup_cgid_ret; +unsigned int wakeup_cgid_pid_ret; +unsigned long wakeup_cgid_se_to_cgid_ret; + +unsigned long entity_tgidpid_ret; +unsigned int entity_cgid_ret; +unsigned int entity_cgid_pid_ret; +unsigned long entity_cgid_se_to_cgid_ret; + + +SEC("sched/cfs_check_preempt_tick") +int BPF_PROG(test_check_preempt_tick, struct sched_entity *curr, unsigned long delta_exec) +{ + unsigned long curr_tgidpid; + + if (curr == NULL) + return 0; + + if (tgidpid) { + curr_tgidpid = bpf_sched_entity_to_tgidpid(curr); + if (curr_tgidpid == tgidpid) + tick_tgidpid_ret = curr_tgidpid; + } else if (cgid) { + if (bpf_sched_entity_belongs_to_cgrp(curr, cgid)) { + tick_cgid_ret = 1; + + if (!curr->my_q) { + curr_tgidpid = bpf_sched_entity_to_tgidpid(curr); + tick_cgid_pid_ret = curr_tgidpid & 0xFFFFFFFF; + } + + if (curr->my_q) + tick_cgid_se_to_cgid_ret = bpf_sched_entity_to_cgrpid(curr); + } + } + return 0; +} + +SEC("sched/cfs_check_preempt_wakeup") +int BPF_PROG(test_check_preempt_wakeup, struct task_struct *curr, struct task_struct *p) +{ + __u64 *value = NULL; + __u32 key = 0; + + if (curr == NULL || p == NULL) + return 0; + + value = bpf_map_lookup_elem(&array, &key); + if (value) + *value = 0; + value = bpf_map_lookup_elem(&hash, &key); + if (value) + *value = 0; + + if (tgidpid) { + unsigned long curr_tgidpid, p_tgidpid; + + curr_tgidpid = bpf_sched_entity_to_tgidpid(&curr->se); + p_tgidpid = bpf_sched_entity_to_tgidpid(&p->se); + + if (curr_tgidpid == tgidpid) + wakeup_tgidpid_ret = curr_tgidpid; + else if (p_tgidpid == tgidpid) + wakeup_tgidpid_ret = p_tgidpid; + } else if (cgid) { + if (bpf_sched_entity_belongs_to_cgrp(&curr->se, cgid)) { + wakeup_cgid_ret = 1; + wakeup_cgid_pid_ret = curr->pid; + } else if (bpf_sched_entity_belongs_to_cgrp(&p->se, cgid)) { + wakeup_cgid_ret = 1; + wakeup_cgid_pid_ret = p->pid; + } + } + return 0; +} + +SEC("sched/cfs_wakeup_preempt_entity") +int BPF_PROG(test_wakeup_preempt_entity, struct sched_entity *curr, struct sched_entity *se) +{ + unsigned long curr_tgidpid, se_tgidpid; + + if (curr == NULL || se == NULL) + return 0; + + if (tgidpid) { + curr_tgidpid = bpf_sched_entity_to_tgidpid(curr); + se_tgidpid = bpf_sched_entity_to_tgidpid(se); + + if (curr_tgidpid == tgidpid) + entity_tgidpid_ret = curr_tgidpid; + else if (se_tgidpid == tgidpid) + entity_tgidpid_ret = se_tgidpid; + } else if (cgid) { + if (bpf_sched_entity_belongs_to_cgrp(curr, cgid)) { + entity_cgid_ret = 1; + + if (!curr->my_q) { + curr_tgidpid = bpf_sched_entity_to_tgidpid(curr); + entity_cgid_pid_ret = curr_tgidpid & 0xFFFFFFFF; + } + + if (curr->my_q) + entity_cgid_se_to_cgid_ret = bpf_sched_entity_to_cgrpid(curr); + } else if (bpf_sched_entity_belongs_to_cgrp(se, cgid)) { + entity_cgid_ret = 1; + + if (!se->my_q) { + se_tgidpid = bpf_sched_entity_to_tgidpid(se); + entity_cgid_pid_ret = se_tgidpid & 0xFFFFFFFF; + } + + if (se->my_q) + entity_cgid_se_to_cgid_ret = bpf_sched_entity_to_cgrpid(se); + } + } + return 0; +}