Li W, Yang H, Luo X, et al. PyRTFuzz: Detecting Bugs in Python Runtimes via Two-Level Collaborative Fuzzing[C]//Proceedings of the 2023 ACM SIGSAC Conference on Computer and Communications Security. 2023: 1645-1659.
Abstract
Python的runtime包括语言解释器和runtime的库
PyRTFuzz在编译器层和应用层分别采用了基于生成的fuzzing和基于变异的fuzzing,利用静态分析提取运行时的API,以格式/结构感知应用程序输入生成为导向的自定义类型引导的变异策略
针对Cpython实现,应用到了3个版本的runtime中,发现了61个可利用的bug
Introduction
现有的机器学习系统基本采用python实现,安全性和可靠性依赖于Python runtime,包括interpreter和runtime library
- compiler:代码执行之前,负责将一种语言写的源码转换成另一种计算机代码(通常是机器码)
- interpreter:分析并执行代码;把源代码翻译为更高效率的中间码执行;执行解释器内部的编译器预编译后保存的代码
现有的使用最广泛的CPython过去5年有2000个bug,但是缺乏自动化分析工具
Related work
现有对compiler和runtime的测试工具确实存在
- fuzzing JVM
- 基于生成的fuzzer:JSfunfuzz,TreeFuzz,Skyfire,学习现有样例的语法特征生成有效的测试用例
- 基于变异的fuzzer:Superion、Fuzzil,通过编辑AST或其他IR
Python runtime fuzz的挑战
- 对Python运行时进行测试需要同时测试解释器核心和语言的运行时库,只考虑生成有效的测试用例不够,还需要测试用例的输入。全面模糊测试Python运行时将需要两个不同层次(生成程序与具体程序输入)的模糊测试密切协作(即广泛地执行解释器和运行时库之间的交互)。然而,目前对于如何设计这样一个两级协作模糊测试技术的先前知识很少
- 测试解释器需要多样性但是语义有效的程序
- 为fuzz生成高质量的输入:普遍的挑战,尤其python是动态类型语言,数据类型不可用
为了解决以上挑战,提出了PYRTFuzz,2层协同式模糊测试解决挑战2
第一级模糊测试通过基于规范的Python代码生成方法,根据使用静态和动态分析提取的Python运行时中每个API的描述,生成具有不同控制流复杂性的有效且多样化的Python应用程序,以解决挑战2
第二层,基于变异的fuzzing对给定的runtime进行插桩,收集覆盖率进行反馈,生成具体的python程序的输入。生成输入的过程中利用API的描述进行类型引导,解决挑战3
基于已有的python fuzzer Atheris和libFuzzer,测试了CPython的3个不同版本:3.7.15/3.8.15/3.9.15
调整被测试app的大小测试了可扩展性
测试了5*24小时,发现了61个bugs,分布在3个python版本中
程序生成部分:4.77 KLoC of code in 40 minutes with a memory usage of 291.71 MB
Background and Motivation
Greybox fuzzing
基于覆盖率的灰盒测试是常采用的技术,针对测试样例的生成方法,可以分为两类:Mutation和generation
Generation是基于语法和定义的规则生成输入,需要保证语法和语义的有效
基于变异的容易实施,基于生成的方法发现深层bug更有效
compiler testing
关键挑战是生成有效且多样的程序,现有方法包括基于语法的和基于程序变异的
Python Runtime Fuzzing
工作动机包括Python的流行和实证研究结果
CPython是最流行的Python运行时,作者收集了98.3k个历史issue,发现了23.4k个bug相关的,分析了每年上报多少,上报的趋势
为了理解漏洞是如何检测出的,手工分析了500个issue,超过98%都是开发者发现的,说明确实需要漏洞检测工具;漏洞大多80%分布在library中
现有代码生成工作的缺陷:
- 忽略了多样性
- 虽然通过各种Python应用程序覆盖不同领域很重要,但这对于有效地测试Python运行时来说还不够
- 缺乏整体性测试:Python应用程序在Python运行时的环境中运行,该环境包括解释器核心和运行时库。分别测试这两个部分对于全面测试来说是不够的。对于测试Python运行时来说,应该同时关注解释器核心、运行时库以及两者之间的交互作用。
例子,不同输入下报错类型不一样,”%2u” 表示将一个无符号整数(unsigned integer)按照至少两位的宽度进行格式化输出,并在需要时在前面填充空格
这个例子说明如果我们不为特定的运行时模块(比如locale)生成应用程序,那么该模块内部的Bug可能无法被触发(限制L1)。此外,即使生成了各种不同领域的应用程序,如果不考虑应用程序的各种输入,可能还不足以检测出所有的Bug(限制L2)。最后,不同的结果显示了解释器核心和运行时库之间的相互作用和协作,凸显了对它们进行整体测试的重要性(限制L3)。
Technique Design
Overview
level1: generation-based fuzzing
level2: mutation-based fuzzing
输入包括3个,单元测试、和2部分源码
获得Runtime的库的ABI,先是没有类型的,通过执行测试用例得到类型
通过声明式规范语言SLang来生成python程序,为每一个API生成一个APP,将所有APP整理为一个序列
- 为了在每个API的模糊测试深度和在所有API上的模糊测试广度之间取得平衡,L1核心会为每个APP的第2级模糊测试安排一个预算时间。一旦预算用完,L1核心会根据上一次第2级模糊测试的覆盖范围决定是否为相同的API生成另一个(更复杂的)APP,如果是,则触发APP生成,并根据上一次第2级模糊测试迭代中的覆盖情况选择下一个APP进行第2级模糊测试
- L2核心最初会维护一个每个应用程序的种子队列,其中种子是随机生成的。随后,通过基于收集到的覆盖反馈的自定义变异方案,对种子进行变异以获取新的输入值。自定义变异器可以生成适应每个特定应用程序输入格式的值,这得益于在第2阶段应用程序生成过程中插入的探针。这些探针旨在根据应用程序中调用的API的(带类型的)描述,将字节序列解码为单独的参数值。任何触发的Bug以及触发的种子将作为PyRTFuzz的输出产生
Runtime API Description Extraction
python的官方文档给出了abi,但是是非结构化的文本形式,手工提取折磨,需要自动化方式
采用Python的AST parser进行提取,鉴于静态类型推断的不准确性,暂时将类型设置为None,并将在下一步进行细化。因此,静态提取以无类型的API描述结束。
对于异常字段,会尽可能全面地收集API可能抛出的明确异常以及从导入的模块中隐式抛出的异常。这些信息在应用程序生成期间指导API调用的异常处理,这对于避免在第2级模糊测试期间将异常错误地标识为Bug非常重要
动态改进:运行测试用例,提取返回值和参数的类型
算法每次处理一个单元测试,inspect
是 Python 标准库中的一个模块,能够获得对象的信息
问题:
- 动态执行不一定可以覆盖全部的abi
- python的动态特性,每个参数返回类型可能在不同执行中不同
- argue:只要有一个就足够生成有效的输入
Level-1 fuzzing
目的:生成多样化的有效的python程序
- API 覆盖率
- APP多样性:测试到API的场景,控制流要复杂
- APP有效性:语义和语法上有效,数据流可达性要从程序入口到call site
使用SLang简单的声明性规范语言,SLang通过一组生成原语作为最低级语言构造来定义规范的语法和语义。使用SLang,开发人员可以表达规范要求,并通过SLang生成Python应用程序。SLang提供了一种简洁的方式来描述应用程序的要求和行为,以及如何生成满足这些要求的代码。
一个SLang程序(即APP规范)𝑃是一个语句序列𝑆∗。一个语句𝑆只有一种类型:赋值语句。在每个赋值语句中,右值𝐶(𝑒)∗表示一个取表达式𝑒的原语𝐶;左值𝑐表示𝐶(𝑒)∗的结果。这里的表达式𝑒可以是两种类型之一:一个变量𝑐或者对运行时API 𝐴的调用。
Primitive Derivation
SLang的原语是根据Python语言参考文档并参考解释器实现而推导出来的,总结了python程序的关键控制流结果,可分为基本原语和扩展原语
基本原语只接受运行时API的调用作为输入。它用于构建Python程序的基本(控制流)结构,该程序可以是面向对象或面向过程的,并定义程序的入口点。因此一个SLang程序应该以一个且仅有一个基本原语开始。
算法2的伪代码介绍了基本原语的一般操作,生成的P包含三部分T,D,M 包装ABI调用的D,主函数
扩展原语:扩展原语将其他原语的结果作为输入。它用于增加APP的复杂性,同时使其多样化。这些原语的推导还受到我们在现实世界中观察到的Python软件中的编程模式的影响。
通过自上而下的方式将输入程序𝑃包装起来生成新的应用程序。根据表2所示,推导出了七个原语,它们基于相关的Python抽象语法树(AST)操作进行实现。每个原语可以操作任何Python运行时API,而不依赖于特定API的语法或语义,这允许生成覆盖所有API的APP,从而满足需求R1。
Implementation and limitation
https://github.com/awen-li/PyRTFuzz
https://figshare.com/s/d5b8d5a7111abe4eafb1
Brief Introduction and setup demonstration: https://youtu.be/cOhi9eG-IK0?si=p3ef3BDaMGGIwd4r
Ideas
- 实证研究,EVM的调研,不同EVM的issue整理
- 能否利用github的测试用例
- 大模型做abi提取
- solidity的slang
- SLang的撰写依赖专家知识