Ошибка при загрузке программы BPF, которая копирует буфер в стек BPF.

Я пытаюсь загрузить программу BPF, которая просто копирует параметр buf из tty_write в стек BPF. Моя программа выглядит следующим образом:

#define BUFSIZE 256

SEC("kprobe/tty_write")
int kprobe__tty_write(struct pt_regs *ctx, struct file *file, const char __user *buf, size_t count)
{
  char buffer[BUFSIZE];
  bpf_probe_read(buffer, BUFSIZE, (void *)buf);

  return 0;
}   

Обратите внимание, что я использую bpf_helpers.h из tcptracer-bpf, чтобы определить макрос SEC. В моей реальной программе я бы использовал buffer для чего-то, но я не показывал здесь эту часть. Когда я пытаюсь загрузить программу (из файла ELF, используя gobpf), я получаю следующую ошибку:

error while loading "kprobe/tty_write" (permission denied):
0: (bf) r1 = r10
1: (07) r1 += -256
2: (b7) r2 = 256
3: (85) call bpf_probe_read#4
R3 !read_ok

Почему это? Моя программа была адаптирована из ttysnoop.py, так что я знаю можно сделать то, что я пытаюсь сделать. Полный дизассемблирование моей программы выглядит следующим образом:

Disassembly of section kprobe/tty_write:
kprobe__tty_write:
       0:   bf a1 00 00 00 00 00 00     r1 = r10
       1:   07 01 00 00 00 ff ff ff     r1 += -256
       2:   b7 02 00 00 00 01 00 00     r2 = 256
       3:   85 00 00 00 04 00 00 00     call 4
       4:   b7 00 00 00 00 00 00 00     r0 = 0
       5:   95 00 00 00 00 00 00 00     exit

uname -a: Linux ubuntu1710 4.13.0-32-универсальный #35-Ubuntu SMP Чт, 25 января, 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

РЕДАКТИРОВАТЬ:

В качестве эксперимента я попытался загрузить программу, похожую на пример программы, описанный при появлении bpf_probe_read_str как вспомогательная функция:

#define BUFSIZE 256

SEC("kprobe/sys_open")
void bpf_sys_open(struct pt_regs *ctx)
{
  char buf[BUFSIZE];
  bpf_probe_read(buf, sizeof(buf), (void *)ctx->di);
}

Который загружается без проблем и дает следующую сборку:

Disassembly of section kprobe/sys_open:
bpf_sys_open:
       0:   79 13 70 00 00 00 00 00     r3 = *(u64 *)(r1 + 112)
       1:   bf a1 00 00 00 00 00 00     r1 = r10
       2:   07 01 00 00 00 ff ff ff     r1 += -256
       3:   b7 02 00 00 00 01 00 00     r2 = 256
       4:   85 00 00 00 04 00 00 00     call 4
       5:   95 00 00 00 00 00 00 00     exit

Итак, похоже, что моя программа tty_write передает третий регистр прямо на вызов bpf_probe_read, когда он был установлен после запуска kprobe; это может быть причиной ошибки, которую я вижу, но я не уверен.


person dippynark    schedule 10.02.2018    source источник


Ответы (1)


Как вы сами обнаружили, проблема возникает из-за использования дополнительных параметров для kprobe__tty_write. Это работает в ttysnoop, поскольку для компиляции и загрузки используется bcc. программы БФП. bcc фактически перезаписывает дополнительные параметры в ctx->xx разыменованиях. Вы можете увидеть это с помощью следующего фрагмента:

from bcc import BPF
BPF(text="""
#include <linux/ptrace.h>
int kprobe__tty_write(struct pt_regs *ctx, struct file *file, const char __user *buf, size_t count) {
return 0;
}
""", debug=4)

Благодаря debug=4 мы можем видеть перезаписи при выполнении:

$ sudo python tmp.py
clang -cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc -emit-llvm-uselists -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.c -mrelocation-model static -mthread-model posix -fmath-errno -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu x86-64 -momit-leaf-frame-pointer -dwarf-column-info -debugger-tuning=gdb -coverage-notes-file /usr/src/linux-headers-4.4.0-112-generic/main.gcno -nostdsysteminc -nobuiltininc -resource-dir lib/clang/5.0.1 -isystem /virtual/lib/clang/include -include ./include/linux/kconfig.h -include /virtual/include/bcc/bpf.h -include /virtual/include/bcc/helpers.h -isystem /virtual/include -I /home/paul/bcc2 -I ./arch/x86/include -I arch/x86/include/generated/uapi -I arch/x86/include/generated -I include -I ./arch/x86/include/uapi -I arch/x86/include/generated/uapi -I ./include/uapi -I include/generated/uapi -D __KERNEL__ -D __HAVE_BUILTIN_BSWAP16__ -D __HAVE_BUILTIN_BSWAP32__ -D __HAVE_BUILTIN_BSWAP64__ -O2 -Wno-deprecated-declarations -Wno-gnu-variable-sized-type-not-at-end -Wno-pragma-once-outside-header -Wno-address-of-packed-member -Wno-unknown-warning-option -Wno-unused-value -Wno-pointer-sign -fdebug-compilation-dir /usr/src/linux-headers-4.4.0-112-generic -ferror-limit 19 -fmessage-length 168 -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -o main.bc -x c /virtual/main.c

#include <linux/ptrace.h>
__attribute__((section(".bpf.fn.kprobe__tty_write")))
int kprobe__tty_write(struct pt_regs *ctx) { struct file *file = ctx->di; const char __user *buf = ctx->si; size_t count = ctx->dx;
return 0;
}

Таким же образом bcc извлекает имя функции из kprobe__tty_write и автоматически прикрепляет программу BPF к tty_write.

person pchaigno    schedule 10.02.2018
comment
офигенно, заменил параметры на правильные контекстные разыменования и прекрасно работает :) - person dippynark; 12.02.2018