如何在bpf中访问用户空间函数参数?



我试图通过使用libbpf来检测用户空间nginx函数。我能够连接一个探针,并从探针打印pid,tid等。然而,每当我试图解析函数参数数据时,我都会遇到很大的问题。我已经能够做到这一点与bpftrace,但不能这样做与libbpf。我的问题是,如何正确访问和打印我想要跟踪的用户空间函数的参数?

nginx.bpf.c

#include "ngx_http.h"
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC("uprobe//usr/sbin/nginx:ngx_http_finalize_request")
int handle_ngx_http_finalize_request(struct ngx_http_request_s* r, ngx_int_t rc)
{
u_char *s_ptr;
u_char str[128];
int err;
err = bpf_probe_read_user(&s_ptr, sizeof(s_ptr), &r->request_line.data);
if (!s_ptr || err < 0) {
bpf_printk("Error %dn", err);
return -2;
}
bpf_probe_read_user_str(str, sizeof(str), &s_ptr);
bpf_printk("String: %sn", str);
return 0;
}

每当我试图解析函数参数bpf_probe_read_user返回错误-14。当我尝试使用bpf_core_read时,验证器拒绝了以下错误的代码。

❯ sudo ./nginx
libbpf: loading object 'nginx_bpf' from buffer
libbpf: elf: section(3) uprobe//usr/sbin/nginx:ngx_http_finalize_request, size 280, link 0, flags 6, type=1
libbpf: sec 'uprobe//usr/sbin/nginx:ngx_http_finalize_request': found program 'handle_ngx_http_finalize_request' at insn offset 0 (0 bytes), code size 35 insns (280 bytes)
libbpf: elf: section(4) .reluprobe//usr/sbin/nginx:ngx_http_finalize_request, size 32, link 12, flags 40, type=9
libbpf: elf: section(5) license, size 13, link 0, flags 3, type=1
libbpf: license of nginx_bpf is Dual BSD/GPL
libbpf: elf: section(6) .rodata, size 22, link 0, flags 2, type=1
libbpf: elf: section(7) .BTF, size 12720, link 0, flags 0, type=1
libbpf: elf: section(9) .BTF.ext, size 252, link 0, flags 0, type=1
libbpf: elf: section(12) .symtab, size 240, link 1, flags 0, type=2
libbpf: looking for externs among 10 symbols...
libbpf: collected 0 externs total
libbpf: map 'nginx_bp.rodata' (global data): at sec_idx 6, offset 0, flags 80.
libbpf: map 0 is "nginx_bp.rodata"
libbpf: sec '.reluprobe//usr/sbin/nginx:ngx_http_finalize_request': collecting relocation for section(3) 'uprobe//usr/sbin/nginx:ngx_http_finalize_request'
libbpf: sec '.reluprobe//usr/sbin/nginx:ngx_http_finalize_request': relo #0: insn #13 against '.rodata'
libbpf: prog 'handle_ngx_http_finalize_request': found data map 0 (nginx_bp.rodata, sec 6, off 0) for insn 13
libbpf: sec '.reluprobe//usr/sbin/nginx:ngx_http_finalize_request': relo #1: insn #28 against '.rodata'
libbpf: prog 'handle_ngx_http_finalize_request': found data map 0 (nginx_bp.rodata, sec 6, off 0) for insn 28
libbpf: loading kernel BTF '/sys/kernel/btf/vmlinux': 0
libbpf: map 'nginx_bp.rodata': created successfully, fd=4
libbpf: sec 'uprobe//usr/sbin/nginx:ngx_http_finalize_request': found 1 CO-RE relocations
libbpf: prog 'handle_ngx_http_finalize_request': relo #0: <byte_off> [2] typedef ngx_http_request_t.request_line.data (0:21:1 @ offset 992)
libbpf: prog 'handle_ngx_http_finalize_request': relo #0: no matching targets found
libbpf: prog 'handle_ngx_http_finalize_request': relo #0: substituting insn #1 w/ invalid insn
libbpf: prog 'handle_ngx_http_finalize_request': BPF program load failed: Invalid argument
libbpf: prog 'handle_ngx_http_finalize_request': -- BEGIN PROG LOAD LOG --
R1 type=ctx expected=fp
; int handle_ngx_http_finalize_request(ngx_http_request_t* r, ngx_int_t rc)
0: (bf) r3 = r1
1: <invalid CO-RE relocation>
failed to resolve CO-RE relocation <byte_off> [2] typedef ngx_http_request_t.request_line.data (0:21:1 @ offset 992)
processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'handle_ngx_http_finalize_request': failed to load: -22
libbpf: failed to load object 'nginx_bpf'
libbpf: failed to load BPF skeleton 'nginx_bpf': -22
Failed to open and load BPF skeleton

下面是bpftrace代码。

nginx.bt

uprobe:/usr/sbin/nginx:ngx_http_finalize_request
{
$req = (struct ngx_http_request_s*)arg0;
printf("Request Line: %sn", str($req->request_line.data));
}

所以我想解析数据并根据它做一些自定义逻辑。

下面是ngx_http_request_sngx_str_t结构体。

编辑


用户空间码:

#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "nginx.skel.h"
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
return vfprintf(stderr, format, args);
}
int main(int argc, char **argv)
{
struct nginx_bpf *skel;
int err;
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
libbpf_set_print(libbpf_print_fn);
skel = nginx_bpf__open_and_load();
if (!skel) {
fprintf(stderr, "Failed to open and load BPF skeletonn");
return 1;
}
err = nginx_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to auto-attach BPF skeleton: %dn", err);
goto cleanup;
}
printf("Successfully started!n");
for (;;) {
sleep(1);
}
cleanup:
nginx_bpf__destroy(skel);
return -err;
}

您实际上没有正确定义BPF程序。您应该执行如下操作。请注意,您也不应该使用bpf_core_read()BPF_CORE_READ()的CO-RE变体,而应使用BPF_PROBE_READ_USER():

SEC("uprobe//usr/sbin/nginx:ngx_http_finalize_request")
int BPF_KPROBE(handle_ngx_http_finalize_request,
struct ngx_http_request_s* r, ngx_int_t rc)
{
u_char *s_ptr;
u_char str[128];
int err;
/* you can access rc directly now, btw */
s_ptr = BPF_PROBE_READ_USER(r, request_line.data);
/* note no dereferencing of s_ptr above */
bpf_probe_read_user_str(str, sizeof(str), s_ptr); 
bpf_printk("String: %sn", str);
return 0;

你不必使用BPF_PROBE_READ_USER()宏,你可以做同样的bpf_probe_read_user()就像你在你的例子。但是,如果您需要跟踪几层指针,BPF_PROBE_READ_USER()将特别方便。

但是对于用户空间类型没有CO-RE,它只适用于内核类型,因为内核提供BTF信息来允许正确地重定位偏移量。

最新更新