LWN:对生产内核使用drgn!

Meta内核开发者OmarSandoval在LinuxPlumbersConference上介绍了drgn,一个可编程的内核调试器,着重讨论了内存写入和断点功能在生产环境中的潜在应用。他希望通过社区的头脑风暴找到在运行内核上执行这些操作的安全解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关注了就能看到更多这么棒的文章哦~

Using drgn on production kernels

By Jake Edge
November 28, 2023
LPC
ChatGPT translation
https://lwn.net/Articles/952942/

drgn是由 Omar Sandoval 在 Meta 内核团队工作时开发的基于 Python 的内核调试器。他现在大部分时间都在致力于 drgn 的开发,包括为工具开发新功能以及在 Meta 解决生产问题时使用 drgn。这使他可以看到信息反馈的两方面的信息。在2023 Linux Plumbers Conference(LPC)上,他主持了一个有关 drgn的内核调试微会议的会话,他希望就如何为调试器添加一些新功能以及具体如何让它们在生产内核上运行,进行了头脑风暴。

快速介绍

Sandoval 在 2019 年有过一次关于drgn的演讲,介绍了该工具的一些基础知识,该工具自那时以来显然在不断发展。他表示,他还进行过其他关于 drgn 的深入讨论,但在 LPC 会议上,他只会对该工具进行快速介绍。之后,他希望专注于两个功能,即写入内存和设置断点,并证明为什么他的团队希望能够在运行中的内核上执行这些操作(“听起来很疯狂”)。他希望头脑风暴之后可以提出一个支持这些功能的机制,同时也提供一个 API,“既友好,又不会因为您意外执行了一些事情而过于危险”。

570cb364bd076a9945b7f5d1e8b20032.png

Drgn 是一种“可编程调试器”;与内置命令不同,它提供了代表内核对象、类型、堆栈跟踪等的内在组成模块,可用于创建所需的工具。例如,有许多跟具体内核相关的辅助函数,提供对各种内部数据结构的访问,例如查找任务结构或遍历各种 slab 缓存。这些函数可以在交互式会话中使用,然后转换为脚本,以便在下次出现类似问题时使用(或与他人共享),他说。

他在笔记本电脑上的虚拟机上对 drgn 进行了简要演示;感兴趣的人可以观看来自会议直播YouTube视频。在 Python REPL(读取-求值-打印循环)中,他预先输入了一些 import 语句,然后进行了一些演示,例如使用其中一个内核助手函数查找 idle 任务的变量名(init_task)。

他还展示了使用 for_each_task() 这个内核助手函数循环,该循环查找了在 shell 中运行的 cat 的 task struct;然后,他可以打印该任务的堆栈跟踪信息,其中包含了所有的符号信息、文件名和行号。他使用堆栈帧编号索引到一个数组,进一步调查特定的堆栈帧,包括其本地变量及其值等。除了 drgn 提供的所有助手函数之外,还有大家贡献出的大量脚本,这些脚本包含了“人们的调试会话”,可以在其中查看。

然而,他展示的所有内容都是只读的;您可以读取活动内核或内核转储(kernel dump)中的任何内存。但是用户一直以来一直在要求能增加对活动内核进行读写的功能;这将允许覆盖内存并在运行中的内核中设置断点。这在开发工作流程中是很合理的需求,Sandoval 说;drgn 可以附加到在 QEMU 中运行的内核,使用其 GDB stub 来完成。开发人员在调试时已经习惯了这种功能,因此他希望在 drgn 中支持它。

他提出了一个内存写入的 API,最底层的形式是接受目标地址和要写入的字节缓冲区,用户负责根据内核类型找出正确的地址以及如何正确地将值放入缓冲区。在此基础上也提供了一个更加用户友好的界面,在某种程度上是跟读取工作对应的;可以查找对象,然后将其字段用作 Python 属性,drgn 确保写入正确。它还可以接受一个 Python 字典,其中的 structure 成员作为 key,写入具有这些值的结构。由于他尚未进行任何开发,因此 API 仍在讨论中。

“设置断点有点复杂,但不会太复杂。”用户可能希望以几种不同的方式设置断点:按地址、函数名、函数名和偏移量或文件名和行号。然后,处理断点可能会使用同步事件循环(synchronous event loop),这个事件就指明哪个线程命中了断点,允许访问堆栈跟踪信息和堆栈帧的本地变量等,并提供一种在处理完成后恢复线程的方式。

Sandoval 再次表示,他希望听到更简单的替代方案或仍需解决的用例。Chris Mason 表示,他希望能够看到何时从某个特定函数中调用频繁调用的函数;Sandoval 表示,通过在断点中查看堆栈帧并在不被关注的函数调用时继续,可以通过他的 API 轻松实现。另一位与会者建议为内存设置观察点(watch point),Sandoval 认为这可以以类似于他提出的 set_breakpoint()调用的方式添加到 API 中。

Production

这些功能在开发中显然很有用,但 Meta 团队在一些情况下发现,能够在生产系统上使用它们将会很有帮助。例如,存在一些紧急情况,通过覆盖内核内存的某个部分足以解决问题,从而避免把开发人员从床上叫起来。可以用它来修复未正确减少的引用计数,重置统计信息溢出或下溢信息,更改无效状态等。

一个更具体的例子是 Btrfs 中的“一个令人尴尬的错误”(通过此提交修复),其中启用异步 discard 的动作并未完全处理正确。它引发了一些磁盘开始变慢的事件报告,最终追踪到根本没有发出 discard 信息(也就是没有告诉设备某些块不再使用,以便它可以对其进行垃圾回收)。经过一些“英雄式的调试”,问题被追踪到并迅速在代码树中修复,但如果能够在受影响的机器上运行一个 drgn 脚本以更改实际启用 Btrfs 挂载的 discard 动作的 bit,那将非常方便。

然而,在生产环境中执行此操作有“很多警告”。必须小心覆盖写入内容以及确定何时覆盖它们——“确实存在竞态情况”。这并不意味着它可以取代实时补丁或更新的内核,而是用来测试 fix 和应急措施。他希望这解释了为什么,因此他想转向“我们可能能够做到这一点的所有疯狂方式”。

对于开发而言,解决方案很简单,Sandoval 说,只需使用 QEMU 提供的 GDB stub 就好。他列举了生产用例的一些可能性,首先是恢复/dev/kmem,这几乎是一个“笑话”。他提到了一个LWN文章,该文章赞扬了删除 /dev/kmem ,并指出/dev/kmem 接口读取和写入内核内存是“rootkit 的一种最好工具”。Drgn 不是 rootkit,但调试器与 rootkit 共享一些元素,因此/dev/kmem 将是“支持此操作最直接的方式,但我认为没有人会接受这个方案”。

另一种选择可能是一个自定义内核模块,实际上就是/dev/kmem,他说这也没有太多改善。但是,为了启用该功能,将需要以某种方式将值写入任意地址,因此关键将在于正确设置访问控制。也许可以使用 BPF,但那“有点违背 BPF 所信仰的一切,即您的程序应该是安全的”。

另一种可能的方法是与kgdb交互,尽管 Meta 在生产内核中不启用它“但这不是完全不可以做的那种最糟糕的事情”。Kgdb 已经支持内存写入和断点,但据他所知,它从未打算被用在在运行中的实时系统上。例如,触发断点会停止每个 CPU,因此 drgn 无法继续运行;也许可以修改它改成仅停止某些 CPU,并使其中一个保持运行状态以供 drgn 使用。

与会者问为什么用户在 kgdb 触发断点时希望其他 CPU 保持运行。整个想法是其他 CPU 不能干扰断点时内核的状态。Sandoval 表示,当有另一个系统驱动调试时,这样做效果很好,但他希望能够登录到一个损坏的机器并在那里运行 drgn。另一位观众表示,如果设置了断点,drgn 可能会导致其触发,从而导致死锁。

Sandoval 承认这是一个难题。他的想法是设置一个看门狗,它会引发一个不可屏蔽的中断(NMI)以取消死锁情况下的断点。Kprobes被认为是执行断点的另一种方式,Sandoval 认为这可能可行。仍然需要一个内核模块来通知 drgn 已经触发了 kprobe 断点,并且需要一个用于防止死锁的看门狗,他说。

与会者提出内核lockdown模式可能是一个潜在的问题;它旨在限制可能更改运行中内核的任何机制,并且很可能在许多生产内核上启用。那么 Sandoval 是否考虑过 drgn 如何与或绕过锁定模式一起工作呢?Sandoval 表示,可能最好的方法是在 lockdown 的内核上禁用 drgn 支持。

在考虑访问控制时,他想要添加到 drgn 中的功能是一些已经可以从自定义内核模块中执行的事务,因此 CAP_SYS_MODULE 和 CAP_SYS_ADMIN 可能可以控制对决定的任何底层机制的访问。需要注意的是,一些组织要求使用签名过的内核模块,而不仅仅是具有这些功能。这可能意味着 drgn 机制需要以某种方式基于内核密钥环中的密钥验证用户。

Stephen Brennan 指出,Python 本身从系统上的各种位置加载了大量代码,需要以某种方式保护,以使运行 drgn 不会变成一个攻击点。Sandoval 说他“有点逃避责任,直接将其变成了每个用户的身份验证问题”,因此用户必须小心这些事情,但他表示,这种类型的访问控制多年来并不起作用,特别是 setuid 二进制文件。

Mason 表示,与其在 drgn 中拥有完整的断点,不如在到达代码时执行有限的一组事务。然后可以将其转换为 BPF 或 kprobe,然后需要将其插入内核;这不会改变安全性问题,但会简化停止所有 CPU 和防止死锁的问题。Sandoval 表示,定义集合中的一项将需要写入内存的动作,因此仍然需要解决该问题的某些解决方案。

随着时间的流逝,他总结说他仍然有“更多的问题,不急于得出答案”,但鼓励与会者随后找到他讨论“更多糟糕的想法”——或者他可以向他们展示“很酷的 drgn 功能”,他笑着说。

[I would like to thank LWN's travel sponsor, the Linux Foundation, for assistance with my travel costs to Richmond for LPC.]

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值