代码之家  ›  专栏  ›  技术社区  ›  Chris H

破解bpf程序上传中的“无效mem访问映射”

bpf c
  •  1
  • Chris H  · 技术社区  · 6 年前

    我正在编写一个自定义应用程序,它读取c代码,调用llvm从该c代码生成bpf字节码,然后重新定位任何bpf映射符号并将其上载到内核。我可以成功地上载和运行不使用bpf映射的程序,但只要将程序重新定位为使用bpf映射,就会出现以下错误:

    invalid mem access 'map_ptr'
    

    详情如下:

    向llvm提供以下输入:

    // Placeholder values for user-requested maps
    void *a;
    
    #include <linux/ptrace.h>
    #include <uapi/linux/bpf.h>
    #include "bpf_helpers.h"
    
    int kprobe__blk_start_request(struct pt_regs *ctx) {
      long rq = PT_REGS_PARM1(ctx);
      u64 val = bpf_ktime_get_ns();
      bpf_map_update_elem(a, &rq, &val, BPF_ANY);
      return 0;
    }
    

    它生成以下字节码

    Found function: kprobe__blk_start_request
      size: 120 bytes
      addr: 0
    relocate: a @ 32
    
    /tmp/bpf7990/bpf.o: file format ELF64-BPF
    
    Disassembly of section .text:
    kprobe__blk_start_request:
           0:   79 11 70 00 00 00 00 00     r1 = *(u64 *)(r1 + 112)
           1:   7b 1a f8 ff 00 00 00 00     *(u64 *)(r10 - 8) = r1
           2:   85 00 00 00 05 00 00 00     call 5
           3:   7b 0a f0 ff 00 00 00 00     *(u64 *)(r10 - 16) = r0
           4:   18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00     r1 = 0ll
           6:   79 11 00 00 00 00 00 00     r1 = *(u64 *)(r1 + 0)
           7:   bf a2 00 00 00 00 00 00     r2 = r10
           8:   07 02 00 00 f8 ff ff ff     r2 += -8
           9:   bf a3 00 00 00 00 00 00     r3 = r10
          10:   07 03 00 00 f0 ff ff ff     r3 += -16
          11:   b7 04 00 00 00 00 00 00     r4 = 0
          12:   85 00 00 00 02 00 00 00     call 2
          13:   b7 00 00 00 00 00 00 00     r0 = 0
          14:   95 00 00 00 00 00 00 00     exit
    

    “A”符号在指令4中重新定位如下:

    Relocating instruction 4
        from code:0x18 dst_reg:1 src_reg:0 off:0x0 imm:0x0
          to code:0x18 dst_reg:1 src_reg:1 off:0x0 imm:0x4
    kprobe__blk_start_request:
           0:   79 11 70 00 00 00 00 00 
           1:   7b 1a f8 ff 00 00 00 00 
           2:   85 00 00 00 05 00 00 00 
           3:   7b 0a f0 ff 00 00 00 00 
           4:   18 11 00 00 04 00 00 00 
           5:   00 00 00 00 00 00 00 00 
           6:   79 11 00 00 00 00 00 00 
           7:   bf a2 00 00 00 00 00 00 
           8:   07 02 00 00 f8 ff ff ff 
           9:   bf a3 00 00 00 00 00 00 
          10:   07 03 00 00 f0 ff ff ff 
          11:   b7 04 00 00 00 00 00 00 
          12:   85 00 00 00 02 00 00 00 
          13:   b7 00 00 00 00 00 00 00 
          14:   95 00 00 00 00 00 00 00
    

    指令4中的“04”是符号“A”映射的FD。 在调用bpf_prog_load之后,我在内核的错误日志中得到以下信息。

    Failed to load kprobe__blk_start_request BPF code
    0: R1=ctx(id=0,off=0,imm=0) R10=fp0
    0: (79) r1 = *(u64 *)(r1 +112)
    1: R1=inv(id=0) R10=fp0
    1: (7b) *(u64 *)(r10 -8) = r1
    2: R1=inv(id=0) R10=fp0
    2: (85) call bpf_ktime_get_ns#5
    3: R0=inv(id=0) R10=fp0
    3: (7b) *(u64 *)(r10 -16) = r0
    4: R0=inv(id=0) R10=fp0
    4: (18) r1 = 0xffff88042be0b000
    6: R0=inv(id=0) R1=map_ptr(id=0,off=0,ks=8,vs=8) R10=fp0
    6: (79) r1 = *(u64 *)(r1 +0)
    R1 invalid mem access 'map_ptr'
    
    Errno: 13 (Permission denied)
    

    我无法破译这个错误日志。内核想告诉我什么?

    1 回复  |  直到 6 年前
        1
  •  2
  •   pchaigno    6 年前

    错误解释

    invalid mem access 'map_ptr' 意味着您试图从无效的内存位置读取数据,特别是映射指针指向的内存位置。

    实际上,参考你的字节码:

       4:   18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00     r1 = 0ll
       6:   79 11 00 00 00 00 00 00     r1 = *(u64 *)(r1 + 0)
    

    首先在r1中加载一个立即数,然后读取r1指向的内存位置。但是,内核验证器将bpf-ld-imm指令标识为映射指针加载。所以, it tags r1 with the map_ptr type . 验证者然后拒绝指令6 because it tries to read the memory location pointed to by the map pointer (您只需要将它传递给地图助手,而不是在bpf程序中使用它)。

    基本上,指令6既无效又不需要。没有它,你的程序应该通过验证程序。


    可能修复

    我没有你的重定位代码,所以我无法复制,但我猜这个无效的字节码与你如何声明映射有关。如果你看看 BPF samples in the Linux kernel ,你会看到的 映射通常声明为全局结构,第一个map helper参数指向该全局结构 :

    // Placeholder values for user-requested maps
    struct bpf_map_def a = {
        .type = BPF_MAP_TYPE_ARRAY,
        .key_size = sizeof(u32),
        .value_size = sizeof(int),
        .max_entries = 1024,
    };
    
    #include <linux/ptrace.h>
    #include <uapi/linux/bpf.h>
    #include "bpf_helpers.h"
    
    int kprobe__blk_start_request(struct pt_regs *ctx) {
      long rq = PT_REGS_PARM1(ctx);
      u64 val = bpf_ktime_get_ns();
      bpf_map_update_elem(&a, &rq, &val, BPF_ANY);
      return 0;
    }