Re: [PATCH bpf-next] selftests/bpf: fix probe_user test failure with clang build kernel

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Sep 28, 2021 at 8:46 AM Yonghong Song <yhs@xxxxxx> wrote:
>
>
>
> On 9/27/21 10:11 PM, Andrii Nakryiko wrote:
> > On Mon, Sep 27, 2021 at 10:05 AM Yonghong Song <yhs@xxxxxx> 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@xxxxxx>
> >> ---
> >>   .../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
> >>



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux