0%

ThreadNeck

Wu S, Li Z, Zhou H, et al. Following the “Thread”: Toward Finding Manipulatable Bottlenecks in Blockchain Clients[C]//Proceedings of the 33rd ACM SIGSOFT International Symposium on Software Testing and Analysis. 2024: 1440-1452.

Abstract

区块链客户端是区块链网络的基础,客户端本身存在性能瓶颈,攻击者可以故意利用瓶颈制造DoS。现有工作关注了少部分瓶颈,且局限于手工分析。本文深入分析了瓶颈的主要原因,开发了Thread-Neck,在客户端运行时检测症状。ThreadNeck 将客户端建模为多个线程,描绘它们之间的相互关系,以准确刻画客户端的行为。对Geth/Besu/Reth/FISCO-BCOS的实验发现了13个可利用的瓶颈,其中6个是未知的。

代码仓库:Not Available

Intro

区块链应用:供应链、IoT、保险

客户端存在瓶颈->DoS->数量减少->51%攻击、DApp宕机

Related Attack:

  • DoS相关的工作,NURGLE
  • 对client插桩可能误差(记录日志对IO的影响)

挑战

  • 对客户端的执行的刻画:区块链相比传统软件更加复杂、数据层、网络、共识和合约。每层有独立的设计并且有交互,如何决定划分的细粒度?按层划分可能太粗,按函数划分理解调用的语境
  • 从正常状态中识别出瓶颈:难以生成对客户端的输入(需要满足密码学和共识的约束),客户端是有状态的并且和网络交互,难以穷尽所有执行。因此可行的方法是检测客户端的正常运行,寻找可能存在瓶颈的地方(好low),由于缺乏异常性能指标,这种方法也存在问题。例如,当内存和 CPU 使用等性能指标在正常操作中保持正常时,很难识别出瓶颈的细微迹象。
  • 跨客户端的多样性:每个客户端的瓶颈不同,设计和实现也不同,难以用统一框架进行检测

解决方法:

  • 将客户端可化为线程,根据线程的生命周期对客户端进行划分,为客户端构建了一个线程等待与唤醒图,以展示线程之间的相互关系
  • 分析现有的性能诊断和优化的研究,提出了两类的瓶颈的根本原因。记录了发现了与瓶颈相关的症状,后续进行PoC验证
  • 将不同编程语言的线程模型进行对比,对不同实现提出对应的策略

测试了4种客户端 (Geth/Besu/Reth/FISCO-BCOS),发现了13个可利用的瓶颈,攻击者可以DoS客户端或者对应的模块,6个未知的问题已经被开发者修复

贡献

  • 首个系统研究区块链客户端瓶颈的工作
  • 设计并实现ThreadNeck,准确刻画了区块链客户端的执行并帮助确认瓶颈
  • 将ThreadNeck应用到4个主流客户端,发现了13个瓶颈

Background

Client

客户端维护区块链,和其他客户端通信,验证和传播交易/区块

作者认为Parity和Besu结构一致

Geth的结构(很有帮助)

image-20241210172247318

可以划分为

  • 网络层:管理和P2P网络的通信
  • 存储层:存储以太坊网络的状态,管理账户余额、合约、合约的状态
  • 执行引擎:执行交易、运行合约,包括交易池和EVM
  • 接口层:提供RPC、HTTP和其他接口,外部应用程序可以和客户端交互,发交易和读取区块链数据
  • 外部的consensus engine:确定规则,处理区块的产生和验证。PoS之后,共识引擎已经被分离了,通过接口层和执行层客户端接触

用户通过DApp发送交易,首先交易作为RPC发送给客户端,然后通过网络层传播交易,能够出块的节点可以将交易打包进区块,新产生的区块被广播到网络中。其他节点收到区块后使用共识引擎验证区块,在EVM中执行交易,更新状态,结果存储在客户端

Thread State

线程:程序中的执行路径,可能会存在多个状态

  • running:运行
  • runnable:可运行
  • blocked:阻塞

Threat Model

大多数编程语言使用操作系统提供的线程,Golang的机制不同,采用goroutine,支持了更高的并发

image-20241210193943478

Motivation

瓶颈是软件实现中限制了应用效率的弱点

Motivating Example

真实事件,调用extcodesize,带来严重IO负载

cancun中的策略

image-20241211152658856

Root Cause

分析现有论文,总结方法

  • 无效同步:用于保证线程以特定顺序执行。无效同步指使用错误,导致某些线程挂起,影响程序性能,已有工作给出了无效同步的三类主要原因
    • image-20241211153851788
    • 锁竞争:主要原因,攻击A,B和C会等待更长时间
    • 负载不均衡
    • IO阻塞
  • 资源耗尽:如CPU和内存

为了检测无效同步类的瓶颈,需要理解线程间的交互,因此将客户端建模为线程,画出线程等待和唤醒图,分析线程之间的pattern

为了检测资源类型的瓶颈,测量线程执行路径的资源消耗

Overview

image-20241211163317680

可以划分为两个阶段

  • 在运行时刻画客户端,记录线程唤醒时间和其他相关信息,构造线程等待&唤醒图,将线程之间的相互关系可视化
  • 分析图,监测两类瓶颈对应的症状,检测存在的瓶颈,对于检测出的线程,根据调用栈定位出测试用例。利用开发者提供的模板测试,进行PoC,测试发现的瓶颈是否会导致DoS

Building Wait&Wake Graph

有向图,记录程序执行过程中线程之间等待和唤醒的关系

  • 点代表线程,包含调用栈,记录了程序的执行路径以及资源消耗
    • 磁盘、网络接口认为是 虚拟线程
  • 边代表唤醒关系, A->b 线程A唤醒了线程B,即B可能等待A的执行

image-20241211165226500

接下来讨论如何构造这个图,根据编程语言实现 Thread 的方法

Native

基于wPerf[OSDI18],hook 内核函数 try_to_wake_up

Golang

涉及对go routine的支持

one OS thread -> multiple go routines

始终保持OS thread running

解决办法:阅读go的源码,插桩 goready 函数

image-20241213101314474

利用go官方的trace和pprof测量资源利用

Merge Similar Threads

区块链客户端运行的线程非常多,为了减小图的规模,将相似的线程进行合并

因为很多线程实际执行了相同的功能(网络连接多个线程处理网络请求)

基于call stack鉴定相似的线程

线程建模为稀疏向量,计算余弦相似度

Identify the Bottlenecks

针对两类,无效同步和资源耗尽类型的瓶颈

无效同步

观察出最主要的指标是图中的循环等待,问题转化为在图中检测循环

重视被NIC(网络接口)调起的线程

image-20241213110330983

去掉worker和pool之间的边

注释掉可能会漏掉一些,因此手工代码审计了注释掉的worker和pool

构造PoC很难:

  • 松耦合的系统
  • 需要对源码的深刻理解

方法:unit test,用class和方法名去检索

对于已知事件,策略

  • 增加交易数量
  • 提升交易复杂度
  • 区块大小

未知事件,根据源码分析原因,哪个函数导致了瓶颈,是否被外部输入影响

资源耗尽

xxx presents a challenge.

符号执行无法观察资源占用,路径不敏感的符号执行生成的假阳多,路径敏感的符号执行可扩展性不足(区块链客户端是规模庞大的软件)

解决方法:通过线程来刻画资源占用,关注了两类线程

  • 执行时间长或内存占用高
  • 执行时间或内存分配变化大,表明该类线程容易受到外部输入的影响

使用标准差刻画变化性

Demo

image-20241215100203061

使用Besu作为例子

下载区块头,验证,下载区块体,验证交易签名,将区块导入链

可以观察到存在两个循环,PoC的设计

观察call stack,找到入口点,构造恶意合约,修改单元测试函数

image-20241215100836809

Evaluation

回答的研究问题

image-20241215100939772

  • 能否检测出瓶颈
  • 是否比已有工作更有效
  • 额外的开销有多少
  • 配置会如何影响检测的效果

环境配置

image-20241215185902457

和主网同步,使用现实世界中的交易作为输入

使用主网数据的原因(多样性,更贴合现实世界)

GraphQL API是什么?

运行4天

env:64-bit machine with 16 cores and 256GB memory

使用滑动窗口策略,每2小时刻画120s,频率为4000HZ(这是什么)

RQ1

7个已知,6个未知,所有都被确认,3个CVE

image-20241215190715896

case study

image-20241215190930104

发现IO-2,Peer,Downloader线程之间存在环

peer可以在节点即将结束同步时恶意地延迟消息

可能的缓解方法

image-20241215191658700

RQ2

9%的CPU消耗,内存可不计

RQ3

和已有工作在Besu和C++对比

wPerf发现了568,未能发现本文工具未发现的

给出了不能发现的原因

RQ4

门限实验,合并同类线程的模块 和 资源占用的门限

image-20241215192539520

50% 距离时会漏掉一个bug,因此采用了60%和70%

15 线程

Discussion

可以扩展到其他应用程序中

为了选择区块链作为研究对象

  • 对web3社区的重要性
  • 区块链的开源,有丰富的单元测试用例
  • 工作原理近似,发现的瓶颈和缺陷可以迁移

缺陷:无法适用于所有软件

  • 对于多线程软件使用IPC进行通信,无法有效监测

  • 如果软件线程数太少,难以发现线程间同步的问题

Threat to Validity

  • 依赖人工撰写测试用例,消耗时间较长,缺乏可扩展性

    • 计划后续使用大模型基于模板生成测试用例
    • 结合模糊测试增加测试用例的多样性
  • 区块链客户端数量较少,未能支持Nethermind,未来计划支持更多客户端

  • 瓶颈并不一定会导致系统性能降低,为了检测这类问题不能直接用现在的方法,需要设计新的oracle
  • 代码覆盖率不高,可能因为dead code和路径不够丰富

On CPU 许多性能测量工具采用 插桩 和 采样方法实现,将代码在总体执行时间内的占比排序,缺少出花费最多时间的部分

Off CPU 比如等待的事件也可能是瓶颈

区块链客户端性能测量

高纬度的参数(吞吐量、延迟)

插桩,分析时间消耗的分布,研究交易处理中的瓶颈

每个opcode的耗时

函数的耗时

区块链客户端优化

优化共识算法

分片

合约交易并行

DAG

新的MPT的设计

Conclusion

检测区块链客户端中可利用的瓶颈

给出了两类瓶颈的根本原因

将客户端建模为多个线程,研究多个线程的关系

Q

  • GraphQL API
  • RPC eth_call is free?
  • 存储的消耗?