我正在编写一个bpf程序,其中我需要在openat
系统调用中匹配文件名的前缀。
由于我们无法链接libc
,并且没有这样的内置函数,所以我自己编写了一个。
#define MAX_FILE_NAME_LENGTH 128
#define LOG_DIR "/my/prefix"
#define LEN_LOG_DIR sizeof(LOG_DIR)
int matchPrefix(char str[MAX_FILE_NAME_LENGTH]) {
for (int i = 0; i < LEN_LOG_DIR; i++) {
char ch1 = LOG_DIR[i];
if (ch1 == ' ') {
return 0;
}
char ch2 = str[i];
if (ch2 == ' ') {
return -1;
}
if (ch1 != ch2) {
return -2;
}
}
return (-3);
}
当我尝试加载这个程序时,我得到invalid mem access 'mem_or_null'
错误。
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
Validating matchPrefix() func#1...
38: R1=mem_or_null(id=2,off=0,imm=0) R10=fp0
; int matchPrefix(char str[MAX_FILE_NAME_LENGTH]) {
38: (18) r0 = 0xffffffff ; R0_w=P4294967295
; char ch2 = str[i];
40: (71) r2 = *(u8 *)(r1 +0)
R1 invalid mem access 'mem_or_null'
processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
libbpf: -- END LOG --
libbpf: failed to load program 'syscall_enter_open'
R1
是第一个参数的寄存器。这是一个栈上的字符数组。我需要单独传递数组的长度吗?
函数是这样调用的
char filename[MAX_FILE_NAME_LENGTH];
bpf_probe_read_user(filename, sizeof(filename), args->filename);
if (matchPrefix(filename) != 0) {
return 0;
}
即使我更改函数签名以接受char *
,也会出现其他R1 invalid mem access 'scalar'
错误。
有人可以帮助理解为什么我在功能验证中得到这个错误?
TL; dr使matchPrefix
函数成为static inline
函数应该可以解决验证器问题。
我相信这是因为BPF验证器将您的函数识别为全局函数(相对于内联函数),因此独立验证它。这意味着它不会为参数做任何假设。因此,str
参数被识别为mem_or_null
,并且验证失败,因为您没有检查指针是否为空。
内联函数将解决这个问题,因为验证器将不再看到函数。在验证与matchPrefix
体对应的代码时,将能够保留filename
的推断类型。
使用strcmp有一个更简单的解决方案。查找xdp-project/bpf-next
代码来自相同的
int strcmp(const char *cs, const char *ct)
{
unsigned char c1, c2;
while (1) {
c1 = *cs++;
c2 = *ct++;
if (c1 != c2)
return c1 < c2 ? -1 : 1;
if (!c1)
break;
}
return 0;
}
如果你还有问题,请告诉我。
注意:你不能使用#define来定义字符串。重新验证行
char ch1 = LOG_DIR[i];