From: Andrii Nakryiko <andrii.nakryiko@gmail.com>
To: Yonghong Song <yhs@fb.com>
Cc: bpf <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Kernel Team <kernel-team@fb.com>
Subject: Re: [PATCH bpf-next] selftests/bpf: fix probe_user test failure with clang build kernel
Date: Tue, 28 Sep 2021 15:46:05 -0700 [thread overview]
Message-ID: <CAEf4BzY004LfARDVMHUmNh97CjTTVWSvtBSLwpESo+qEBkbNaA@mail.gmail.com> (raw)
In-Reply-To: <c9d610e7-eb5b-ba8e-2b2d-6c37eea57ef7@fb.com>
On Tue, Sep 28, 2021 at 8:46 AM Yonghong Song <yhs@fb.com> wrote:
>
>
>
> On 9/27/21 10:11 PM, Andrii Nakryiko wrote:
> > On Mon, Sep 27, 2021 at 10:05 AM Yonghong Song <yhs@fb.com> wrote:
> >>
> >> clang build kernel failed the selftest probe_user.
> >> $ ./test_progs -t probe_user
> >> $ ...
> >> $ test_probe_user:PASS:get_kprobe_res 0 nsec
> >> $ test_probe_user:FAIL:check_kprobe_res wrong kprobe res from probe read: 0.0.0.0:0
> >> $ #94 probe_user:FAIL
> >>
> >> The test attached to kernel function __sys_connect(). In net/socket.c, we have
> >> int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
> >> {
> >> ......
> >> }
> >> ...
> >> SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,
> >> int, addrlen)
> >> {
> >> return __sys_connect(fd, uservaddr, addrlen);
> >> }
> >>
> >> The gcc compiler (8.5.0) does not inline __sys_connect() in syscall entry
> >> function. But latest clang trunk did the inlining. So the bpf program
> >> is not triggered.
> >>
> >> To make the test more reliable, let us kprobe the syscall entry function
> >> instead. But x86_64, arm64 and s390 has syscall wrapper and they have
> >> to be handled specially. I also changed the test to use vmlinux.h and CORE
> >> to accommodate relocatable pt_regs structure, similar to
> >> samples/bpf/test_probe_write_user_kern.c.
> >>
> >> Signed-off-by: Yonghong Song <yhs@fb.com>
> >> ---
> >> .../selftests/bpf/prog_tests/probe_user.c | 4 +--
> >> .../selftests/bpf/progs/test_probe_user.c | 30 +++++++++++++++----
> >> 2 files changed, 26 insertions(+), 8 deletions(-)
> >>
> >> diff --git a/tools/testing/selftests/bpf/prog_tests/probe_user.c b/tools/testing/selftests/bpf/prog_tests/probe_user.c
> >> index 95bd12097358..52fe157e2a90 100644
> >> --- a/tools/testing/selftests/bpf/prog_tests/probe_user.c
> >> +++ b/tools/testing/selftests/bpf/prog_tests/probe_user.c
> >> @@ -3,7 +3,7 @@
> >>
> >> void test_probe_user(void)
> >> {
> >> - const char *prog_name = "kprobe/__sys_connect";
> >> + const char *prog_name = "handle_sys_connect";
> >> const char *obj_file = "./test_probe_user.o";
> >> DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, );
> >> int err, results_map_fd, sock_fd, duration = 0;
> >> @@ -18,7 +18,7 @@ void test_probe_user(void)
> >> if (!ASSERT_OK_PTR(obj, "obj_open_file"))
> >> return;
> >>
> >> - kprobe_prog = bpf_object__find_program_by_title(obj, prog_name);
> >> + kprobe_prog = bpf_object__find_program_by_name(obj, prog_name);
> >> if (CHECK(!kprobe_prog, "find_probe",
> >> "prog '%s' not found\n", prog_name))
> >> goto cleanup;
> >> diff --git a/tools/testing/selftests/bpf/progs/test_probe_user.c b/tools/testing/selftests/bpf/progs/test_probe_user.c
> >> index 89b3532ccc75..9b3ddbf6289d 100644
> >> --- a/tools/testing/selftests/bpf/progs/test_probe_user.c
> >> +++ b/tools/testing/selftests/bpf/progs/test_probe_user.c
> >> @@ -1,21 +1,39 @@
> >> // SPDX-License-Identifier: GPL-2.0
> >>
> >> -#include <linux/ptrace.h>
> >> -#include <linux/bpf.h>
> >> -
> >> -#include <netinet/in.h>
> >> +#include "vmlinux.h"
> >>
> >> #include <bpf/bpf_helpers.h>
> >> +#include <bpf/bpf_core_read.h>
> >> #include <bpf/bpf_tracing.h>
> >>
> >> +#if defined(__TARGET_ARCH_x86)
> >
> > I mangled this check (locally) to test the #else case, and it failed
> > compilation:
> >
> > progs/test_probe_user.c:24:15: error: expected ')'
> > SEC("kprobe/" SYS_PREFIX "sys_connect")
> >
> > adding #define SYS_PREFIX "" fixes the issue.
> >
> > But I'm also curious:
> >
> > 1. do we need all this arch-specific logic? maybe we can find some
> > other and more stable interface to attach to? e.g., would
> > move_addr_to_kernel() work? it's a global function, so it shouldn't be
> > inlined, right? Or did you intend to also demo the use of
> > PT_REGS_PARM1_CORE() with this?
>
> The following are move_add_to_kernel() call usages:
>
> fs/io_uring.c: return move_addr_to_kernel(conn->addr, conn->addr_len,
> &io->address);
> fs/io_uring.c: ret = move_addr_to_kernel(req->connect.addr,
> include/linux/socket.h:extern int move_addr_to_kernel(void __user
> *uaddr, int ulen, struct sockaddr_storage *kaddr);
> net/compat.c: err =
> move_addr_to_kernel(compat_ptr(msg.msg_name),
> net/socket.c: * move_addr_to_kernel - copy a socket address
> into kernel space
> net/socket.c:int move_addr_to_kernel(void __user *uaddr, int ulen,
> struct sockaddr_storage *kaddr)
> net/socket.c: err = move_addr_to_kernel(umyaddr, addrlen,
> &address);
> net/socket.c: ret = move_addr_to_kernel(uservaddr, addrlen,
> &address);
> net/socket.c: err = move_addr_to_kernel(addr, addr_len, &address);
> net/socket.c: err = move_addr_to_kernel(msg.msg_name,
>
> inlining could happen within net/socket.c. Another two use cases are
> fs/io_uring.c and net/compat.c. Using compat syscall may be too complex,
> I assume. Using io_uring with bpf selftest may be a choice but I think
> it makes thing too complicated.
io_uring for this would be overkill
>
> More importantly, assuming in the future the kernel may be compiled
> with LTO, move_addr_to_kernel() might be inlined into fs/io_uring.c.
> So that is the main reason I am using syscall entry point directly.
Yep, makes sense.
>
> You are right, vmlinux.h is generated from the very kernel we are
> testing so we don't need CORE. I added CORE similar to
> samples/bpf/test_probe_write_user_kern.c, but it is not really
> needed here.
>
> >
> > 2. global .rodata variable isn't really necessary. Static one would
> > work just fine and would be eliminated by the compiler at compilation
> > time. Or equivalently just doing #define SYS_WRAPPER 1 for all cases
> > but #else would work as well. I don't mind global var, but I'm just
> > curious if you explicitly wanted to test .rodata global variable in
> > this case?
>
> Good point. This test is not to test .rodata variable. So using macro
> to differentiate different cases is actually better.
Ok, sounds good.
>
> >
> >
> >> +volatile const int syscall_wrapper = 1;
> >> +#define SYS_PREFIX "__x64_"
> >> +#elif defined(__TARGET_ARCH_s390)
> >> +volatile const int syscall_wrapper = 1;
> >> +#define SYS_PREFIX "__s390x_"
> >> +#elif defined(__TARGET_ARCH_arm64)
> >> +volatile const int syscall_wrapper = 1;
> >> +#define SYS_PREFIX "__arm64_"
> >> +#else
> >> +volatile const int syscall_wrapper = 0;
> >> +#endif
> >> +
> >> static struct sockaddr_in old;
> >>
> >> -SEC("kprobe/__sys_connect")
> >> +SEC("kprobe/" SYS_PREFIX "sys_connect")
> >> int BPF_KPROBE(handle_sys_connect)
> >> {
> >> - void *ptr = (void *)PT_REGS_PARM2(ctx);
> >> + void *ptr;
> >> struct sockaddr_in new;
> >>
> >> + if (syscall_wrapper == 0) {
> >> + ptr = (void *)PT_REGS_PARM2_CORE(ctx);
> >> + } else {
> >> + struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1_CORE(ctx);
> >> + ptr = (void *)PT_REGS_PARM2_CORE(real_regs);
> >> + }
> >> +
> >> bpf_probe_read_user(&old, sizeof(old), ptr);
> >> __builtin_memset(&new, 0xab, sizeof(new));
> >> bpf_probe_write_user(ptr, &new, sizeof(new));
> >> --
> >> 2.30.2
> >>
prev parent reply other threads:[~2021-09-28 22:46 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-09-27 17:05 [PATCH bpf-next] selftests/bpf: fix probe_user test failure with clang build kernel Yonghong Song
2021-09-28 5:11 ` Andrii Nakryiko
2021-09-28 15:46 ` Yonghong Song
2021-09-28 22:46 ` Andrii Nakryiko [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAEf4BzY004LfARDVMHUmNh97CjTTVWSvtBSLwpESo+qEBkbNaA@mail.gmail.com \
--to=andrii.nakryiko@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).