我有用于使用跟踪点跟踪 execve 的 ebpf 代码:
#define EXECVE_COMM_LENGTH 1024
#define EXECVE_ARGS_COUNT 100
#define EXECVE_ARG_SIZE 20
#define DEFAULT_SUB_BUF_LEN 16
#define DEFAULT_SUB_BUF_SIZE 255
struct p_exec_program_event
{
u64 ktime;
u32 pid;
u8 filename[EXECVE_COMM_LENGTH];
u8 args[EXECVE_ARGS_COUNT][EXECVE_ARG_SIZE];
u8 pwd[DEFAULT_SUB_BUF_LEN][DEFAULT_SUB_BUF_SIZE];
};
inline int read_dentry_strings(struct dentry *dentry, u8 buf[DEFAULT_SUB_BUF_LEN][DEFAULT_SUB_BUF_SIZE])
{
struct dentry *curr_dtry = dentry;
struct dentry *lastdtryp = dentry;
unsigned int i = 0;
if (buf)
{
bpf_probe_read_str(buf[i], DEFAULT_SUB_BUF_SIZE, BPF_CORE_READ(curr_dtry, d_name.name)); // problems start here
for (i = 1; i < DEFAULT_SUB_BUF_LEN; i++)
{
struct dentry *parent = BPF_CORE_READ(curr_dtry, d_parent);
if (parent != lastdtryp)
{
lastdtryp = parent;
curr_dtry = parent;
bpf_probe_read_str(buf[i], DEFAULT_SUB_BUF_SIZE, BPF_CORE_READ(curr_dtry, d_name.name));
}
else
break;
}
}
return 0;
}
inline int read_argv(const char *const *argv, u8 args[EXECVE_ARGS_COUNT][EXECVE_ARG_SIZE])
{
for (int i = 1; i < EXECVE_ARGS_COUNT; i++)
{
char arg[EXECVE_ARG_SIZE];
void *curr_arg = (void *)&argv[i];
if (curr_arg != NULL)
{
const char *argp = NULL;
bpf_probe_read_user(&argp, sizeof(argp), curr_arg);
if (argp != NULL)
{
bpf_probe_read_user_str(args[i - 1], EXECVE_ARG_SIZE, (void *)argp);
}
else
{
break;
}
}
else
{
break;
}
}
return 0;
}
inline int handle_execveat(char *filename, const char *const *argv)
{
struct p_exec_program_event *event_info;
if (!filename || !argv)
{
return 0;
}
event_info = bpf_ringbuf_reserve(&p_exec_program_events, sizeof(struct p_exec_program_event), 0);
if (!event_info)
{
return 0;
}
event_info->ktime = bpf_ktime_get_ns();
event_info->pid = bpf_get_current_pid_tgid();
long res = bpf_probe_read_user_str(event_info->filename, sizeof(event_info->filename), filename);
if (res)
{
if (event_info->filename[0] == '.')
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
struct dentry *dentry = BPF_CORE_READ(task, fs, pwd.dentry);
read_dentry_strings(dentry, event_info->pwd);
}
// else -> absolute path in filename already resolved
}
read_argv(argv, event_info->args);
bpf_ringbuf_submit(event_info, 0);
return 0;
}
SEC("tracepoint/syscalls/sys_enter_execve")
int sys_enter_execve(struct enter_execve_info *ctx)
{
return handle_execveat(ctx->filename, ctx->argv);
}
当我运行此代码时,我收到错误消息:
2023/12/27 21:41:28 Loading objects: field SysEnterExecve: program sys_enter_execve: Call at insn 36: symbol "handle_execveat": unsatisfied program reference
exit status 1
你能帮我理解 read_dentry_strings 中的代码(或根本代码中)有什么问题吗?
我正在使用 ebpf2go(github 上的最新版本)
我对代码进行了实验,发现问题出在 read_dentry_strings 中 - 无需调用此函数,程序就可以正常工作。我开始注释所有行,发现当我调用
bpf_probe_read_str(buf[i], DEFAULT_SUB_BUF_SIZE, BPF_CORE_READ(curr_dtry, d_name.name));
时出现错误
TL;DR. 该错误表明函数
handle_execveat
未内联。您可以使用 __attribute__((always_inline))
确保编译器将内联它。
2023/12/27 21:41:28 加载对象:字段 SysEnterExecve:程序 sys_enter_execve:在 insn 36 处调用:符号“handle_execveat”:不满足的程序引用
此错误意味着加载程序(在您的例子中为 cilium/ebpf)尝试解析函数调用中的符号
handle_execveat
。
BPF 到 BPF 函数调用是可能的(有限制),但考虑到使用
inline
,这可能不是您想要实现的目标。因此我们可以得出结论,handle_execveat
没有按预期内联。
创建一个函数
inline
实际上并不能保证编译器会内联它。使函数static
增加了编译器实际内联它的机会。标记它 __attribute__((always_inline))
确保它会内联。