簡體   English   中英

破譯 BPF 程序上傳中的“invalid mem access map_ptr”

[英]deciphering “invalid mem access map_ptr” on BPF program upload

我正在編寫一個自定義應用程序,它讀取 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)

我無法破譯此錯誤日志。 內核想告訴我什么?

錯誤說明

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 指令識別為映射指針加載。 因此, 它使用map_ptr類型標記 r1 然后驗證器拒絕指令 #6, 因為它試圖讀取映射指針指向的內存位置(你只應該將它傳遞給映射助手,而不是在 BPF 程序中使用它)。

基本上,指令#6 既無效又不需要。 沒有它,您的程序應該通過驗證程序。


可能修復

我沒有你的重定位代碼,所以我無法復制,但我猜這個無效的字節碼與你如何聲明地圖有關。 如果您查看Linux 內核中的 BPF 示例,您會看到映射通常被聲明為第一個映射助手參數指向的全局結構

// 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM