// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ #include #include #include #include "task_kfunc_common.h" char _license[] SEC("license") = "GPL"; int err, pid; /* Prototype for all of the program trace events below: * * TRACE_EVENT(task_newtask, * TP_PROTO(struct task_struct *p, u64 clone_flags) */ struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym __weak; void invalid_kfunc(void) __ksym __weak; void bpf_testmod_test_mod_kfunc(int i) __ksym __weak; static bool is_test_kfunc_task(void) { int cur_pid = bpf_get_current_pid_tgid() >> 32; return pid == cur_pid; } static int test_acquire_release(struct task_struct *task) { struct task_struct *acquired = NULL; if (!bpf_ksym_exists(bpf_task_acquire)) { err = 3; return 0; } if (!bpf_ksym_exists(bpf_testmod_test_mod_kfunc)) { err = 4; return 0; } if (bpf_ksym_exists(invalid_kfunc)) { /* the verifier's dead code elimination should remove this */ err = 5; asm volatile ("goto -1"); /* for (;;); */ } acquired = bpf_task_acquire(task); if (acquired) bpf_task_release(acquired); else err = 6; return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_acquire_release_argument, struct task_struct *task, u64 clone_flags) { if (!is_test_kfunc_task()) return 0; return test_acquire_release(task); } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_acquire_release_current, struct task_struct *task, u64 clone_flags) { if (!is_test_kfunc_task()) return 0; return test_acquire_release(bpf_get_current_task_btf()); } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_acquire_leave_in_map, struct task_struct *task, u64 clone_flags) { long status; if (!is_test_kfunc_task()) return 0; status = tasks_kfunc_map_insert(task); if (status) err = 1; return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_xchg_release, struct task_struct *task, u64 clone_flags) { struct task_struct *kptr; struct __tasks_kfunc_map_value *v; long status; if (!is_test_kfunc_task()) return 0; status = tasks_kfunc_map_insert(task); if (status) { err = 1; return 0; } v = tasks_kfunc_map_value_lookup(task); if (!v) { err = 2; return 0; } kptr = bpf_kptr_xchg(&v->task, NULL); if (!kptr) { err = 3; return 0; } bpf_task_release(kptr); return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_map_acquire_release, struct task_struct *task, u64 clone_flags) { struct task_struct *kptr; struct __tasks_kfunc_map_value *v; long status; if (!is_test_kfunc_task()) return 0; status = tasks_kfunc_map_insert(task); if (status) { err = 1; return 0; } v = tasks_kfunc_map_value_lookup(task); if (!v) { err = 2; return 0; } bpf_rcu_read_lock(); kptr = v->task; if (!kptr) { err = 3; } else { kptr = bpf_task_acquire(kptr); if (!kptr) err = 4; else bpf_task_release(kptr); } bpf_rcu_read_unlock(); return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_current_acquire_release, struct task_struct *task, u64 clone_flags) { struct task_struct *current, *acquired; if (!is_test_kfunc_task()) return 0; current = bpf_get_current_task_btf(); acquired = bpf_task_acquire(current); if (acquired) bpf_task_release(acquired); else err = 1; return 0; } static void lookup_compare_pid(const struct task_struct *p) { struct task_struct *acquired; acquired = bpf_task_from_pid(p->pid); if (!acquired) { err = 1; return; } if (acquired->pid != p->pid) err = 2; bpf_task_release(acquired); } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_from_pid_arg, struct task_struct *task, u64 clone_flags) { if (!is_test_kfunc_task()) return 0; lookup_compare_pid(task); return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_from_pid_current, struct task_struct *task, u64 clone_flags) { if (!is_test_kfunc_task()) return 0; lookup_compare_pid(bpf_get_current_task_btf()); return 0; } static int is_pid_lookup_valid(s32 pid) { struct task_struct *acquired; acquired = bpf_task_from_pid(pid); if (acquired) { bpf_task_release(acquired); return 1; } return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(test_task_from_pid_invalid, struct task_struct *task, u64 clone_flags) { if (!is_test_kfunc_task()) return 0; bpf_strncmp(task->comm, 12, "foo"); bpf_strncmp(task->comm, 16, "foo"); bpf_strncmp(&task->comm[8], 4, "foo"); if (is_pid_lookup_valid(-1)) { err = 1; return 0; } if (is_pid_lookup_valid(0xcafef00d)) { err = 2; return 0; } return 0; } SEC("tp_btf/task_newtask") int BPF_PROG(task_kfunc_acquire_trusted_walked, struct task_struct *task, u64 clone_flags) { struct task_struct *acquired; /* task->group_leader is listed as a trusted, non-NULL field of task struct. */ acquired = bpf_task_acquire(task->group_leader); if (acquired) bpf_task_release(acquired); else err = 1; return 0; }