Thread (20 messages) 20 messages, 5 authors, 2018-01-22

Re: len = bpf_probe_read_str(); bpf_perf_event_output(... len) == FAIL

From: Arnaldo Carvalho de Melo <acme@kernel.org>
Date: 2017-11-21 14:29:09

Em Tue, Nov 14, 2017 at 02:58:24PM -0800, Yonghong Song escreveu:
On 11/14/17 12:25 PM, Daniel Borkmann wrote:
quoted
Yeah, I know, that's what I mentioned earlier in this thread to resolve it,
but do we really want to add this hack everywhere? :( Potentially any function
having ARG_CONST_SIZE would need to handle size 0 and bail out again in their
helper implementation and it ends up that progs start relying on this runtime
check where we won't be able to get rid of it later on anymore.
 
The compiler actually does the right thing for the below code:
         int ret = bpf_probe_read_str(filename, sizeof(filename),
                                      filename_ptr);
         if (ret > 0)
           bpf_perf_event_output(ctx, &__bpf_stdout__,BPF_F_CURRENT_CPU,
                           filename, ret & (sizeof(filename) - 1));
 
Just from the above code without consulting bpf_probe_read_str internals, it
is totally possible that ret = 128, then
ret & (sizeof(filename) - 1) = 0.
The issue is that the verifier did not set the "ret" initial range as (-inf,
sizeof(filename) - 1). We could have this information associated with helper
and feed back to verifier.
 
If we have this range, later for ret & (sizeof(filename) - 1) with ret >= 1,
the verifier should be able to conclude
 ret & (sizeof(filename) - 1) >= 1.
 
To workaround the immediate problem, I tested the following hack
with bcc and it works fine.
 
BPF_PERF_OUTPUT(events);
int trace(struct pt_regs *ctx) {
  char filename[128];
  int ret = bpf_probe_read_str(filename, sizeof(filename), 0);
  if (ret > 0) {
    if (ret == 1)
          events.perf_submit(ctx, filename, ret);
    else if (ret < 128)
          events.perf_submit(ctx, filename, ret);
  }
  return 1;
}
 
The idea is to make control flow more complex to prevent llvm
do certain optimizations.
So, the hack makes it work for me, using clang 6.0:

set env: NR_CPUS=4
set env: LINUX_VERSION_CODE=0x40e00
set env: CLANG_EXEC=/usr/local/bin/clang
unset env: CLANG_OPTIONS
set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/7/include -I/home/acme/git/linux/arch/x86/include -I./arch/x86/include/generated  -I/home/acme/git/linux/include -I./include -I/home/acme/git/linux/arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I/home/acme/git/linux/include/uapi -I./include/generated/uapi -include /home/acme/git/linux/include/linux/kconfig.h 
set env: WORKING_DIR=/lib/modules/4.14.0+/build
set env: CLANG_SOURCE=/home/acme/bpf/open.c
llvm compiling command template: $CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS -DLINUX_VERSION_CODE=$LINUX_VERSION_CODE $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -

[root@jouet bpf]# perf probe -V do_sys_open
Available variables at do_sys_open
        @<do_sys_open+0>
                char*   filename
                int     dfd
                int     flags
                struct open_flags       op
                umode_t mode
[root@jouet bpf]# cat open.c 
#include "bpf.h"

SEC("prog=do_sys_open filename")
int prog(void *ctx, int err, char *filename_ptr)
{
	char filename[128];
	int len = bpf_probe_read_str(filename, sizeof(filename), filename_ptr); 
	if (len > 0) {
		if (len == 1)
       			perf_event_output(ctx, &__bpf_stdout__, BPF_F_CURRENT_CPU, filename, len);
		else if (len < 128)
       			perf_event_output(ctx, &__bpf_stdout__, BPF_F_CURRENT_CPU, filename, len);
	}
	return 1;
}
[root@jouet bpf]#
[root@jouet bpf]# perf trace -e *open,open.c touch /tmp/Thanks.Yonghong.Song\!
LLVM: dumping open.o
     0.000 ( 0.009 ms): touch/9034 open(filename: 0x5b678e37, flags: CLOEXEC                             ) ...
     0.009 (         ): __bpf_stdout__:/etc/ld.so.cache....)
     0.011 (         ): perf_bpf_probe:prog:(ffffffff8f260da0) filename=0x7f805b678e37)
     0.000 ( 0.016 ms): touch/9034  ... [continued]: open()) = 3
     0.034 ( 0.002 ms): touch/9034 open(filename: 0x5b87c640, flags: CLOEXEC                             ) ...
     0.036 (         ): __bpf_stdout__:/lib64/libc.so.6....)
     0.037 (         ): perf_bpf_probe:prog:(ffffffff8f260da0) filename=0x7f805b87c640)
     0.034 ( 0.009 ms): touch/9034  ... [continued]: open()) = 3
     0.251 ( 0.002 ms): touch/9034 open(filename: 0x5b422c70, flags: CLOEXEC                             ) ...
     0.253 (         ): __bpf_stdout__:/usr/lib/locale/locale-archive......)
     0.254 (         ): perf_bpf_probe:prog:(ffffffff8f260da0) filename=0x7f805b422c70)
     0.251 ( 0.009 ms): touch/9034  ... [continued]: open()) = 3
     0.296 ( 0.002 ms): touch/9034 open(filename: 0x1d3a00f1, flags: CREAT|NOCTTY|NONBLOCK|WRONLY, mode: IRUGO|IWUGO) ...
     0.298 (         ): __bpf_stdout__:/tmp/Thanks.Yonghong.Song!..)
     0.299 (         ): perf_bpf_probe:prog:(ffffffff8f260da0) filename=0x7ffd1d3a00f1)
     0.296 ( 0.009 ms): touch/9034  ... [continued]: open()) = 3
[root@jouet bpf]#
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help