From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 550DB31283C for ; Fri, 21 Nov 2025 23:14:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763766843; cv=none; b=ovubEDe61qN/oc+noDTNpaifV8nqHcwOkga02Hbk+SveaAQfqP1wIuiN3ZnKSqOFaR06HrhbFFZJ5vM0YAapwKL7AzzBtzW4hDpy8mqVnMILlFI9oXPes35hFC5+KDrPcLYBlzTRKTcvWMcgnucRA3zTqxRMODvSL+vt9dkKvEU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763766843; c=relaxed/simple; bh=j3HhbcDJGAZ+KHPYwNusGru13+91oPjZ9of1S1u8p4Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NQy732ZdZYqp1S1BS+tPyFHzJz7M1PCl5grgE+GT0r4ck0Gn5CkHGo/SQqxvUVHH6J+XxeYerpP+KS7v0DuCHtW4d9q0IFCMljQsgH3wUjiVmx3Gu61V7v4U9qOLkvvQY3ovv1XWq0zO3of8y/esGE5PUbIvg4Yaartcw4/7mm8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Gj6sVInS; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Gj6sVInS" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-297d4a56f97so36700145ad.1 for ; Fri, 21 Nov 2025 15:14:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763766840; x=1764371640; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=OC6A8mPFLhueq64eXidqCz96jiCjvdyEHQ90Q6pI/JM=; b=Gj6sVInSdlZtMRf1/Rz1Sy3CHerTqUBUDtQcdW87zhr+XutSv8qIzxbn/mzqwaapQW 6BBmPv34MD02QtSsdSW3Clych1m/d1FxDLXwZOVJPY9Czn+RSfdhOMsx3/GFeBBELVo1 zu06WSs2a4x82IJa2AOlpexW49vpQ2f2MlSGHa620Argek3G/GVKbkBXZWNOyqz7dXsh dM1oYNkQIzd/LU62Alq7t13ukr2zcZ9+cdelW3NWTt9X2iLsYUBsdG93W0Eg+o/F9Til KJSfWw9FcZXLnj5JkId0boS0wOef4JI03lFEcDS0sjiHHnoMztZ2KQESzn5FYu+Bnvxs jBTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763766840; x=1764371640; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=OC6A8mPFLhueq64eXidqCz96jiCjvdyEHQ90Q6pI/JM=; b=GWvKwm1jjW1k/DkWrfw/0O9FUV34qIlzJU4mXTUFAL8IKEbezAIgcLfTYKyp1Q2BcI D+0LvGY768fWGxrxvg2eP6NM8aP+rPTMEHk7KP5oipQFS70yPCI6IbVqnvbSd78JRUwk FnTDb8HKIz+fE+Wa5zTOO/xhFQbTdHiRYWuyw90rL07SOQ1fcsg17DICApon4Ba8IbGB oxP8/D5QZ4Q2EMgoedPVZoKEllf1LltnXEvrF0ZGecGS4WB5ljYzwF8XAxkOcd5pvaqk P4SANqhfHmNpr12xCmCWNlVOS/2bqn6svqL7flbhY0Bo4pjzcRkboCuq6RrTQmZd79kk uLPg== X-Gm-Message-State: AOJu0Yw05qzXcDs3B2n6fxNZrPFzfvBm2wOzKz3Wm1OJaf3MwCvZ5TPm 7Gp/aANiqrzSNU8SdxjvPHjAjIOnkD4UnPd8PEH6pYXQV2HNwTNLHQMOiIYR4g== X-Gm-Gg: ASbGncvHyebjfTtvoCh6VCm5JTdbEdspomln4vJP3kx/hLAy2MbLmVtPY7tIzjUc7jR 42gTbk7a13FbFXeWISNxwvbA9deyiTfR1uPdCM7wCrcs/CtgYlbEWFNn2JBf8NPlHM+91kMVA+r EJpj/GvrTn7XS8/2Ww12gSRzPF1hQ0+R4O2hC8t3juDtQvJekPqqUtsN0Qq7hELH16KZU2mWrth RAnLh4Aez6Q6qJ/6DQgH9x2+Ag0/dN1tOngsDmmTznOyNcWmyW9IJHqxE/I23sshAGn6kofyYop OcXhmzAYdkxrpHbF2UeODR3ieRvhWsgs/AHjCyUWURbqWAsbxVbKsjA4EyksFdD8ooj1kvmYXvu OoKcjRCh5vGYhLcLQpoGrruJ2ErpSIwpFZqQnIHb/sqcW+QKm+sPa0wMGpdwFL+i2jHspErqWfI 0hSKRRJAjpp+FycA== X-Google-Smtp-Source: AGHT+IFdIPv+fCYsGWSNxnZXtdjctxLEPTAGeo+FYiMRLUEiHSN3+03oqRfaaUe071hIg48Vweorfw== X-Received: by 2002:a17:903:2c9:b0:297:c638:d7c9 with SMTP id d9443c01a7336-29b6c3e813emr42894375ad.13.1763766840410; Fri, 21 Nov 2025 15:14:00 -0800 (PST) Received: from localhost ([2a03:2880:ff:48::]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29b5b111f65sm67791655ad.13.2025.11.21.15.13.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 15:14:00 -0800 (PST) From: Amery Hung To: bpf@vger.kernel.org Cc: netdev@vger.kernel.org, alexei.starovoitov@gmail.com, andrii@kernel.org, daniel@iogearbox.net, tj@kernel.org, martin.lau@kernel.org, ameryhung@gmail.com, kernel-team@meta.com Subject: [PATCH bpf-next v7 6/6] selftests/bpf: Test getting associated struct_ops in timer callback Date: Fri, 21 Nov 2025 15:13:52 -0800 Message-ID: <20251121231352.4032020-7-ameryhung@gmail.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251121231352.4032020-1-ameryhung@gmail.com> References: <20251121231352.4032020-1-ameryhung@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Make sure 1) a timer callback can also reference the associated struct_ops, and then make sure 2) the timer callback cannot get a dangled pointer to the struct_ops when the map is freed. The test schedules a timer callback from a struct_ops program since struct_ops programs do not pin the map. It is possible for the timer callback to run after the map is freed. The timer callback calls a kfunc that runs .test_1() of the associated struct_ops, which should return MAP_MAGIC when the map is still alive or -1 when the map is gone. The first subtest added in this patch schedules the timer callback to run immediately, while the map is still alive. The second subtest added schedules the callback to run 500ms after syscall_prog runs and then frees the map right after syscall_prog runs. Both subtests then wait until the callback runs to check the return of the kfunc. Signed-off-by: Amery Hung --- .../bpf/prog_tests/test_struct_ops_assoc.c | 81 +++++++++++++++++++ .../bpf/progs/struct_ops_assoc_in_timer.c | 77 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c index 02173504f675..461ded722351 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c @@ -3,6 +3,7 @@ #include #include "struct_ops_assoc.skel.h" #include "struct_ops_assoc_reuse.skel.h" +#include "struct_ops_assoc_in_timer.skel.h" static void test_st_ops_assoc(void) { @@ -101,10 +102,90 @@ static void test_st_ops_assoc_reuse(void) struct_ops_assoc_reuse__destroy(skel); } +static void test_st_ops_assoc_in_timer(void) +{ + struct struct_ops_assoc_in_timer *skel = NULL; + int err; + + skel = struct_ops_assoc_in_timer__open_and_load(); + if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_in_timer__open")) + goto out; + + err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog, + skel->maps.st_ops_map, NULL); + ASSERT_OK(err, "bpf_program__assoc_struct_ops"); + + err = struct_ops_assoc_in_timer__attach(skel); + if (!ASSERT_OK(err, "struct_ops_assoc__attach")) + goto out; + + /* + * Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks + * the return value. .test_1 will also schedule timer_cb that runs .test_1 again + * immediately. + */ + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL); + ASSERT_OK(err, "bpf_prog_test_run_opts"); + + /* Check the return of the kfunc after timer_cb runs */ + while (!READ_ONCE(skel->bss->timer_cb_run)) + sched_yield(); + ASSERT_EQ(skel->bss->timer_test_1_ret, 1234, "skel->bss->timer_test_1_ret"); + ASSERT_EQ(skel->bss->test_err, 0, "skel->bss->test_err_a"); +out: + struct_ops_assoc_in_timer__destroy(skel); +} + +static void test_st_ops_assoc_in_timer_no_uref(void) +{ + struct struct_ops_assoc_in_timer *skel = NULL; + struct bpf_link *link; + int err; + + skel = struct_ops_assoc_in_timer__open_and_load(); + if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_in_timer__open")) + goto out; + + err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog, + skel->maps.st_ops_map, NULL); + ASSERT_OK(err, "bpf_program__assoc_struct_ops"); + + link = bpf_map__attach_struct_ops(skel->maps.st_ops_map); + if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) + goto out; + + /* + * Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks + * the return value. .test_1 will also schedule timer_cb that runs .test_1 again. + * timer_cb will run 500ms after syscall_prog runs, when the user space no longer + * holds a reference to st_ops_map. + */ + skel->bss->timer_ns = 500000000; + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL); + ASSERT_OK(err, "bpf_prog_test_run_opts"); + + /* Detach and close struct_ops map to cause it to be freed */ + bpf_link__destroy(link); + close(bpf_program__fd(skel->progs.syscall_prog)); + close(bpf_map__fd(skel->maps.st_ops_map)); + + /* Check the return of the kfunc after timer_cb runs */ + while (!READ_ONCE(skel->bss->timer_cb_run)) + sched_yield(); + ASSERT_EQ(skel->bss->timer_test_1_ret, -1, "skel->bss->timer_test_1_ret"); + ASSERT_EQ(skel->bss->test_err, 0, "skel->bss->test_err_a"); +out: + struct_ops_assoc_in_timer__destroy(skel); +} + void test_struct_ops_assoc(void) { if (test__start_subtest("st_ops_assoc")) test_st_ops_assoc(); if (test__start_subtest("st_ops_assoc_reuse")) test_st_ops_assoc_reuse(); + if (test__start_subtest("st_ops_assoc_in_timer")) + test_st_ops_assoc_in_timer(); + if (test__start_subtest("st_ops_assoc_in_timer_no_uref")) + test_st_ops_assoc_in_timer_no_uref(); } diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c new file mode 100644 index 000000000000..9d4e427568b2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" +#include "../test_kmods/bpf_testmod.h" +#include "../test_kmods/bpf_testmod_kfunc.h" + +char _license[] SEC("license") = "GPL"; + +struct elem { + struct bpf_timer timer; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct elem); +} array_map SEC(".maps"); + +#define MAP_MAGIC 1234 +int recur; +int test_err; +int timer_ns; +int timer_test_1_ret; +int timer_cb_run; + +__noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer) +{ + struct st_ops_args args = {}; + + recur++; + timer_test_1_ret = bpf_kfunc_multi_st_ops_test_1_prog_arg(&args, NULL); + recur--; + + timer_cb_run++; + + return 0; +} + +SEC("struct_ops") +int BPF_PROG(test_1, struct st_ops_args *args) +{ + struct bpf_timer *timer; + int key = 0; + + if (!recur) { + timer = bpf_map_lookup_elem(&array_map, &key); + if (!timer) + return 0; + + bpf_timer_init(timer, &array_map, 1); + bpf_timer_set_callback(timer, timer_cb); + bpf_timer_start(timer, timer_ns, 0); + } + + return MAP_MAGIC; +} + +SEC("syscall") +int syscall_prog(void *ctx) +{ + struct st_ops_args args = {}; + int ret; + + ret = bpf_kfunc_multi_st_ops_test_1_prog_arg(&args, NULL); + if (ret != MAP_MAGIC) + test_err++; + + return 0; +} + +SEC(".struct_ops.link") +struct bpf_testmod_multi_st_ops st_ops_map = { + .test_1 = (void *)test_1, +}; -- 2.47.3