中是如何"%p"实施的bpf_trace_printk?看起来很不一样printf。
#include <uapi/linux/ptrace.h>
int print_args(struct pt_regs *ctx) {
void *ptr = (void*)PT_REGS_PARM1(ctx);
bpf_trace_printk("args: %lx %p %ld\n", ptr, ptr, ptr);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用这个 eBPF 程序来跟踪一个函数的参数。参数的类型是指向某个结构的指针。
一种输出是:
args: 7ffde047d6c4 00000000ec7e9023 140728366257860
我们可以注意到 的输出"%p"非常奇怪。如果我们使用标准 C 程序来检查输出:
#include <stdio.h>
int main() {
void *ptr = (void*)0x7ffde047d6c4;
printf("args: %lx %p %ld\n", ptr, ptr, ptr);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我们将获得: args: 7ffde047d6c4 0x7ffde047d6c4 140728366257860
我正在编写一个检查任务 UID 的 eBPF kprobe,即在 execve 调用之间唯一允许的 UID 更改是 setuid()、seteuid() 和 setreuid() 调用所允许的 UID 更改。
由于探测器检查所有任务,因此它使用从 init_task 开始迭代的展开循环,并且最多必须使用 1024 或 8192 个分支,具体取决于内核版本。
我的问题是,如果存在非法更改,如何实现返回非零的检查,定义为:
(new_ruid != old_euid && new_ruid != old_ruid) ||
(new_euid != old_euid && new_euid != old_ruid && new_euid != old_suid)
Run Code Online (Sandbox Code Playgroud)
但不使用分支(clang 使用跳转来短路检查之间的任何表达式是否&&计算为 true)。
在kretprobe/sys_read探测器中,我尝试读取 BPF_MAP_TYPE_ARRAY 中的请求缓冲区(用户空间)。eBPF 验证程序抛出以下错误:
{"error": "field SyscallProbeRetRead: program syscall__probe_ret_read: load program: permission denied: invalid access to map value, value_size=70 off=0 size=16383: R1 min value is outside of the allowed memory range (truncated, 73 line(s) omitted)"}
Run Code Online (Sandbox Code Playgroud)
逻辑:如果 buf_size 大于MAX_MSG_SIZE,那么我们将读取TRACE_PARENT_SIZE或更少(取决于缓冲区的大小)到seventy_bytes_array->data,否则,我们将不会读取任何内容。
#define MAX_MSG_SIZE 16383
#define TRACE_PARENT_SIZE 70
SEC("kretprobe/sys_read")
int syscall__probe_ret_read(struct pt_regs *ctx)
{
size_t buf_size = PT_REGS_RC(ctx);
u64 id = bpf_get_current_pid_tgid();
struct data_args_t *read_args = bpf_map_lookup_elem(&active_read_args_map, &id);
if (read_args)
{
char *buf = read_args->buf; …Run Code Online (Sandbox Code Playgroud) 在Linux内核中提供kprobe事件支持ftrace时使用跟踪点事件的用例有哪些?似乎可以使用"跟踪点事件"使用kprobe事件完成所有可能的事情,因为可以在跟踪点事件可用的同一点设置kprobe事件.
我错过了什么吗?
samples/bpf/pare_simple.c我通过非常简单的更改(来自 Linux 内核树)编译了 BPF 示例:
SEC("simple")
int handle_ingress(struct __sk_buff *skb)
{
return TC_ACT_SHOT;
}
Run Code Online (Sandbox Code Playgroud)
所以我希望丢弃任何数据包。我安装它如下:
这种情况发生在 Ubuntu 16.04.3 LTS 上,内核为 4.4.0-98,从软件包安装了 llvm 和 clang 版本 3.8,iproute2 是来自 github 的最新版本。
$ tc qdisc add dev eth0 clsact
$ tc filter add dev eth0 ingress bpf \
object-file ./net-next.git/samples/bpf/parse_simple.o \
section simple verbose
Prog section 'simple' loaded (5)!
- Type: 3
- Instructions: 2 (0 over limit)
- License: GPL
Verifier analysis:
0: (b7) r0 = 2
1: (95) exit
processed …Run Code Online (Sandbox Code Playgroud) 在运行一些示例时,samples/bpf我注意到bpf_printk输出带有一些额外的信息,例如:
telnet-470 [001] .N.. 419421.045894: 0x00000001: BPF command: 2
Run Code Online (Sandbox Code Playgroud)
BPF command: 2实际的字符串是传递给bpf_printkbpf 程序的,但其余的是什么?我假设这来自内核的 JIT ?
我在哪里可以仔细查看这些位的含义?谢谢。
在阅读man bpf了一些其他文档来源后,我的印象是a map只能由用户进程创建.然而,以下小程序似乎神奇地创建了bpf地图:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(u32),
.value_size = sizeof(long),
.max_entries = 10,
};
SEC("sockops")
int my_prog(struct bpf_sock_ops *skops)
{
u32 key = 1;
long *value;
...
value = bpf_map_lookup_elem(&my_map, &key);
...
return 1;
}
Run Code Online (Sandbox Code Playgroud)
所以我用内核加载程序,tools/bpf/bpftool并验证程序是否已加载:
$ bpftool prog show
1: sock_ops name my_prog tag f3a3583cdd82ae8d
loaded_at Jan 02/18:46 uid 0
xlated 728B not jited memlock 4096B
$ bpftool map show
1: array …Run Code Online (Sandbox Code Playgroud) 我正在查看ebpf验证器代码,但无法理解以下宏:
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#define offsetofend(TYPE, MEMBER) \
(offsetof(TYPE, MEMBER) + sizeof(((TYPE *)0)->MEMBER))
<...>
#define bpf_ctx_range(TYPE, MEMBER) \
offsetof(TYPE, MEMBER) ... offsetofend(TYPE, MEMBER) - 1
#define bpf_ctx_range_till(TYPE, MEMBER1, MEMBER2) \
offsetof(TYPE, MEMBER1) ... offsetofend(TYPE, MEMBER2) - 1
Run Code Online (Sandbox Code Playgroud)
我理解offsetof宏offsetofend,但是什么...意思呢?
谢谢。
来自 bpf 手册页:
eBPF 程序可以用受限制的 C 语言编写,并编译(使用 clang 编译器)为 eBPF 字节码。此受限 C 语言省略了各种功能,例如循环、全局变量、可变参数函数、浮点数以及将结构体作为函数参数传递。
AFAIK 手册页尚未更新。我想知道使用受限C编写eBPF程序时到底禁止什么?手册页所说的仍然正确吗?
阅读一些附加到跟踪点的ebpf示例后,我注意到每个结构都是从这样的填充开始构建的(来自samples/bpf/xdp_redirect_cpu_kern.c)
/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
* Code in: kernel/include/trace/events/xdp.h
*/
struct cpumap_enqueue_ctx {
u64 __pad; // First 8 bytes are not accessible by bpf code
int map_id; // offset:8; size:4; signed:1;
u32 act; // offset:12; size:4; signed:0;
int cpu; // offset:16; size:4; signed:1;
unsigned int drops; // offset:20; size:4; signed:0;
unsigned int processed; // offset:24; size:4; signed:0;
int to_cpu; // offset:28; size:4; signed:1;
};
Run Code Online (Sandbox Code Playgroud)
我发现的所有评论都是bpf代码无法访问前8个字节,但我不明白为什么。
bpf ×9
ebpf ×9
linux-kernel ×7
c ×5
assembly ×1
clang ×1
ftrace ×1
kprobe ×1
libbpf ×1
llvm-clang ×1
macros ×1
manpage ×1
tracepoint ×1