关于安全:eBPF编写避坑指南

10次阅读

共计 1966 个字符,预计需要花费 5 分钟才能阅读完成。

0x1: 基本概念

  1. 当应用 tracepoint 的时候,函数参数如何确认?

cat /sys/kernel/debug/tracing/events/syscalls/xxx/format. xxx 为要跟踪的函数,在这里有函数参数定义。

0x2: 注意事项

  1. 写构造体的时候肯定要留神内存对齐,避免被编译器优化填充。
  2. 应用 LLVM 内置的函数做内存操作

    #ifndef memset
    # define memset(dest, chr, n)   __builtin_memset((dest), (chr), (n))
    #endif
    
    #ifndef memcpy
    # define memcpy(dest, src, n)   __builtin_memcpy((dest), (src), (n))
    #endif
    
    #ifndef memmove
    # define memmove(dest, src, n)  __builtin_memmove((dest), (src), (n))
    #endif
  3. 指针被操作过后,就得再次申明,不然会被禁止拜访

    struct iphdr *ip4 = (struct iphdr *) skb->data + ETH_HLEN; // 第一次赋值
    
    skb_store_bytes(skb, l3_off + offsetof(struct iphdr, saddr), &new_saddr, 4, 0); //skb 被操作了 因而 ip4 的值不可信,此时如果操作 ip4 会被回绝
    
    ip4 = (struct iphdr *) skb->data + ETH_HLEN; // 再获取一次
    
    if (ip4->protocol == IPPROTO_TCP) { // 能力失常应用
     // do something
    }

    0x3: 常见报错

  4. R2 min value is negative, either use unsigned or ‘var &= const’

第二个变量须要保障非负。逻辑运算 0xFFFFFFFF。

  1. R2 unbounded memory access, use ‘var &= const’ or ‘if (var < const)’

    bpf 验证器有限度 
    
    #define BPF_MAX_VAR_SIZ (1 << 29)
    if (reg->umax_value >= BPF_MAX_VAR_SIZ) {verbose(env, "R%d unbounded memory access, use'var &= const'or'if (var < const)'\n",
         regno);
     return -EACCES;
    }
  2. invalid stack type R1 off=-72 access_size=536870911

     相似的问题,须要进行逻辑运算保障变量的范畴。off = reg->off + reg->var_off.value;
    if (off >= 0 || off < -MAX_BPF_STACK || off + access_size > 0 ||
     access_size < 0 || (access_size == 0 && !zero_size_allowed)) {
     verbose(env, "invalid stack type R%d off=%d access_size=%d\n",
         regno, off, access_size);
     return -EACCES;
    }
  3. 从 map 中 lookup 进去的指针,不能间接 update 回去,在 eBPF 代码中更新值之后不再须要从新 update,因为拿到了援用.
  4. 字符串拷贝能够应用编译器内置的 __builtin_memcpy
  5. 一个 bpf 程序不能申请太多的栈空间,目前限度 512Byte,多了就会报错:Looks like the BPF stack limit of 512 bytes is exceeded.。例如在程序中申请了两个数组 char arr1[256];char arr2[256]; 程序就会报错了
  6. 程序蕴含无奈执行到的指令

    unreachable insn 1
  7. 程序读取未初始化的寄存器

    0: (bf) r0 = r2
    R2 !read_ok
  8. 程序退出前未设置 R0 寄存器

    0: (bf) r2 = r1
    1: (95) exit
    R0 !read_ok
  9. 程序拜访超出栈空间

    0: (7a) *(u64 *)(r10 +8) = 0
    invalid stack off=8 size=8
  10. 未初始化栈内元素,就传递该栈地址

    0: (bf) r2 = r10
    1: (07) r2 += -8
    2: (b7) r1 = 0x0
    3: (85) call 1
    invalid indirect read from stack off -8+0 size 8
  11. 程序未查看 map_lookup_elem() 的返回值是否为空就开始应用

    0: (7a) *(u64 *)(r10 -8) = 0
    1: (bf) r2 = r10
    2: (07) r2 += -8
    3: (b7) r1 = 0x0
    4: (85) call 1
    5: (7a) *(u64 *)(r0 +0) = 0
    R0 invalid mem access 'map_value_or_null'

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0