当读取安全且在范围内时,eBPF 验证器错误“无界变量偏移读取”

问题描述 投票:0回答:1

我有一个 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)];
};
c linux ebpf
1个回答
0
投票

由于从

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)];
};

然后验证者就会知道您没有超出缓冲区的界限。

© www.soinside.com 2019 - 2024. All rights reserved.