我有一个 eBPF 程序,我想比较 2 个字符串的结尾:
char buffer[MAX_FILE_NAME];
int length = bpf_probe_read_str(buffer, sizeof(buffer), file_name) - 1;
if (length < 0 || length >= (MAX_FILE_NAME - 1)) {
bpf_trace_printk("ERROR: Failed to read file name. \\n");
return 1;
}
if (length == 0) {
return 0;
}
if (ends_with(file->file_name, file->length, buffer, length)) {
bpf_trace_printk("INFO: Accessed file: %s \\n", file->file_name)
}
return 0;
我生成了此代码,用于对展开的最多 32 个字符的每个可能的比较排列进行比较。这是生成的代码的示例:
inline bool ends_with09(char needle[], const char haystack[], int haystack_length) {
int haystack_start = haystack_length - 9;
if (haystack_start < 0) {
return false;
}
return
needle [0] == haystack[(haystack_start + 0)] &&
needle [1] == haystack[(haystack_start + 1)] &&
needle [2] == haystack[(haystack_start + 2)] &&
needle [3] == haystack[(haystack_start + 3)] &&
needle [4] == haystack[(haystack_start + 4)] &&
needle [5] == haystack[(haystack_start + 5)] &&
needle [6] == haystack[(haystack_start + 6)] &&
needle [7] == haystack[(haystack_start + 7)] &&
needle [8] == haystack[(haystack_start + 8)];
};
inline bool ends_with(char needle[], u8 needle_length, char haystack[], int haystack_length) {
switch (needle_length) {
case 1: return ends_with01(needle, haystack, haystack_length);
case 2: return ends_with02(needle, haystack, haystack_length);
/* ... more generated code ... */
case 9: return ends_with09(needle, haystack, haystack_length);
/* ... more generated code ... */
case 31: return ends_with31(needle, haystack, haystack_length);
case 32: return ends_with32(needle, haystack, haystack_length);
default: return false;
}
};
但是,验证者不断地给我一个如下错误:
invalid unbounded variable-offset read from stack R2
或者:
R2 invalid mem access 'scalar'
即使我添加一个检查来验证
haystack_start + 8 >= haystack_length
(始终为假),程序仍然无法验证:
inline bool ends_with09(char needle[], const char haystack[], int haystack_length) {
int haystack_start = haystack_length - 9;
if (haystack_start < 0 || haystack_start + 8 >= haystack_length) {
return false;
}
return
needle [0] == haystack[(haystack_start + 0)] &&
needle [1] == haystack[(haystack_start + 1)] &&
needle [2] == haystack[(haystack_start + 2)] &&
needle [3] == haystack[(haystack_start + 3)] &&
needle [4] == haystack[(haystack_start + 4)] &&
needle [5] == haystack[(haystack_start + 5)] &&
needle [6] == haystack[(haystack_start + 6)] &&
needle [7] == haystack[(haystack_start + 7)] &&
needle [8] == haystack[(haystack_start + 8)];
};
由于从
length
返回的变量 bpf_probe_read_str
,验证者不知道您正在安全地访问数据。相反,它看到您正在从缓冲区访问数据,并且您可能正在缓冲区边界之外读取数据(即使在您的代码中这是不可能的)。在 编写 eBPF 程序的艺术:入门。中,Gianluca Borello 描述了如何让验证者相信您的代码是安全的。
将您的访问/索引与最大可能的缓冲区索引进行按位与运算:
needle [8] == haystack[(haystack_start + 8) & (MAX_FILE_NAME - 1)]
所以生成的代码如下所示:
inline bool ends_with09(char needle[], const char haystack[], int haystack_length) {
int haystack_start = haystack_length - 9;
if (haystack_start < 0) {
return false;
}
return
needle [0] == haystack[(haystack_start + 0) & (MAX_FILE_NAME - 1)] &&
needle [1] == haystack[(haystack_start + 1) & (MAX_FILE_NAME - 1)] &&
needle [2] == haystack[(haystack_start + 2) & (MAX_FILE_NAME - 1)] &&
needle [3] == haystack[(haystack_start + 3) & (MAX_FILE_NAME - 1)] &&
needle [4] == haystack[(haystack_start + 4) & (MAX_FILE_NAME - 1)] &&
needle [5] == haystack[(haystack_start + 5) & (MAX_FILE_NAME - 1)] &&
needle [6] == haystack[(haystack_start + 6) & (MAX_FILE_NAME - 1)] &&
needle [7] == haystack[(haystack_start + 7) & (MAX_FILE_NAME - 1)] &&
needle [8] == haystack[(haystack_start + 8) & (MAX_FILE_NAME - 1)];
};
然后验证者就会知道您没有超出缓冲区的界限。