关注了就能看到更多这么棒的文章哦~
New kernel tools: wprobes, KStackWatch, and KFuzzTest
By Jonathan Corbet
September 15, 2025
Gemini flash translation
https://lwn.net/Articles/1037390/
内核在一个特殊的环境中运行,这使得用户空间(user-space)开发者可用的许多开发工具难以直接用于内核开发。内核开发者通常对此的回应是索性不用这些工具,但事实上,他们和其他开发者一样需要良好的工具。最近,三个用于追踪 Bug(错误)的新工具已在 linux-kernel 邮件列表中发布;以下是它们的概述。
wprobes
用户空间调试器中长期存在的一个实用功能是观察点(watchpoints)——即请求在内存中特定位置被访问时触发一个陷阱(trap)的能力。观察点可以用于找出哪段代码导致了指定的变量被破坏的问题。这项功能在内核环境中可能特别有用,因为内核中同时发生着许多事情,而问题的根源可能存在于一个庞大而复杂的系统中的任何位置。但内核开发者不得不放弃使用观察点,尤其是在非虚拟化环境下工作时。
内核所拥有的功能是 kprobe(内核探测点),它允许在运行中的内核中(几乎)任何位置放置调试代码。它们有点类似于调试器插入的动态断点(dynamic breakpoint),但它们并不会真正停止内核的执行;相反,它们会打印一些(希望有用的)信息然后继续运行。
Masami Hiramatsu 的这个补丁系列向 kprobe 子系统添加了一个名为 "wprobe(观察点探测)" 的新功能。wprobe 类似于用户空间观察点,因为它会在访问给定内存范围时触发陷阱;然而,与 kprobe 类似,wprobe 并不会真正停止执行。但幸运的是,它们可以被配置为打印足够多的信息,以帮助精确找到问题的根源。
观察点是通过向 `/sys/kernel/tracing/dynamic_events` 写入一个神秘的字符串来设置的,其格式如下:
w:[GRP/][EVENT][r|w|rw]@<ADDRESS|SYMBOL[+OFFS]>[FETCHARGS]举个例子(补丁系列附带的说明中提供),这组命令将创建一个观察点,当内核的 jiffies 变量被修改时触发:
# cd /sys/kernel/tracing# echo 'w:my_jiffies w@jiffies:8 value=+0($addr)' >> dynamic_events# echo 1 > events/wprobes/my_jiffies/enable中间一行,`w:my_jiffies` 设置了一个名为 my_jiffies 的观察点;`w@jiffies:8` 目标是写入八字节的 jiffies 变量,而 `value=+0($addr)` 则打印写入该地址的值。最后一行激活了观察点。
还有一个机制可以在特定事件发生时动态创建观察点;它支持在动态创建的内存对象(例如 slab 分配)的整个生命周期中进行观察。有关示例,请参阅上面链接的补丁说明;有关描述 wprobe 的文档更新,请参阅这个补丁;有关动态 wprobe 创建的文档,请参阅这个补丁。
KStackWatch
无论在何种上下文中,调用栈(call stack)的损坏对开发者来说都可能是一个特别有害的问题。内核可以使用 stack canaries 和其他工具来检测栈溢出(stack overflow)的发生,但它们无法精确指出问题发生的时间。Jinchao Wang 的 KStackWatch 补丁系列旨在通过提供一个轻量级工具来填补这一空白,该工具可以捕获栈损坏事件。
本质上,KStackWatch 会在给定函数处于活动状态时,监控其栈帧(stack frame)的特定部分。与 wprobe 类似,它会在对感兴趣内存的访问上设置一个陷阱,并在内存被修改时打印有用的信息。内核的追踪基础设施(tracing infrastructure)被用来在进入感兴趣函数时启用此观察点,并在返回时禁用它。
该工具添加了一个新的控制文件 `/proc/kstackwatch`,这与通常将此类文件放在 debugfs 或 tracefs 中的做法相悖。管理员可以通过向该文件写入以下格式的字符串来启用该工具:
function+ip_offset[+depth][local_var_offset:local_var_len]这里,function 是感兴趣函数的名称,ip_offset 指示了探测点应放置在函数内的何处。目前尚不完全清楚为何需要该偏移量,以及用户如何确定其值。depth 值可用于递归函数;它指定了应监控的递归级别。local_var_offset 和 local_var_len 参数指示了要监控的栈上特定变量的位置和大小,表示为从进入函数时栈指针(stack pointer)的偏移量。如果未提供这些值,KStackWatch 将转而监控 stack canaries。
当前的栈观察在整个系统范围内是全局的,任何给定时间只能有一个处于活动状态。向 `/proc/kstackwatch` 写入新配置将移除之前的观察;写入空字符串将完全禁用该工具。
当检测到对被观察变量的写入时,KStackWatch 将向系统日志输出一些诊断信息。有一个模块参数 panic_on_catch,它会在检测到写入时立即导致系统恐慌(panic)。目前似乎没有办法在不卸载和重新加载模块的情况下更改该参数。
截至本文撰写之时,该系列已是第四个修订版本;值得庆幸的是,这个修订版本包含了一个描述 KStackWatch 的基本文档文件。wprobe 补丁集之间有一些有趣的交互,因为它们互相借鉴了有用的代码;两者似乎都正在接近就绪状态。
KFuzzTest
对内核进行模糊测试(fuzz testing)并非什么新鲜事;syzkaller 等工具在过去十年中一直揭露着内核 Bug(错误)。然而,这种测试都是从用户空间运行的,因此它能锻炼的代码受限于内核的用户空间 API。Ethan Graham 提出的 KFuzzTest(内核模糊测试子系统)子系统旨在为模糊测试器提供一种深入内核并直接滥用底层函数接口的方法。
为了安排一个内核函数由 KFuzzTest 进行压力测试,开发者必须在内核内部设置一些脚手架(scaffolding),很可能在目标函数所在的源文件中。第一步是定义一个结构体,用于封装该函数的参数。如果我们要测试以下内部内核函数:
int mangle_data(constu8*data,size_tlen){/* do something interesting */}我们将从这个定义开始:

一旦完成,就可以定义测试本身了。这通过 FUZZ_TEST() 宏完成:

这个声明定义了一个名为 test_mangle_data 的测试。在声明的主体中,变量 arg 将指向一个 mangle_data_input 结构体,其中包含要传递给函数的参数;对被测试函数的调用在此声明的末尾进行。用户空间将能够按照下文所述调用此测试,提供任何可能暴露 Bug 的“邪恶”数据。值得注意的是,KFuzzTest 本身不会检测这些 Bug;它只是用提供的数据运行函数。因此,KFuzzTest 通常会与 KASAN (Kernel Address Sanitizer,内核地址清理器) 等工具一起运行,以检测何时出现问题。
测试可以包含对传入数据的约束。例如,如果 test_mangle_data 的定义包含以下行:
KFUZZTEST_EXPECT_NOT_NULL(mangle_data_input, data);任何输入值都将被测试,以确保 data 指针非空。如果该条件不满足,测试本身将不会运行。有一整套 KFUZZTEST_EXPECT_ 宏可用于约束函数的输入数据;它们可以在这个补丁中找到。
测试定义还可以包含注释(annotations),提供有关函数输入数据的进一步信息。例如:
KFUZZTEST_ANNOTATE_LEN(mangle_data_input, len, data);文档说明 len 旨在作为 data 数组的长度。其他可能的注释表示给定参数是一个数组,或者它预期包含一个 C 字符串。注释不影响测试本身的运行。约束和注释被编译到内核可执行文件的一个特殊部分,用户空间工具可以在其中找到它们。
在用户空间一侧,KFuzzTest 设置了一个 debugfs 目录(称为 fuzztest),其中包含每个已定义测试的子目录。用户空间工具可以使用此目录来发现可用的测试,但它仍然必须读取内核镜像文件以获取约束和注释信息。此外,还需要使用 DWARF 调试信息(DWARF debugging information)构建内核,以提供有关结构体布局的信息;目前尚不清楚为何不为此目的使用通常存在且更紧凑的内核 BTF 信息(BTF information)。
运行测试是将一些数据写入测试目录中的 input 文件;对于上面的示例,该文件将是 `…/kfuzztest/test_mangle_data/input`。然而,该数据的格式并不简单。函数的输入可能包含指向一组复杂的、由指针连接的数据结构;KFuzzTest 允许测试工具将整套数据作为输入提供。为此,用户空间必须将数据序列化为 KFuzzTest 期望的格式,然后将结果写入 input 文件。
该补丁系列包含一个工具(kfuzztest-bridge),可用于使用随机数据运行测试;它的输入也比较复杂。有关所有这些工作原理的详细信息,请参阅此文档补丁,以及关于几个示例测试的此补丁。这项工作仍处于 RFC 阶段,但似乎有一定程度的兴趣,因此它很可能在某个时候脱离该阶段。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~



933

被折叠的 条评论
为什么被折叠?



