BPF-JIT中bug归类

article/2025/9/19 22:36:34

文章目录

    • 前言
    • BPF-JITs中的bugs分类
      • Subtle architectural semantics(微妙的架构语义)
      • Subtle machine state(微妙的机器状态)
      • Subtle instruction encoding(微妙的指令编码)
    • Bug-fixing commits in BPF JITs in the Linux kernel (May 2014–April 2020)
    • 其他

前言

本篇内容来自:Nelson L, Van Geffen J, Torlak E, et al. Specification and verification in the field: Applying formal methods to {BPF} just-in-time compilers in the Linux kernel[C]//14th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 20). 2020: 41-61.

这里记录论文3.2节 Bugs in BPF JITs 的阅读过程。


BPF-JITs中的bugs分类

我们手动审查了linux 内核,从2014年五月到2020年四月,所有关于BPF JITs的提交信息。我们对JIT目标为Arm32,Arm63,RV64,x86-32,x86-64的correctness bugs进行分类。correctness bugs指那些JIT生成错误指令的bugs,而非那些JIT过程中内存泄露等问题。下面描述了我们使用jitterbug发现的一些代表性的错误。


Subtle architectural semantics(微妙的架构语义)

RSH64_IMM是BPF中的逻辑右移指令。该指令根据立即数,将64位寄存器中的数值,进行逻辑右移。目标架构Arm32中的寄存器是32位的。所以JIT需要使用两个32位的寄存器来表示BPF指令中的64位寄存器。

下面的JIT代码逻辑为,使用两个32位寄存器,来表达一个64位寄存器的逻辑右移。

  • 当右移位数小于32时:低位寄存器本身右移+接受高位移除的内容;高位寄存器直接右移。
  • 当右移位数等于32时:使用高位寄存器的内容填充低位寄存器;高位寄存器清零。
  • 当右移位数大于32时:高位寄存器移位后的内容填充低位寄存器;高位寄存器清零。
/* rd[0]: upper 32 bits of the destination register
rd[1]: lower 32 bits of the destination register
tmp2[1]: a temporary register */
if (val < 32) {
/* tmp2[1] = rd[1] >> val */
emit(ARM_MOV_SI(tmp2[1], rd[1], SRTYPE_LSR, val), ctx);
/* rd[1] = tmp2[1] | (rd[0] << (32 - val)) */
emit(ARM_ORR_SI(rd[1], tmp2[1], rd[0], SRTYPE_ASL,32 - val), ctx);
/* rd[0] = rd[0] >> val */
emit(ARM_MOV_SI(rd[0], rd[0], SRTYPE_LSR, val), ctx);
} else if (val == 32) {
/* rd[1] = rd[0] */
emit(ARM_MOV_R(rd[1], rd[0]), ctx);
/* rd[0] = 0 */
emit(ARM_MOV_I(rd[0], 0), ctx);
} else {
/* rd[1] = rd[0] >> (val - 32) */
emit(ARM_MOV_SI(rd[1], rd[0], SRTYPE_LSR,val - 32), ctx);
/* rd[0] = 0 */
emit(ARM_MOV_I(rd[0], 0), ctx);
}

代码逻辑很好,没问题。但是,对于Arm32而言,当逻辑右移的立即数为0时,表示逻辑右移32位。

BPF的RSH64_IMM指令,逻辑右移0位时,应当什么也没做。但按照上面的JIT代码指令,当翻译的指令在Arm32中执行时,会将寄存器清零。

修复这个问题也很容易,只需要在JIT时,当val为0时,什么也不做。


下面展示的是RV64 JIT的bug。

在RISC-V Reader Chiness的2.4节中,有这样一句话:

将 auipc 中的 20 位立即数与 jalr 中 12 位立即数的组合,我们可以将执行流转移到任何 32 位 PC 相对地址。

其JIT代码实现如下。

/* check if rvoff is in the range »−231,231 −1… */
if (!is_32b_int(rvoff))
return -ERANGE;
...
s64 upper = (rvoff + (1 << 11)) >> 12;
s64 lower = rvoff & 0xfff;
/* aupic t1,upper */
emit(rv_auipc(RV_REG_T1, upper), ctx);
/* jalr ra,lower(t1) */
emit(rv_jalr(RV_REG_RA, RV_REG_T1, lower), ctx);

但是,上面的JIT代码,在RV64中,无法实现转移到任何32位PC的相对地址。

因为,在RV64中,auipc和jalr使用的都是有符号数。这表示jalr中12位数的最高位为符号位。

所以在RV64中,能够转移的最大范围是:
在这里插入图片描述


Subtle machine state(微妙的机器状态)

由于x86-32中寄存器数量的限制,JIT不得不将BPF寄存器入栈处理。

BPF中的JSET64_REGJSET32_REG是两条跳转指令。JSET32_REG用C语言宏定义表示为 BPF_JMP[32]|BPF_JSET|BPF_X

JSET64_REG DST,SRC,OFF的语义为jump if DST & SRC

x86-32中,对这两条指令的JIT编码如下。

  • 如果是JSET32_REG:将目标寄存器内容,从栈中提取放入eax中;将源寄存器内容,从栈中提取出来放入ecx中。
  • 如果是JSET64_REG:上面两步照做,分别存放的是源寄存器和目标寄存器的低32位。在使用两个寄存器edx,ebx分别存放源寄存器和目标寄存器的高32位。
  • 将BPF的目标寄存器和源寄存器从栈中取出,放入机器真实的寄存器后,进行&操作。
  • 根据&结果,修改标志zf位。跳转语句根据zf判断,是否跳转。
case BPF_JMP | BPF_JSET | BPF_X:
case BPF_JMP32 | BPF_JSET | BPF_X:
bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP;
u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
u8 sreg_lo = sstk ? IA32_ECX : src_lo;
u8 sreg_hi = sstk ? IA32_EBX : src_hi;
if (dstk) {EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EAX),STACK_VAR(dst_lo)); /* eax <- dst_lo */if (is_jmp64)EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EDX),STACK_VAR(dst_hi)); /* edx <- dst_hi */
}
if (sstk) {EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_ECX),STACK_VAR(src_lo)); /* ecx <- src_lo */if (is_jmp64)EMIT3(0x8B, add_2reg(0x40, IA32_EBP, IA32_EBX),STACK_VAR(src_hi)); /* ebx <- src_hi */
}
/* and dreg_lo,sreg_lo */
EMIT2(0x23, add_2reg(0xC0, sreg_lo, dreg_lo));
/* and dreg_hi,sreg_hi */
EMIT2(0x23, add_2reg(0xC0, sreg_hi, dreg_hi));
/* or dreg_lo,dreg_hi */
EMIT2(0x09, add_2reg(0xC0, dreg_lo, dreg_hi));
goto emit_cond_jmp; /* emit conditional jump */

上面JIT代码,当BPF指令为JSET64_REG,没有问题。

当时当BPF指令为JSET32_REG,有问题。因为,当指令为JSET32_REG时,不需要EMIT2(0x23, add_2reg(0xC0, sreg_hi, dreg_hi));EMIT2(0x09, add_2reg(0xC0, dreg_lo, dreg_hi));。写代码的人估计是为了统一,让其存在,但,由于没有初始化寄存器edx、ebx为零,它们的操作会导致zf位再次被修改。


Subtle instruction encoding(微妙的指令编码)

论文中,想表达的意思是:JIT作为(BPF指令和实际机器指令)中间者,其想要生成的指令和实际生成指令,不等价。

论文举例是EMIT3(0xC7, add_1reg(0xC0, dst_hi), 0)生成的指令和mov dst_hi,0不等价。

这里是EMIT3的宏展开。我暂时没明白0xC70xC0是什么含义。不要紧,后期了解BPF-JIT之后,自然知道。


Bug-fixing commits in BPF JITs in the Linux kernel (May 2014–April 2020)

我这里粘贴下论文附录截图,详细见论文。

The following table lists bug-fixing commits in the BPF JITs in the Linux kernel for Arm32, Arm64, RV64, x86-32, and x86-64.
The superscripts 𝐽 and S mark those for fixing bugs found using Jitterbug and the BPF bug finder in Serval, respectively

在这里插入图片描述


其他

之前简单整理过ebpf指令系统,感兴趣的话,自行参考ebpf指令系统。


http://chatgpt.dhexx.cn/article/c60LHNb8.shtml

相关文章

bpf的加载流程分析

文章目录 前言elf结构简介load_bpf_file函数准备工作创建map处理所有的重定向section加载ebpf程序 参考 前言 我们知道&#xff0c;使用clang/llvm编译生成的target为bpf的elf文件&#xff0c;使用load_bpf_file函数加载进入内核。 所以&#xff0c;这里&#xff0c;我们需要…

深入理解 BPF map 实现机制

揭秘 BPF map 前生今世 目录 揭秘 BPF map 前生今世1. 前言2. 简单的使用样例用户空间与内核 BPF 辅助函数参数对比 3. 深入指令分析3.1 查看 BPF 指令3.2 加载器创建 map 对象3.3 第一次变身&#xff1a; map fd 替换3.4 第二次变身&#xff1a; map fd 替换成 map 结构指针 4…

bpf简介1

文章目录 前言prefaceIntroduction历史发展结构 推荐阅读 前言 来源&#xff1a;Linux Observability with BPF 这里整理下该书第一章&#xff1a;preface && Introduction 这本书有中文版的《Linux内核观测技术BPF》。这个链接里面的资料也是很好的&#xff0c;可以…

BPF技术学习分享

什么是BPF程序&#xff1a; BPF is a highly flexible and efficient virtual machine-like construct in the Linux kernel allowing to execute bytecode at various hook points in a safe manner. BPF程序 ----LLVMClang----> BPF字节码 ----JIT----> BPF指令集&…

BPF之事件源

基础 1. BPF和eBPF概念 BPF 原是 Berkeley Packet Filter&#xff08;伯克利数据包过滤器&#xff09;的缩写&#xff0c;1992诞生&#xff0c;用于网络包过滤。2014经过修改并入 Linux 内核主线&#xff0c;从此 BPF 变成了一个更通用的执行引擎&#xff0c;主要用于网络、可…

DPDK BPF

DPDK BPF DPDK 自版本 18.05 已集成了 librte_bpf, 主要利用rte_eth_rx_burst/rte_eth_tx_burst 回调函数机制, 执行eBPF字节码. 当前支持以下特性: base eBPF ISA (except tail-pointer)JIT (x86_64 and arm64 only)eBPF code verifieruser-defined helper functions (64-bi…

Linux超能力BPF技术介绍及学习分享

近两年BPF技术跃然成为了一项热门技术&#xff0c;在刚刚结束的KubeCon 2020 Europe会议上有7个关于BPF的技术分享&#xff0c; 而在KubeCon 2020 China会议上也已有了3个关于BPF技术的中文分享&#xff0c;分别来自腾讯和PingCAP&#xff0c;涉足网络优化和系统追踪等领域。在…

bpf原理与入门

一、bpf架构 如上图所示,bpf由六部分构成,以下为其在bpf中的作用: bpf工具:该部分涉及bpf用户态程序、bpf的编译工具,通过bpf编译工具如Clang、LLVM将bpf用户态程序编译成bpf字节码; 加载器:可以简单理解为bpf系统调用,将bpf字节码加载到内核; 验证器:对bpf程序的…

BPF入门1:BPF技术简介

目录 cbpf 介绍ebpf 介绍ebpf 和 cbpf对比ebpf和内核模块的对比 ebpf应用ebpf架构Why BPF is FAST指令虚拟机JIT How BPF extends KernelLLVM 编写ebpf程序BCCBPFTraceC 语言原生方式 国内大厂使用ebpf的实践经验参考 cbpf 介绍 BPF&#xff08;Berkeley Packet Filter &#…

增广拉格朗日函数

对于优化问题 arg ⁡ min ⁡ z E ( z ) ( 1 a ) s . t . C z − b 0 ( 1 b ) \mathop{\arg\min}_{z} \ E(z)\qquad(1a)\\ s.t. \quad Cz-b0 \qquad(1b) argminz​ E(z)(1a)s.t.Cz−b0(1b) 其增广拉格朗日函数被定义为&#xff1a; L ( z , α , μ ) E ( z ) α T ( C z −…

约束优化:PHR-ALM 增广拉格朗日函数法

文章目录 约束优化&#xff1a;PHR-ALM 增广拉格朗日函数法等式约束非凸优化问题的PHR-ALM不等式约束非凸优化问题的PHR-ALM对于一般非凸优化问题的PHR-ALM参考文献 约束优化&#xff1a;PHR-ALM 增广拉格朗日函数法 基础预备&#xff1a; 约束优化&#xff1a;约束优化的三种…

matlab编写拉格朗日插值代码函数

要求&#xff1a;根据拉格朗日多项式插值法原理&#xff0c;设计算法流程并且编写拉格朗日插值代码函数。 代码如下&#xff1a; function[y]lagrange(x0,y0,x) %建立一个函数名为lagrange的函数&#xff0c;输入x0,y0为插值点的坐标&#xff0c;均为数组&#xff0c;x为要…

拉格朗日函数最优化问题

目的&#xff1a;将有约束条件的函数最优化问题通过拉格朗日函数转化为无条件的函数最优化问题。 条件极值最优化问题&#xff1a; 对于无条件的函数最优化问题&#xff0c;常用的有3种方式&#xff1a; 梯度下降&#xff1a;求解一阶导数&#xff0c;其实就是使用泰勒一阶展…

【深度学习】拉格朗日( Lagrange)中值定理

文章目录 1、定理2、几何意义3、证明思路4、有限增量定理5、推论1、定理 如果函数 f(x) 满足: 在闭区间[a,b]上连续; 在开区间(a,b)内可导。 那么在(a,b)内至少有一点ξ(a<ξ<b),使等式 : f(b)-f(a)=f′(ξ)(b-a) 成立,或: f′(ξ) =(f(b)-f(a)) / (b-a) 或存…

拉格朗日(lagrange)插值及其MATLAB程序

一、n次拉格朗日插值 根据《插值多项式的性质》中的定理6.1可得 其中&#xff08;6.19&#xff09;称为基函数&#xff0c;&#xff08;6.18&#xff09;称为拉格朗日多项式&#xff0c;用&#xff08;6.18&#xff09;计算插值称为拉格朗日多项式插值。 方法2&#xff1a;通过…

拉格朗日函数优化

等式约束最优化 可以写为&#xff1a; 引入拉格朗日乘子&#xff08;&#xff09;把问题转换成拉格朗日函数 因为对于任何可行解&#xff0c;有&#xff0c;所以有 &#xff0c;也就是&#xff0c;。 求解。对分别求的偏导数为零&#xff0c;得到方程组求解极值点&#xff0c…

增广拉格朗日函数法

增广拉格朗日函数法 在二次罚函数法中&#xff0c;为了保证可行性&#xff0c;罚因子必须趋于正无穷。此时&#xff0c;子问题因条件数爆炸而难以求解。那么&#xff0c;是否可以通过对二次罚函数进行某种修正&#xff0c;使得对有限的罚因子&#xff0c;得到的毕竟最优解也是…

增广拉格朗日函数法(ALM)

增广拉格朗日函数法&#xff08; Augmented Lagrangian method&#xff09; 一、等式约束 考虑问题&#xff1a; min ⁡ x f ( x ) s . t . c i ( x ) 0 , i 1 , ⋯ , m . \begin{array}{ll} \min_x &f(x)\\ s.t. &c_i(x) 0, \quad i1,\cdots,m. \end{array} min…

广义拉格朗日函数的理解

1、拉格朗日函数&#xff1a; 求极值 求函数f(x,y,z)在条件φ(x,y,z)0下的极值 方法&#xff08;步骤&#xff09;是&#xff1a; 1.做拉格朗日函数Lf(x,y,z)λφ(x,y,z),λ称拉格朗日乘数 2.求L分别对x,y,z,λ求偏导,得方程组,求出驻点P(x,y,z) 如果这个实际问题的最大或…

各种拉格朗日函数

目录 一&#xff0c;拉格朗日函数 二&#xff0c;部分拉格朗日函数 三&#xff0c;增广拉格朗日函数 一&#xff0c;拉格朗日函数 以三元函数为例&#xff1a; 求f(x,y,z)的极值&#xff0c;s.t.g(x,y,z)0 拉格朗日函数L(x,y,z,a) f(x,y,z) a * g(x,y,z) 在极值点处一…