文章目录

  • 前言
  • 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

BPF-JIT中bug归类相关推荐

  1. iphone XCode调试技巧之EXC_BAD_ACCESS中BUG解决

    http://mobile.51cto.com/iphone-279455.htm XCode调试技巧之EXC_BAD_ACCESS中BUG解决是本文要介绍的内容,在iphone开发的时候EXC_BA ...

  2. JVM系列之:JIT中的Virtual Call接口

    文章目录 简介 最常用的接口List 多个List的调用 不一样的List调用 总结 简介 上一篇文章我们讲解了Virtual Call的定义并举例分析了Virtual Call在父类和子类中的优化. ...

  3. 小师妹学JVM之:JIT中的PrintAssembly

    文章目录 简介 使用PrintAssembly 输出过滤 总结 简介 想不想了解JVM最最底层的运行机制?想不想从本质上理解java代码的执行过程?想不想对你的代码进行进一步的优化和性能提升? 如果你 ...

  4. 小师妹学JVM之:JIT中的PrintCompilation

    文章目录 简介 PrintCompilation 分析PrintCompilation的结果 总结 简介 上篇文章我们讲到了JIT中的LogCompilation,将编译的日志都收集起来,存到日志文件 ...

  5. 软件测试中Bug的分类(类型)

    软件测试中Bug的分类: 1.按严重程度分类: 是指bug对软件质量的破坏程度,即此bug的存在将对软件的功能和性能产生什么样的影响. 崩溃(Blocker):系统无法正常运行.阻碍开发或测试工作的问 ...

  6. 测试中BUG定义、测试BUG的等级划分、Bug流程以及Bug解决优先级

    一个优秀的软件测试师不仅仅能够发现软件中的bug,还能分析出bug产生的原因. 总结了一些软件测试入门必须要了解和学习的BUG基础知识,主要包括BUG定义.测试BUG的等级划分.Bug流程以及Bug解 ...

  7. Android Studio开发过程中BUG解决方案——持续更新

    Android Studio开发过程中BUG解决方案: android萌新打怪升级之旅:android项目开发BUG集成 起笔时间:2022.5.20 版本:2021.2.1 问题描述1:无法查看/d ...

  8. 软件测试中 Bug 书写规范

    Bug的标准及书写规范 一. Bug有效性 交付过程中测试者需按照设定好的模块,对Bug进行归类提交: Bug的类型默认为UI问题.功能问题.崩溃问题,提交Bug时不能弄错: 需求是否明确.前提条件是 ...

  9. JVM系列之:JIT中的Virtual Call

    文章目录 简介 Virtual Call和它的本质 Virtual Call和classic call Virtual Call优化单实现方法的例子 Virtual Call优化多实现方法的例子 总结 ...

最新文章

  1. 安装SQL SERVER2000提示注册表文件被挂起的解决方案
  2. Nginx 配置实战:负载均衡的实现
  3. Nginx笔记总结八:ngx_http_core_module模块中的变量
  4. jquery插件 autoComboBox 自动创建联动的下拉框 如:省市区联动
  5. java 对第三方的异常_Java第三方API调用打开文件方法时抛出异常
  6. 【转】ABP源码分析四十二:ZERO的身份认证
  7. ElasticSearch优化系列二:机器设置(内存)
  8. linux下安装python3.6
  9. VISIO—如何打开?及安装教程
  10. 微软Kinect是怎么做到的
  11. Zint 库:Zint库的编译及使用(二维码QrCode生码),MFC/VC使用实例
  12. 墨尔本计算机读研申请条件,墨尔本大学计算机硕士申请条件
  13. 计算机文献中的经典语录,经典文献语录摘抄
  14. 大联大品佳集团推出基于Audiowise产品的蓝牙5.1助听(Hearing Device)耳机方案
  15. 消失的2000万辆小黄车去哪儿了?
  16. ArcGIS中,一个点集里的点两两连线,比如有4个点,就连3+2+1=6条线
  17. The following classes could not be found - EditText (Change to android.widget.EditText, Fix Build Pa
  18. docker-compose volumes Mounts 类型
  19. java生成算数表达式_惊!小学生要失业了,Java实现生成并计算四则运算表达式。...
  20. Zhong__安装配置MySQL8.0

热门文章

  1. Android 图片添加白边
  2. 等待指示器(2) -- 网络等待指示器
  3. EDUCoder编程练习题解(结构体)
  4. 体验华为操作系统 openEuler 20.03 LTS linux
  5. 高通SDX12:USB2.0 端口枚举失败问题分析及解决方案
  6. 【东京】赏秋叶静美,听时光…
  7. 120篇精华文章打包送,干货慎入!
  8. 东郊到家预约系统开发流程
  9. word没有显示endnote_word没有endnote加载项 endnote word加载项
  10. android手机连nas,安卓手机照样行 NAS存储器大盘点_群晖 USB Station 2_移动存储新闻-中关村在线...