From patchwork Thu Jul 18 13:27:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Olsa X-Patchwork-Id: 13736514 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5CD9A13AD32 for ; Thu, 18 Jul 2024 13:28:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721309287; cv=none; b=ANFDqsLcY9uMPZEj0uyk2u8zz6nUDCKDvlNH5gHc6Vk6Uds6Fz+zGx0AjfTcleOJuAElZ0DHx3/3XEcCZ+Hz3jvOVJgvVm3WpBa3/bmMqMQuSixm+D02ZoOi64Eby6ckObcQdVrb9cTL9gABF5GYJk/XJIE0wxLWaskPw9H8SnA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721309287; c=relaxed/simple; bh=E6XbZXkIUw3rFes4kKX2xCFAuaGjEE2Tk+8UFZn+oCc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uhAYEfm2wbgQgnZsJ/RHHeaOrX47QEHzMB/Vt8k3TKcr7DlpcnJEEReHWlmZQLGfPpdg5zI2PIMcPiDvh/WeotkQLQsJRAETdv+lbr0fsZ6P1H9bH/XoXaqY1GiBypeDhuTQ7SeCCbhK0o3XwflShW5ed64ppa6O+zG/kEpxpNM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qD46c9B9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qD46c9B9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 81155C4AF0A; Thu, 18 Jul 2024 13:28:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1721309286; bh=E6XbZXkIUw3rFes4kKX2xCFAuaGjEE2Tk+8UFZn+oCc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qD46c9B94upHX+Hh0unoQ5iw0HJnn0V+ertn2XS5w8xWlHt1lgmxS9mGXCbY0AKdI MNV6YmGNQBhEWgQrcmC117+NRLY16QThV/zIi5zJS6oHQCGr7UE2IqGmSz/pq51Ct0 Y7/y7K6WghO7m2Acmn09MgP8EriMpYH4X3eIjng+ip+s41YeGr94hGIg+bdj27Zbn1 26E8N//OlWQehIKVYPJNGoWoUl8gx7JC5ypTGxbAf1szUx9QejMnvKnKvSnY2gBl8A bU537KCTvjxlkgSIf1L5hYgChWkTU6qJFjSPjgnlYZl9E/1/wpWX/aBqtJA0YSW+WH EDURk2Dd53jmw== From: Jiri Olsa To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko Cc: bpf@vger.kernel.org, Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Oleg Nesterov , Peter Zijlstra , Masami Hiramatsu Subject: [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi Date: Thu, 18 Jul 2024 15:27:49 +0200 Message-ID: <20240718132750.2914808-2-jolsa@kernel.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240718132750.2914808-1-jolsa@kernel.org> References: <20240718132750.2914808-1-jolsa@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Addig tests for checking on recovery after failing to attach uprobe. Signed-off-by: Jiri Olsa --- .../bpf/prog_tests/uprobe_multi_test.c | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c index bf6ca8e3eb13..da8873f24a53 100644 --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c @@ -516,6 +516,113 @@ static void test_attach_api_fails(void) uprobe_multi__destroy(skel); } +#ifdef __x86_64__ +noinline __attribute__((naked)) void uprobe_multi_error_func(void) +{ + asm volatile ("int3"); +} + +/* + * Attaching uprobe on uprobe_multi_error_func results in error + * because it already starts with int3 instruction. + */ +static void attach_uprobe_fail_trap(struct uprobe_multi *skel) +{ + LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); + const char *syms[4] = { + "uprobe_multi_func_1", + "uprobe_multi_func_2", + "uprobe_multi_func_3", + "uprobe_multi_error_func", + }; + + opts.syms = syms; + opts.cnt = ARRAY_SIZE(syms); + + skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, -1, + "/proc/self/exe", NULL, &opts); + if (!ASSERT_ERR_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) { + bpf_link__destroy(skel->links.uprobe); + skel->links.uprobe = NULL; + } +} +#else +static void attach_uprobe_fail_trap(struct uprobe_multi *skel) { } +#endif + +static short sema_1 __maybe_unused, sema_2 __maybe_unused; + +static void attach_uprobe_fail_refctr(struct uprobe_multi *skel) +{ + unsigned long *tmp_offsets = NULL, *tmp_ref_ctr_offsets = NULL; + unsigned long offsets[3], ref_ctr_offsets[3]; + LIBBPF_OPTS(bpf_link_create_opts, opts); + const char *path = "/proc/self/exe"; + const char *syms[3] = { + "uprobe_multi_func_1", + "uprobe_multi_func_2", + }; + const char *sema[3] = { + "sema_1", + "sema_2", + }; + int prog_fd, link_fd, err; + + prog_fd = bpf_program__fd(skel->progs.uprobe_extra); + + err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &syms, + &tmp_offsets, STT_FUNC); + if (!ASSERT_OK(err, "elf_resolve_syms_offsets_func")) + return; + + err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &sema, + &tmp_ref_ctr_offsets, STT_OBJECT); + if (!ASSERT_OK(err, "elf_resolve_syms_offsets_sema")) + goto cleanup; + + /* + * We attach to 3 uprobes on 2 functions so 2 uprobes share single function, + * but with different ref_ctr_offset which is not allowed and results in fail. + */ + offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */ + offsets[1] = tmp_offsets[1]; /* uprobe_multi_func_2 */ + offsets[2] = tmp_offsets[1]; /* uprobe_multi_func_2 */ + + ref_ctr_offsets[0] = tmp_ref_ctr_offsets[0]; /* sema_1 */ + ref_ctr_offsets[1] = tmp_ref_ctr_offsets[1]; /* sema_2 */ + ref_ctr_offsets[2] = tmp_ref_ctr_offsets[0]; /* sema_1, error */ + + opts.uprobe_multi.path = path; + opts.uprobe_multi.offsets = (const unsigned long *) &offsets; + opts.uprobe_multi.ref_ctr_offsets = (const unsigned long *) &ref_ctr_offsets; + opts.uprobe_multi.cnt = 3; + + link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); + if (!ASSERT_ERR(link_fd, "link_fd")) + close(link_fd); + +cleanup: + free(tmp_ref_ctr_offsets); + free(tmp_offsets); +} + +static void test_attach_uprobe_fails(void) +{ + struct uprobe_multi *skel = NULL; + + skel = uprobe_multi__open_and_load(); + if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) + return; + + /* attach fails due to adding uprobe on trap instruction, x86_64 only */ + attach_uprobe_fail_trap(skel); + + /* attach fail due to wrong ref_ctr_offs on one of the uprobes */ + attach_uprobe_fail_refctr(skel); + + uprobe_multi__destroy(skel); +} + static void __test_link_api(struct child *child) { int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1; @@ -703,4 +810,6 @@ void test_uprobe_multi_test(void) test_bench_attach_usdt(); if (test__start_subtest("attach_api_fails")) test_attach_api_fails(); + if (test__start_subtest("attach_uprobe_fails")) + test_attach_uprobe_fails(); } From patchwork Thu Jul 18 13:27:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Olsa X-Patchwork-Id: 13736515 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53751746E for ; Thu, 18 Jul 2024 13:28:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721309298; cv=none; b=e2lu7yL+ZprlVsTVcfehy3zOL2LpIffReB/0gPHGmv7kliXFS4pBv5R+Vs7obg5rjDY9Q4v3PaOAkbJGX2oBbGTxvBn4yb+x3IR0Cqfo0PDccWotDjuVvYIAuGs+NY91+yY2JfYO1vnpzIOLj3HK6LCV4Xf29m+dNGswJHZpI1Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721309298; c=relaxed/simple; bh=UhwTgi+H2aw3ZO33OWWykNFRndZGx3w4rav16Dd0iV8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iKJYfRVsWBCEQs/BEhyeh4y1ORthUAyVtU85ajYsJWDkVia3YbJtIb9EsqSKlqkI7xz1XmkRUIrnMQhIhIwy2ac5GSedVweSjTpCpPr0P0WUPFcD2AQ37rZlp6qhutO/XLXe1nhRJWrtyQd9xFCJCAaGEp6vfbxguiXrjc/k0ZE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=p1EMpy6j; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="p1EMpy6j" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7473FC116B1; Thu, 18 Jul 2024 13:28:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1721309297; bh=UhwTgi+H2aw3ZO33OWWykNFRndZGx3w4rav16Dd0iV8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=p1EMpy6jZTH8XsVUmlesRLXRh89eDrIy/ZbDS8fmv7ukq/O/4Q1mqF0dfBUX7qkD6 xWhEXaWjPsoj/0X2Qq/aEqkh8mKih5LKPDvhkj053CDboPqShjEN0PtCi4/Ryx0tRk yOkriss//Ce995f5YD8F4cO4tWptX12tNm85FgOZwC2yHJYviTdXYh1h3OvS/Dnw4z iqlt1DrILERci7Ng2r3W6I/Xtn6pyb0QTaUnauHE7zB3CcSFoIfsOBQr9kfcDsgEiV NqATpRiPmWmIN4xpAEulaj0jfKwhq72vU7icImaiWtR0Tyegh13BsyBDMe/cXeEgh/ CHChF3OyBwwRA== From: Jiri Olsa To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko Cc: bpf@vger.kernel.org, Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Oleg Nesterov , Peter Zijlstra , Masami Hiramatsu Subject: [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test Date: Thu, 18 Jul 2024 15:27:50 +0200 Message-ID: <20240718132750.2914808-3-jolsa@kernel.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240718132750.2914808-1-jolsa@kernel.org> References: <20240718132750.2914808-1-jolsa@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Adding test that attached/detaches multiple consumers on single uprobe and verifies all were hit as expected. Signed-off-by: Jiri Olsa --- .../bpf/prog_tests/uprobe_multi_test.c | 211 +++++++++++++++++- .../bpf/progs/uprobe_multi_consumers.c | 39 ++++ 2 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c index da8873f24a53..5228085c2240 100644 --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c @@ -6,6 +6,7 @@ #include "uprobe_multi.skel.h" #include "uprobe_multi_bench.skel.h" #include "uprobe_multi_usdt.skel.h" +#include "uprobe_multi_consumers.skel.h" #include "bpf/libbpf_internal.h" #include "testing_helpers.h" #include "../sdt.h" @@ -581,7 +582,7 @@ static void attach_uprobe_fail_refctr(struct uprobe_multi *skel) goto cleanup; /* - * We attach to 3 uprobes on 2 functions so 2 uprobes share single function, + * We attach to 3 uprobes on 2 functions, so 2 uprobes share single function, * but with different ref_ctr_offset which is not allowed and results in fail. */ offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */ @@ -722,6 +723,212 @@ static void test_link_api(void) __test_link_api(child); } +static struct bpf_program * +get_program(struct uprobe_multi_consumers *skel, int prog) +{ + switch (prog) { + case 0: + return skel->progs.uprobe_0; + case 1: + return skel->progs.uprobe_1; + case 2: + return skel->progs.uprobe_2; + case 3: + return skel->progs.uprobe_3; + default: + ASSERT_FAIL("get_program"); + return NULL; + } +} + +static struct bpf_link ** +get_link(struct uprobe_multi_consumers *skel, int link) +{ + switch (link) { + case 0: + return &skel->links.uprobe_0; + case 1: + return &skel->links.uprobe_1; + case 2: + return &skel->links.uprobe_2; + case 3: + return &skel->links.uprobe_3; + default: + ASSERT_FAIL("get_link"); + return NULL; + } +} + +static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx) +{ + struct bpf_program *prog = get_program(skel, idx); + struct bpf_link **link = get_link(skel, idx); + LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); + + /* + * bit/prog: 0,1 uprobe entry + * bit/prog: 2,3 uprobe return + */ + opts.retprobe = idx == 2 || idx == 3; + + *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe", + "uprobe_session_consumer_test", + &opts); + if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi")) + return -1; + return 0; +} + +static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx) +{ + struct bpf_link **link = get_link(skel, idx); + + bpf_link__destroy(*link); + *link = NULL; +} + +static bool test_bit(int bit, unsigned long val) +{ + return val & (1 << bit); +} + +noinline int +uprobe_session_consumer_test(struct uprobe_multi_consumers *skel, + unsigned long before, unsigned long after) +{ + int idx; + + /* detach uprobe for each unset programs in 'before' state ... */ + for (idx = 0; idx < 4; idx++) { + if (test_bit(idx, before) && !test_bit(idx, after)) + uprobe_detach(skel, idx); + } + + /* ... and attach all new programs in 'after' state */ + for (idx = 0; idx < 4; idx++) { + if (!test_bit(idx, before) && test_bit(idx, after)) { + if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after")) + return -1; + } + } + return 0; +} + +static void session_consumer_test(struct uprobe_multi_consumers *skel, + unsigned long before, unsigned long after) +{ + int err, idx; + + printf("session_consumer_test before %lu after %lu\n", before, after); + + /* 'before' is each, we attach uprobe for every set idx */ + for (idx = 0; idx < 4; idx++) { + if (test_bit(idx, before)) { + if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before")) + goto cleanup; + } + } + + err = uprobe_session_consumer_test(skel, before, after); + if (!ASSERT_EQ(err, 0, "uprobe_session_consumer_test")) + goto cleanup; + + for (idx = 0; idx < 4; idx++) { + const char *fmt = "BUG"; + __u64 val = 0; + + if (idx < 2) { + /* + * uprobe entry + * +1 if define in 'before' + */ + if (test_bit(idx, before)) + val++; + fmt = "prog 0/1: uprobe"; + } else { + /* uprobe return is tricky ;-) + * + * to trigger uretprobe consumer, the uretprobe needs to be installed, + * which means one of the 'return' uprobes was alive when probe was hit: + * + * idxs: 2/3 uprobe return in 'installed' mask + * + * in addition if 'after' state removes everything that was installed in + * 'before' state, then uprobe kernel object goes away and return uprobe + * is not installed and we won't hit it even if it's in 'after' state. + */ + unsigned long installed = before & 0b1100; // is uretprobe installed + unsigned long exists = before & after; // did uprobe go away + + if (installed && exists && test_bit(idx, after)) + val++; + fmt = "idx 2/3: uretprobe"; + } + + ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt); + skel->bss->uprobe_result[idx] = 0; + } + +cleanup: + for (idx = 0; idx < 4; idx++) + uprobe_detach(skel, idx); +} + +static void test_consumers(void) +{ + struct uprobe_multi_consumers *skel; + int before, after; + + skel = uprobe_multi_consumers__open_and_load(); + if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load")) + return; + + /* + * The idea of this test is to try all possible combinations of + * uprobes consumers attached on single function. + * + * - 2 uprobe entry consumer + * - 2 uprobe exit consumers + * + * The test uses 4 uprobes attached on single function, but that + * translates into single uprobe with 4 consumers in kernel. + * + * The before/after values present the state of attached consumers + * before and after the probed function: + * + * bit/prog 0,1 : uprobe entry + * bit/prog 2,3 : uprobe return + * + * For example for: + * + * before = 0b0101 + * after = 0b0110 + * + * it means that before we call 'uprobe_session_consumer_test' we + * attach uprobes defined in 'before' value: + * + * - bit/prog 0: uprobe entry + * - bit/prog 2: uprobe return + * + * uprobe_session_consumer_test is called and inside it we attach + * and detach * uprobes based on 'after' value: + * + * - bit/prog 0: stays untouched + * - bit/prog 2: uprobe return is detached + * + * uprobe_session_consumer_test returns and we check counters values + * increased by bpf programs on each uprobe to match the expected + * count based on before/after bits. + */ + + for (before = 0; before < 16; before++) { + for (after = 0; after < 16; after++) + session_consumer_test(skel, before, after); + } + + uprobe_multi_consumers__destroy(skel); +} + static void test_bench_attach_uprobe(void) { long attach_start_ns = 0, attach_end_ns = 0; @@ -812,4 +1019,6 @@ void test_uprobe_multi_test(void) test_attach_api_fails(); if (test__start_subtest("attach_uprobe_fails")) test_attach_uprobe_fails(); + if (test__start_subtest("consumers")) + test_consumers(); } diff --git a/tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c b/tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c new file mode 100644 index 000000000000..7e0fdcbbd242 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include "bpf_kfuncs.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +__u64 uprobe_result[4]; + +SEC("uprobe.multi") +int uprobe_0(struct pt_regs *ctx) +{ + uprobe_result[0]++; + return 0; +} + +SEC("uprobe.multi") +int uprobe_1(struct pt_regs *ctx) +{ + uprobe_result[1]++; + return 0; +} + +SEC("uprobe.multi") +int uprobe_2(struct pt_regs *ctx) +{ + uprobe_result[2]++; + return 0; +} + +SEC("uprobe.multi") +int uprobe_3(struct pt_regs *ctx) +{ + uprobe_result[3]++; + return 0; +}