mirror of
https://github.com/torvalds/linux.git
synced 2026-09-11 12:08:02 +02:00
Merge branch 'misc-bug-fixes-part-4'
Kumar Kartikeya Dwivedi says: ==================== Misc bug fixes - part 4 A set of miscellaneous fixes for bugs reported by Nicholas, and GPT-5.6 when analyzing those fixes, batched together again. See commit logs for details. Related rhtab fixes from Yuan Chen and Nuoqi Gui have been folded into the series. ==================== Link: https://patch.msgid.link/20260904104203.345917-1-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
519f858317
|
|
@ -2868,16 +2868,6 @@ static int rhtab_map_alloc_check(union bpf_attr *attr)
|
|||
return htab_map_alloc_check(attr);
|
||||
}
|
||||
|
||||
static void rhtab_check_and_free_fields(struct bpf_rhtab *rhtab,
|
||||
struct rhtab_elem *elem)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(rhtab->map.record))
|
||||
return;
|
||||
|
||||
bpf_obj_free_fields(rhtab->map.record,
|
||||
rhtab_elem_value(elem, rhtab->map.key_size));
|
||||
}
|
||||
|
||||
static void rhtab_mem_dtor(void *obj, void *ctx)
|
||||
{
|
||||
struct htab_btf_record *hrec = ctx;
|
||||
|
|
@ -2967,8 +2957,8 @@ static int rhtab_delete_elem(struct bpf_rhtab *rhtab, struct rhtab_elem *elem, v
|
|||
rhtab_read_elem_value(&rhtab->map, copy, elem, flags);
|
||||
check_and_init_map_value(&rhtab->map, copy);
|
||||
}
|
||||
/* Release internal structs: kptr, bpf_timer, task_work, wq */
|
||||
rhtab_check_and_free_fields(rhtab, elem);
|
||||
bpf_obj_cancel_fields(&rhtab->map,
|
||||
rhtab_elem_value(elem, rhtab->map.key_size));
|
||||
bpf_mem_cache_free_rcu(&rhtab->ma, elem);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3009,7 +2999,6 @@ static int rhtab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, void
|
|||
static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *elem, void *value,
|
||||
u64 map_flags)
|
||||
{
|
||||
struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map);
|
||||
void *old_val = rhtab_elem_value(elem, map->key_size);
|
||||
|
||||
if (map_flags & BPF_NOEXIST)
|
||||
|
|
@ -3029,7 +3018,7 @@ static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *el
|
|||
* kptrs/etc. still sit in the slot. Cancel them after the copy
|
||||
* to match arraymap's update semantics.
|
||||
*/
|
||||
rhtab_check_and_free_fields(rhtab, elem);
|
||||
bpf_obj_cancel_fields(map, old_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3070,7 +3059,6 @@ static long rhtab_map_update_elem(struct bpf_map *map, void *key, void *value, u
|
|||
|
||||
memcpy(elem->data, key, map->key_size);
|
||||
copy_map_value(map, rhtab_elem_value(elem, map->key_size), value);
|
||||
check_and_init_map_value(map, rhtab_elem_value(elem, map->key_size));
|
||||
|
||||
/* Prevent deadlock for NMI programs attempting to take bucket lock */
|
||||
bpf_disable_instrumentation();
|
||||
|
|
|
|||
|
|
@ -4708,8 +4708,15 @@ static int check_map_kptr_access(struct bpf_verifier_env *env,
|
|||
return ret;
|
||||
} else if (class == BPF_STX) {
|
||||
val_reg = reg_state(env, value_regno);
|
||||
if (!bpf_register_is_null(val_reg) &&
|
||||
map_kptr_match_type(env, kptr_field, val_reg, value_regno))
|
||||
if (bpf_register_is_null(val_reg)) {
|
||||
/*
|
||||
* This store is valid only because the scalar is known to be
|
||||
* zero. Mark it precise so another scalar cannot be pruned
|
||||
* against this state.
|
||||
*/
|
||||
return mark_chain_precision(env, value_regno);
|
||||
}
|
||||
if (map_kptr_match_type(env, kptr_field, val_reg, value_regno))
|
||||
return -EACCES;
|
||||
} else if (class == BPF_ST) {
|
||||
if (insn->imm) {
|
||||
|
|
@ -10011,10 +10018,12 @@ int map_set_for_each_callback_args(struct bpf_verifier_env *env,
|
|||
callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_2]);
|
||||
callee->regs[BPF_REG_2].map_ptr = caller->regs[BPF_REG_1].map_ptr;
|
||||
callee->regs[BPF_REG_2].map_uid = caller->regs[BPF_REG_1].map_uid;
|
||||
|
||||
callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_3]);
|
||||
callee->regs[BPF_REG_3].map_ptr = caller->regs[BPF_REG_1].map_ptr;
|
||||
callee->regs[BPF_REG_3].map_uid = caller->regs[BPF_REG_1].map_uid;
|
||||
|
||||
/* pointer to stack or null */
|
||||
callee->regs[BPF_REG_4] = caller->regs[BPF_REG_3];
|
||||
|
|
@ -10092,6 +10101,7 @@ static int set_timer_callback_state(struct bpf_verifier_env *env,
|
|||
int insn_idx)
|
||||
{
|
||||
struct bpf_map *map_ptr = caller->regs[BPF_REG_1].map_ptr;
|
||||
u32 map_uid = caller->regs[BPF_REG_1].map_uid;
|
||||
|
||||
/* bpf_timer_set_callback(struct bpf_timer *timer, void *callback_fn);
|
||||
* callback_fn(struct bpf_map *map, void *key, void *value);
|
||||
|
|
@ -10099,14 +10109,17 @@ static int set_timer_callback_state(struct bpf_verifier_env *env,
|
|||
callee->regs[BPF_REG_1].type = CONST_PTR_TO_MAP;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_1]);
|
||||
callee->regs[BPF_REG_1].map_ptr = map_ptr;
|
||||
callee->regs[BPF_REG_1].map_uid = map_uid;
|
||||
|
||||
callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_2]);
|
||||
callee->regs[BPF_REG_2].map_ptr = map_ptr;
|
||||
callee->regs[BPF_REG_2].map_uid = map_uid;
|
||||
|
||||
callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_3]);
|
||||
callee->regs[BPF_REG_3].map_ptr = map_ptr;
|
||||
callee->regs[BPF_REG_3].map_uid = map_uid;
|
||||
|
||||
/* unused */
|
||||
bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
|
||||
|
|
@ -10206,6 +10219,7 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env,
|
|||
int insn_idx)
|
||||
{
|
||||
struct bpf_map *map_ptr = caller->regs[BPF_REG_3].map_ptr;
|
||||
u32 map_uid = caller->regs[BPF_REG_3].map_uid;
|
||||
|
||||
/*
|
||||
* callback_fn(struct bpf_map *map, void *key, void *value);
|
||||
|
|
@ -10213,14 +10227,17 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env,
|
|||
callee->regs[BPF_REG_1].type = CONST_PTR_TO_MAP;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_1]);
|
||||
callee->regs[BPF_REG_1].map_ptr = map_ptr;
|
||||
callee->regs[BPF_REG_1].map_uid = map_uid;
|
||||
|
||||
callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_2]);
|
||||
callee->regs[BPF_REG_2].map_ptr = map_ptr;
|
||||
callee->regs[BPF_REG_2].map_uid = map_uid;
|
||||
|
||||
callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE;
|
||||
__mark_reg_known_zero(&callee->regs[BPF_REG_3]);
|
||||
callee->regs[BPF_REG_3].map_ptr = map_ptr;
|
||||
callee->regs[BPF_REG_3].map_uid = map_uid;
|
||||
|
||||
/* unused */
|
||||
bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
|
||||
|
|
|
|||
|
|
@ -172,6 +172,12 @@ void test_rhash(void)
|
|||
if (test__start_subtest("test_rhash_delete_nonexistent"))
|
||||
rhash_run("test_rhash_delete_nonexistent");
|
||||
|
||||
if (test__start_subtest("test_rhash_kptr_update"))
|
||||
rhash_run("test_rhash_kptr_update");
|
||||
|
||||
if (test__start_subtest("test_rhash_kptr_delete"))
|
||||
rhash_run("test_rhash_kptr_delete");
|
||||
|
||||
if (test__start_subtest("test_rhash_map_extra_presize"))
|
||||
rhash_map_extra_presize();
|
||||
|
||||
|
|
|
|||
141
tools/testing/selftests/bpf/prog_tests/rhash_timer.c
Normal file
141
tools/testing/selftests/bpf/prog_tests/rhash_timer.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "rhash_timer.skel.h"
|
||||
|
||||
#define MAX_ATTEMPTS 256
|
||||
#define RCU_SYNC_INTERVAL 64
|
||||
|
||||
static int pin_to_first_cpu(cpu_set_t *old_mask)
|
||||
{
|
||||
cpu_set_t new_mask;
|
||||
int cpu;
|
||||
|
||||
if (sched_getaffinity(0, sizeof(*old_mask), old_mask))
|
||||
return -errno;
|
||||
|
||||
for (cpu = 0; cpu < CPU_SETSIZE; cpu++)
|
||||
if (CPU_ISSET(cpu, old_mask))
|
||||
break;
|
||||
if (cpu == CPU_SETSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
CPU_ZERO(&new_mask);
|
||||
CPU_SET(cpu, &new_mask);
|
||||
if (sched_setaffinity(0, sizeof(new_mask), &new_mask))
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_timer_map(int map_fd, __u64 key)
|
||||
{
|
||||
__u64 value[3] = {};
|
||||
|
||||
return bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST);
|
||||
}
|
||||
|
||||
static int run_prog(int prog_fd, struct bpf_test_run_opts *opts)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bpf_prog_test_run_opts(prog_fd, opts);
|
||||
if (err)
|
||||
return err;
|
||||
return opts->retval;
|
||||
}
|
||||
|
||||
void test_rhash_timer(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, opts);
|
||||
struct rhash_timer *skel = NULL;
|
||||
cpu_set_t old_mask;
|
||||
int map_fd = -1, arm_fd, cancel_fd;
|
||||
bool affinity_set = false;
|
||||
__u64 key = 1;
|
||||
int attempt, err;
|
||||
|
||||
err = pin_to_first_cpu(&old_mask);
|
||||
if (!ASSERT_OK(err, "pin_to_first_cpu"))
|
||||
return;
|
||||
affinity_set = true;
|
||||
|
||||
skel = rhash_timer__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "open_and_load"))
|
||||
goto out;
|
||||
|
||||
map_fd = bpf_map__fd(skel->maps.timer_map);
|
||||
if (!ASSERT_GE(map_fd, 0, "timer_map fd"))
|
||||
goto out;
|
||||
arm_fd = bpf_program__fd(skel->progs.arm_deleted_timer);
|
||||
if (!ASSERT_GE(arm_fd, 0, "arm_deleted_timer fd"))
|
||||
goto out;
|
||||
cancel_fd = bpf_program__fd(skel->progs.cancel_recycled_timer);
|
||||
if (!ASSERT_GE(cancel_fd, 0, "cancel_recycled_timer fd"))
|
||||
goto out;
|
||||
|
||||
err = update_timer_map(map_fd, key);
|
||||
if (!ASSERT_OK(err, "seed_timer_map"))
|
||||
goto out;
|
||||
|
||||
for (attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
||||
err = run_prog(arm_fd, &opts);
|
||||
if (err) {
|
||||
ASSERT_OK(err, "arm_deleted_timer");
|
||||
goto out;
|
||||
}
|
||||
if (skel->bss->armed != attempt + 1) {
|
||||
ASSERT_EQ(skel->bss->armed, attempt + 1, "armed");
|
||||
goto out;
|
||||
}
|
||||
if (skel->bss->timer_init_err) {
|
||||
ASSERT_OK(skel->bss->timer_init_err, "timer_init_err");
|
||||
goto out;
|
||||
}
|
||||
if (skel->bss->timer_set_callback_err) {
|
||||
ASSERT_OK(skel->bss->timer_set_callback_err,
|
||||
"timer_set_callback_err");
|
||||
goto out;
|
||||
}
|
||||
if (skel->bss->timer_start_err) {
|
||||
ASSERT_OK(skel->bss->timer_start_err, "timer_start_err");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((attempt + 1) % RCU_SYNC_INTERVAL == 0) {
|
||||
err = kern_sync_rcu();
|
||||
if (err) {
|
||||
ASSERT_OK(err, "kern_sync_rcu");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = update_timer_map(map_fd, ++key);
|
||||
if (err) {
|
||||
ASSERT_OK(err, "replace_timer_map");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = run_prog(cancel_fd, &opts);
|
||||
if (err) {
|
||||
ASSERT_OK(err, "cancel_recycled_timer");
|
||||
goto out;
|
||||
}
|
||||
if (skel->bss->timer_cancel_err) {
|
||||
ASSERT_OK(skel->bss->timer_cancel_err, "timer_cancel_err");
|
||||
goto out;
|
||||
}
|
||||
if (skel->bss->cancelled)
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_GT(skel->bss->cancelled, 0, "preserved timer");
|
||||
out:
|
||||
if (map_fd >= 0)
|
||||
bpf_map_delete_elem(map_fd, &key);
|
||||
rhash_timer__destroy(skel);
|
||||
if (affinity_set)
|
||||
sched_setaffinity(0, sizeof(old_mask), &old_mask);
|
||||
}
|
||||
|
|
@ -59,10 +59,32 @@ void serial_test_timer_mim(void)
|
|||
int err;
|
||||
|
||||
old_print_fn = libbpf_set_print(NULL);
|
||||
timer_reject_skel = timer_mim_reject__open_and_load();
|
||||
libbpf_set_print(old_print_fn);
|
||||
if (!ASSERT_ERR_PTR(timer_reject_skel, "timer_reject_skel_load"))
|
||||
timer_reject_skel = timer_mim_reject__open();
|
||||
if (!ASSERT_OK_PTR(timer_reject_skel, "timer_reject_skel_open"))
|
||||
goto cleanup;
|
||||
bpf_program__set_autoload(timer_reject_skel->progs.test1, true);
|
||||
err = timer_mim_reject__load(timer_reject_skel);
|
||||
ASSERT_ERR(err, "timer_reject_skel_load");
|
||||
timer_mim_reject__destroy(timer_reject_skel);
|
||||
|
||||
timer_reject_skel = timer_mim_reject__open();
|
||||
if (!ASSERT_OK_PTR(timer_reject_skel, "callback_reject_skel_open"))
|
||||
goto cleanup;
|
||||
bpf_program__set_autoload(timer_reject_skel->progs.callback_map_uid_mismatch, true);
|
||||
err = timer_mim_reject__load(timer_reject_skel);
|
||||
ASSERT_ERR(err, "callback_reject_skel_load");
|
||||
timer_mim_reject__destroy(timer_reject_skel);
|
||||
|
||||
timer_reject_skel = timer_mim_reject__open();
|
||||
if (!ASSERT_OK_PTR(timer_reject_skel, "callback_accept_skel_open"))
|
||||
goto cleanup;
|
||||
bpf_program__set_autoload(timer_reject_skel->progs.callback_map_uid_match, true);
|
||||
err = timer_mim_reject__load(timer_reject_skel);
|
||||
if (!ASSERT_OK(err, "callback_accept_skel_load"))
|
||||
goto cleanup;
|
||||
timer_mim_reject__destroy(timer_reject_skel);
|
||||
timer_reject_skel = NULL;
|
||||
libbpf_set_print(old_print_fn);
|
||||
|
||||
timer_skel = timer_mim__open_and_load();
|
||||
if (!timer_skel && errno == EOPNOTSUPP) {
|
||||
|
|
@ -75,6 +97,7 @@ void serial_test_timer_mim(void)
|
|||
err = timer_mim(timer_skel);
|
||||
ASSERT_OK(err, "timer_mim");
|
||||
cleanup:
|
||||
libbpf_set_print(old_print_fn);
|
||||
timer_mim__destroy(timer_skel);
|
||||
timer_mim_reject__destroy(timer_reject_skel);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -409,4 +409,41 @@ int reject_scalar_store_to_kptr(struct __sk_buff *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__description("reject imprecise scalar store to kptr after state pruning")
|
||||
__failure __msg("invalid kptr access, R7 type=scalar")
|
||||
__naked void reject_imprecise_scalar_store_to_kptr(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = 0;"
|
||||
"*(u32 *)(r10 - 4) = r0;"
|
||||
"r2 = r10;"
|
||||
"r2 += -4;"
|
||||
"r1 = %[array_map] ll;"
|
||||
"call %[bpf_map_lookup_elem];"
|
||||
"if r0 == 0 goto l2_%=;"
|
||||
"r6 = r0;"
|
||||
"r9 = *(u64 *)(r6 + 0);"
|
||||
"if r9 != 0 goto l0_%=;"
|
||||
"r7 = 0;"
|
||||
".rept 10;"
|
||||
"r5 = 1;"
|
||||
".endr;"
|
||||
"goto l1_%=;"
|
||||
"l0_%=:"
|
||||
"r7 = 0x4141414141414141 ll;"
|
||||
".rept 10;"
|
||||
"r5 = 1;"
|
||||
".endr;"
|
||||
"l1_%=:"
|
||||
"*(u64 *)(r6 + 8) = r7;"
|
||||
"l2_%=:"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_map_lookup_elem),
|
||||
__imm_addr(array_map)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ struct elem {
|
|||
int val;
|
||||
};
|
||||
|
||||
struct special_elem {
|
||||
struct task_struct __kptr *task;
|
||||
int val;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RHASH);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
|
|
@ -27,6 +32,17 @@ struct {
|
|||
__type(value, struct elem);
|
||||
} rhmap SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RHASH);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, int);
|
||||
__type(value, struct special_elem);
|
||||
} special_fields SEC(".maps");
|
||||
|
||||
extern struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;
|
||||
extern void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
SEC("syscall")
|
||||
int test_rhash_lookup_update(void *ctx)
|
||||
{
|
||||
|
|
@ -246,3 +262,99 @@ int test_rhash_delete_nonexistent(void *ctx)
|
|||
err = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
int test_rhash_kptr_update(void *ctx)
|
||||
{
|
||||
struct special_elem val1 = { .val = 1 };
|
||||
struct special_elem val2 = { .val = 2 };
|
||||
struct task_struct *task, *old;
|
||||
struct special_elem *elem;
|
||||
int key = 0;
|
||||
|
||||
err = 1;
|
||||
if (bpf_map_update_elem(&special_fields, &key, &val1, BPF_NOEXIST))
|
||||
return 1;
|
||||
|
||||
err = 2;
|
||||
elem = bpf_map_lookup_elem(&special_fields, &key);
|
||||
if (!elem)
|
||||
return 2;
|
||||
|
||||
err = 3;
|
||||
task = bpf_task_acquire(bpf_get_current_task_btf());
|
||||
if (!task)
|
||||
return 3;
|
||||
|
||||
err = 4;
|
||||
old = bpf_kptr_xchg(&elem->task, task);
|
||||
if (old) {
|
||||
bpf_task_release(old);
|
||||
return 4;
|
||||
}
|
||||
|
||||
err = 5;
|
||||
if (bpf_map_update_elem(&special_fields, &key, &val2, BPF_EXIST))
|
||||
return 5;
|
||||
|
||||
err = 6;
|
||||
elem = bpf_map_lookup_elem(&special_fields, &key);
|
||||
if (!elem || elem->val != 2)
|
||||
return 6;
|
||||
|
||||
err = 7;
|
||||
old = bpf_kptr_xchg(&elem->task, NULL);
|
||||
if (!old)
|
||||
return 7;
|
||||
bpf_task_release(old);
|
||||
|
||||
err = 8;
|
||||
if (bpf_map_delete_elem(&special_fields, &key))
|
||||
return 8;
|
||||
|
||||
err = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
int test_rhash_kptr_delete(void *ctx)
|
||||
{
|
||||
struct special_elem val = {};
|
||||
struct task_struct *task, *old;
|
||||
struct special_elem *elem;
|
||||
int key = 0;
|
||||
|
||||
err = 1;
|
||||
if (bpf_map_update_elem(&special_fields, &key, &val, BPF_NOEXIST))
|
||||
return 1;
|
||||
|
||||
err = 2;
|
||||
elem = bpf_map_lookup_elem(&special_fields, &key);
|
||||
if (!elem)
|
||||
return 2;
|
||||
|
||||
err = 3;
|
||||
task = bpf_task_acquire(bpf_get_current_task_btf());
|
||||
if (!task)
|
||||
return 3;
|
||||
|
||||
err = 4;
|
||||
old = bpf_kptr_xchg(&elem->task, task);
|
||||
if (old) {
|
||||
bpf_task_release(old);
|
||||
return 4;
|
||||
}
|
||||
|
||||
err = 5;
|
||||
if (bpf_map_delete_elem(&special_fields, &key))
|
||||
return 5;
|
||||
|
||||
err = 6;
|
||||
old = bpf_kptr_xchg(&elem->task, NULL);
|
||||
if (!old)
|
||||
return 6;
|
||||
bpf_task_release(old);
|
||||
|
||||
err = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
98
tools/testing/selftests/bpf/progs/rhash_timer.c
Normal file
98
tools/testing/selftests/bpf/progs/rhash_timer.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <errno.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#define CLOCK_MONOTONIC 1
|
||||
#define TIMER_NSEC (60ULL * 1000 * 1000 * 1000)
|
||||
|
||||
struct timer_value {
|
||||
struct bpf_timer timer;
|
||||
u64 data;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RHASH);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, u64);
|
||||
__type(value, struct timer_value);
|
||||
} timer_map SEC(".maps");
|
||||
|
||||
u64 armed;
|
||||
u64 cancelled;
|
||||
long timer_init_err;
|
||||
long timer_set_callback_err;
|
||||
long timer_start_err;
|
||||
long timer_cancel_err;
|
||||
|
||||
static int timer_cb(void *map, u64 *key, struct timer_value *value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long arm_timer_cb(struct bpf_map *map, u64 *key,
|
||||
struct timer_value *value, void *ctx)
|
||||
{
|
||||
u64 key_copy = *key;
|
||||
long err;
|
||||
|
||||
err = bpf_map_delete_elem(map, &key_copy);
|
||||
if (err)
|
||||
return 1;
|
||||
|
||||
err = bpf_timer_init(&value->timer, map, CLOCK_MONOTONIC);
|
||||
if (err) {
|
||||
timer_init_err = err;
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = bpf_timer_set_callback(&value->timer, timer_cb);
|
||||
if (err) {
|
||||
timer_set_callback_err = err;
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = bpf_timer_start(&value->timer, TIMER_NSEC, BPF_F_TIMER_CPU_PIN);
|
||||
if (err) {
|
||||
timer_start_err = err;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__sync_fetch_and_add(&armed, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long cancel_timer_cb(struct bpf_map *map, u64 *key,
|
||||
struct timer_value *value, void *ctx)
|
||||
{
|
||||
long err;
|
||||
|
||||
err = bpf_timer_cancel(&value->timer);
|
||||
if (err == -EINVAL)
|
||||
return 1;
|
||||
if (err < 0) {
|
||||
timer_cancel_err = err;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__sync_fetch_and_add(&cancelled, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
int arm_deleted_timer(void *ctx)
|
||||
{
|
||||
bpf_for_each_map_elem(&timer_map, arm_timer_cb, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
int cancel_recycled_timer(void *ctx)
|
||||
{
|
||||
bpf_for_each_map_elem(&timer_map, cancel_timer_cb, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
@ -43,7 +43,7 @@ static int timer_cb(void *map, int *key, struct hmap_elem *val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/bpf_fentry_test1")
|
||||
SEC("?fentry/bpf_fentry_test1")
|
||||
int BPF_PROG(test1, int a)
|
||||
{
|
||||
struct hmap_elem init = {};
|
||||
|
|
@ -72,3 +72,85 @@ int BPF_PROG(test1, int a)
|
|||
err |= 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct callback_ctx {
|
||||
void *map;
|
||||
};
|
||||
|
||||
static int mismatch_iter_cb(void *map, int *key, struct hmap_elem *val, struct callback_ctx *ctx)
|
||||
{
|
||||
bpf_timer_init(&val->timer, ctx->map, CLOCK_MONOTONIC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timer_mismatch_cb(void *map, int *key, struct hmap_elem *val)
|
||||
{
|
||||
struct callback_ctx ctx = { .map = map };
|
||||
struct bpf_map *inner_map2;
|
||||
int array_key2 = ARRAY_KEY2;
|
||||
|
||||
inner_map2 = bpf_map_lookup_elem(&outer_arr, &array_key2);
|
||||
if (!inner_map2)
|
||||
return 0;
|
||||
bpf_for_each_map_elem(inner_map2, mismatch_iter_cb, &ctx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_iter_cb(void *map, int *key, struct hmap_elem *val, struct callback_ctx *ctx)
|
||||
{
|
||||
bpf_timer_init(&val->timer, map, CLOCK_MONOTONIC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timer_match_cb(void *map, int *key, struct hmap_elem *val)
|
||||
{
|
||||
struct callback_ctx ctx = {};
|
||||
struct bpf_map *inner_map2;
|
||||
int array_key2 = ARRAY_KEY2;
|
||||
|
||||
inner_map2 = bpf_map_lookup_elem(&outer_arr, &array_key2);
|
||||
if (!inner_map2)
|
||||
return 0;
|
||||
bpf_for_each_map_elem(inner_map2, match_iter_cb, &ctx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry/bpf_fentry_test1")
|
||||
int BPF_PROG(callback_map_uid_mismatch, int a)
|
||||
{
|
||||
struct hmap_elem *val;
|
||||
struct bpf_map *inner_map;
|
||||
int array_key = ARRAY_KEY;
|
||||
int hash_key = HASH_KEY;
|
||||
|
||||
inner_map = bpf_map_lookup_elem(&outer_arr, &array_key);
|
||||
if (!inner_map)
|
||||
return 0;
|
||||
val = bpf_map_lookup_elem(inner_map, &hash_key);
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
bpf_timer_init(&val->timer, inner_map, CLOCK_MONOTONIC);
|
||||
bpf_timer_set_callback(&val->timer, timer_mismatch_cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry/bpf_fentry_test1")
|
||||
int BPF_PROG(callback_map_uid_match, int a)
|
||||
{
|
||||
struct hmap_elem *val;
|
||||
struct bpf_map *inner_map;
|
||||
int array_key = ARRAY_KEY;
|
||||
int hash_key = HASH_KEY;
|
||||
|
||||
inner_map = bpf_map_lookup_elem(&outer_arr, &array_key);
|
||||
if (!inner_map)
|
||||
return 0;
|
||||
val = bpf_map_lookup_elem(inner_map, &hash_key);
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
bpf_timer_init(&val->timer, inner_map, CLOCK_MONOTONIC);
|
||||
bpf_timer_set_callback(&val->timer, timer_match_cb);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user