写点什么

应用程序热补丁(三):完整的设计与实现

  • 2017-04-20
  • 本文字数:4126 字

    阅读完需:约 14 分钟

前言

在前两篇文章介绍了应用程序热补丁的关键技术:

  • 修复运行时进程的函数
  • 加载热补丁到进程中
  • 自动生成热补丁等等

这些是组成应用程序热补丁技术框架的关键部分,但是在生产环境中使用热补丁技术还需要考虑适应现代软件的属性、热补丁的安全性、以及在运营中对热补丁的管理等等。

通过介绍 UCloud 应用程序热补丁框架的设计理念和框架中各个组件,我们会解决以下实践中遇到的问题:

  • 热补丁的管理(加载、卸载、激活、回滚热补丁等)
  • 打入热补丁时的安全检查(简单说是什么时候打入热补丁是安全的)
  • 热补丁对多线程的支持等等。

应用程序热补丁的意义介绍 UCloud 应用程序热补丁框架之前,首先介绍一下我们为什么研发和使用热补丁技术。

目前主流的热补丁技术,例如 Ksplice、kpatch、kGraft、以及后来的 livepatch 等都是特别针对 Linux 内核的热补丁技术,可以在不重启系统的情况下,修复内核缺陷。我们一般称为内核热补丁。

UCloud 使用了内核热补丁修复了若干内核问题,避免了重启系统导致的服务中断,保证了操作系统本身的可用性。

在此基础上,我们的下一个目标提高是核心组件中的单点的可用性。例如虚拟化的核心组件 QEMU,虽然作为单点程序运行,但是可用性的要求和内核是一致的。虽然 QEMU 本身支持在线迁移,可以迁移客户的虚拟机到新版本的 QEMU 上,但是迁移本身比较笨重。在迁移过程中会牵扯多个模块,例如网络、存储等,同时迁移时间和虚拟机的 downtime、break time 在运营上也会带来挑战。

对比 QEMU 通过在线迁移升级,使用热补丁修复极快,并且对虚拟机周边环境没有依赖,可以对用户的虚拟机做到静默升级。由于热补丁本身的天然属性,热补丁更适用于代码改动较小的修复(例如安全漏洞),而在线迁移升级比较适用于大版本的升级。

在 UCloud 我们通过热补丁修复了若干次 QEMU 的缺陷和安全漏洞,极大提高了可用性和安全性。因此我们认为对于代码改动较小的问题时,热补丁是一个完美的解决方案。

为什么要自研应用程序热补丁技术?答案也很简单,我们无法找到一个实用并且易用的应用程序热补丁技术,同时也由于我们已经在内核热补丁领域的具有一定的积累,所以决定敢为人先、自研应用程序热补丁技术。

设计理念提出需求

介绍设计理念之前,首先应该提出应用程序热补丁在 UCloud 云平台的需求:

  • 应用程序热补丁的适用场景和内核热补丁是一致的,目的是修复缺陷,而不是增加功能和升级版本。所以应用程序热补丁必须允许函数级别上的修改(不论是本地函数还是全局函数)。
  • 应用程序热补丁必须是安全的,也就是打入热补丁的前后进程的状态必须一致,热补丁只会操作修改的函数,不会影响进程的正常运行。
  • 应用程序热补丁必须支持云平台环境中现代软件具有的特性,比如说 Linux x86_64、多线程等等。
  • 热补丁必须由工具来构建,也必须要由工具来管理(加载卸载等)。
  • 热补丁必须同时支持回滚,同时支持一个进程多次热补丁修复。
  • 降低热补丁运营的难度。

我们针对这些需求,设计出如下的应用程序热补丁框架。

设计思路

  • 支持修复函数级别的、并且可以自动化生成热补丁的工具。
  • 支持多线程、热补丁安全检查、多热补丁状态管理的热补丁加载工具。
  • 运行中的应用程序应记录热补丁的信息和状态,可供外部工具查询。

或者简单来说,我们要做到,拿到源码和 patch 就能通过工具自动生成热补丁,热补丁可以安全的打入运行的多线程应用程序中(不会引起程序的错乱和崩溃),并且支持打入多个热补丁。打入后的热补丁可以被回滚取消,可以查询当前应用程序中热补丁的状态和信息。

框架组件

这个框架的设计我们通过以下组件实现:

Creator

负责通过 patch 和源码自动化生成热补丁。

支持函数级别修复,不论本地函数还是全局函数。

Loader

负责加载热补丁到目标进程中,也负责管理热补丁(类似于客户端程序)。

目标进程支持 Linux x86_64、多线程等。

支持热补丁安全检查。

支持对热补丁状态的操作(例如加载、卸载、激活、回滚查询等等)。

Core Runtime

负责记录多个热补丁的状态和信息,同时提供热补丁通用操作。

作为热补丁模块的通用运行时框架被 Loader 加载到目标进程中。

Loader 对热补丁状态的操作最终由 Core Runtime 在目标进程空间中执行。

热补丁(补丁本身)

负责提供修复后的替换代码和额外信息。

被 Loader 加载到目标进程中,注册自己的信息到 Core Runtime

在激活后使用自身包含的替换代码代替有问题的函数。

组件之间协作如下图所示,Creator 工具根据程序源码和 patch 生成热补丁模块,然后 Loader 将热补丁模块加载到目标进程 Process 的地址空间里,最后热补丁和通用运行时 Core Runtime 一起完成热修复。

实现方法

接下来分别讲各个组件的实现:

Creator

基于对多种内核热补丁技术的理解,我们认为应用程序的热补丁也是可以通过工具自动生成的。虽然相比内核,应用程序的格式更加复杂、编译链接的过程也更不固定,但是自动生成热补丁应该是可行的。

我们知道,编译源代码之后会生成目标文件,将单个或多个目标文件链接可以生成可执行文件。目标文件和可执行文件都是 ELF 格式(Executable and Linkable Format)。ELF 是一种标准且通用的文件格式,Linux 上的可执行文件、目标文件、库、core dump 都是 ELF。

Creator 工具根据 ELF 标准的格式,解析修复前后的目标文件,找到前后不同的函数,提取出差异(包括改变和新增的函数),连同差异本身的属性信息,生成一个动态链接库格式的热补丁。如下图所示:

之前的文章介绍过二进制比较生成热补丁替换代码,这里不再赘述。

Loader

Loader 工具作为一个客户端程序,操作目标进程 Process,包括热补丁的加载、激活、回滚、卸载、查看等。如下所示:

Loader 利用了 ptrace 调用。Loader 通过 ptrace 可以对 Process 的内存、寄存器进行读写,改变 Process 的运行状态,也可以捕获 Process 的信号。这样 Loader 可以停止 Process 的运行,并根据 AMD64 ABI 对内存和寄存器进行修改,使 Process 执行 dlopen 等函数,加载热补丁到 Process 的地址空间中,或者执行其他热补丁的操作。热补丁被加载之后,在 /proc/pid/maps 文件中可以看到。

Loader 如何利用 ptrace 加载热补丁在之前的文章中有详细描述,不再赘述。

Loader 随后会停止 Process 所有的线程,准备激活热补丁,此时 Loader 需要进行一致性检查,也就是查看热补丁的激活对当前的所有线程来讲是否安全。需要检查的是热补丁的需要替换的函数是否在线程当前函数调用栈上,如果在调用栈上,说明现在是不安全的,激活热补丁不能保证一致性。

在停止所有线程的时候,首先需要得到全部的线程信息,可以通过 libthread_db 库与进程中 libc 进行交互得到线程的信息,也可以通过 /proc/pid/tasks/ 目录从内核中直接得到线程的信息。在停止所有线程之后,需要再次获取所有线程信息,查看是否有新增线程,如果有需要停止新增线程。重复以上动作直到没有新线程出现。

Core Runtime

在一个进程的生命周期中,可能需要多次热补丁修复,同时多个热补丁也会使用一些通用的功能,因此需要一个通用的核心模块来提供通用功能,并且记录进程中每个热补丁的信息。这个通用模块作为一个动态链接库,我们叫做 Core Runtime。

Loader 在加载热补丁时,首先需要加载 Core Runtime 到进程的地址空间,对进程而言,Core Runtime 只需要加载一次。

在热补丁被加载到进程的地址空间后,通过构造函数,首先向 Core Runtime 提供自己的信息,注册到 Core Runtime 里,然后将热补丁中差异函数的需要重定向的部分手动计算重定向。在激活热补丁的时候,Core Runtime 会根据热补丁注册时得到的信息,保存旧函数,并把旧函数入口位置替换成跳转到新的函数的机器码,完成热修复。如下所示:

在回滚热补丁的时候,Core Runtime 把旧函数入口位置恢复,完成回滚。

Core Runtime 同时可以管理多个热补丁,以热补丁的名字作为 ID,区分不同的热补丁,记录必要的信息。

如下所示:

热补丁(补丁本身)

这里的热补丁指的是狭义上的作为动态链接库被 Loader 加载到目标进程 Process 中的热补丁。

热补丁由 Creator 产生,包含了替换代码和一些动态信息(比如新旧函数的地址、大小、重定向信息等)。热补丁被加载后,包含的函数和变量就存在于目标进程的地址空间中。热补丁激活以后,所有对老函数的访问,都会重定向到热补丁地址范围内的新函数。

如下所示:

总结

Creator、Loader、Core Runtime、热补丁这四者构成了 UCloud 热补丁技术框架,这四个组件相辅相成,互相协作完成热补丁。

Creator 负责生成热补丁,Loader 负责热补丁的进程外管理(包括加载、卸载、激活、回滚热补丁等),Core Runtime 负责热补丁的进程内管理(记录热补丁、备份旧函数、恢复旧函数等)。虽然是四个组件,但是都必须遵守同一个热补丁规格标准,这样才能共同完成热补丁的工作。

通过这个框架,极大降低了我们制作热补丁、打入热补丁和运营热补丁的难度。

例如,一个 QEMU 安全漏洞修复的流程可以简化为:

  1. Creator 通过 QEMU 源码和漏洞修复 patch 生成热补丁。
  2. 热补丁被 Loader 打入正在运行的应用程序中(加载并且激活)。
  3. (可选)对运行中的应用程序查询热补丁的状态和信息。
  4. (可选)对已经打入的热补丁进行回滚和卸载。

值得指出的是,目前不是全部 patch 都可以自动生成热补丁,原因是极少部分由于程序修改复杂,但是可以通过手动修改 patch 简化代码或者简化逻辑做到可以自动生成热补丁。大约 90% 的 patch 在无需修改的情况下都能自动生成热补丁。

在一些特定场景下,我们通过第一篇文章(《应用程序热补丁(一):几行代码构造免重启修复补丁》)中介绍的热补丁技术手动编写热补丁即可,无需使用复杂的自动生成热补丁技术。

另外,目前 UCloud 应用程序热补丁技术支持 Linux C 语言程序,但对于其他编译型语言解决思路基本一致(例如 C++ 等)。

在 UCloud,我们利用应用程序热补丁修复了若干紧急安全漏洞和缺陷,在关键时刻迅速解决问题,相比于传统的软件升级方式,解决问题更加及时。

希望通过一系列的文章填补目前应用程序热补丁的空白部分,使更多人了解热补丁的技术原理,让热补丁技术给更多人带来更多的价值。


感谢孟夕对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-04-20 19:003268

评论

发布
暂无评论
发现更多内容

低代码平台浅析:引迈JNPF

互联网工科生

低代码 JNPF 引迈

行业唯一!百度上榜“2023年度中国数据管理十大名牌企业”

百度安全

19家游戏伙伴参加鸿蒙原生游戏合作仪式,与华为游戏中心一起推动产业自主创新和高质量发展

新消费日报

【案例分享】 小鹅通|渐进式拥抱 DevOps

CODING DevOps

DevOps

最佳 Mock 工具排行榜: 顶级5个必知选择

Liam

前端 前端开发 Mock Mock 服务 Mock.js

华为云CodeArts Repo常见问答汇总

华为云PaaS服务小智

华为云

luchysheet升级版univer产品介绍会

梦数技术团队

typescript 前端 Vue3 Typescript

这就是univer

梦数技术团队

typescript 前端 React Hydrate Vue3 Typescript

软通咨询彭智勇(上):探索供应链金融创新路径,驱动产业破局

软通咨询

数字化转型 供应链金融 数字化咨询 管理咨询

华为云CodeArts Pipeline常见问答汇总

华为云PaaS服务小智

华为云

KaiwuDB 连续三年荣获开源中国“优秀开源技术团队”

KaiwuDB

KaiwuDB 优秀开源技术团队

5种容器内指定特定域名解析结果的方式

华为云开发者联盟

容器 云原生 华为云 域名解析 华为云开发者联盟

【教程】源代码加密、防泄密软件

雪奈椰子

华为云CodeArts Deploy常见问答汇总

华为云PaaS服务小智

华为云

宣布推出适用于 Amazon DynamoDB 的增量导出到 S3

亚马逊云科技 (Amazon Web Services)

S3 API 云存储服务 Amazon DynamoDB

点燃数字引擎,天翼云助力汽车行业云上加速跑!

天翼云开发者社区

人工智能 数字化转型 汽车

项目实战接口开发SpringBoot

不在线第一只蜗牛

springboot 项目开发 区块链项目开发

Axie Infinity 之后,Ronin 的潜力何在?

Footprint Analytics

区块链 以太坊 Web3 游戏 Ronin

亮相CCF中国软件大会,天翼云助力千行百业搭上“数字快车”

天翼云开发者社区

人工智能 CCF

管理 Kubernetes 集群这3年,我踩过的十个坑

高端章鱼哥

Kubernetes 计算机

诚邀报名|聚焦项目管理和社区协作,解读开源治理前沿见解和最佳实践

开放原子开源基金会

Java 开源 程序员 开发者 算法

AI浪潮下,大模型如何在音视频领域运用与实践?

不在线第一只蜗牛

人工智能 AI 音视频 大模型

凝心聚力 共赢计算新时代 ——2023计算产业生态大会在京圆满举办

彭飞

理解Mysql索引原理及特性

快乐非自愿限量之名

MySQL 开发 检索 检索引擎

介绍一款低代码数据可视化平台

秃头小帅oi

低代码 数据可视化

小红书可观测 Metrics 架构演进,如何实现数十倍性能提升?

小红书技术REDtech

云原生 可观测性

技术写作者所需的关键技能和知识

小万哥

技术 程序人生 写作 软件开发 作者

应用程序热补丁(三):完整的设计与实现_语言 & 开发_王超_InfoQ精选文章