Linux内核编译时是否启用了UBSAN?探讨内核中的未定义行为检测技术

本文探讨了Linux内核是否在编译时启用了UBSAN(未定义行为地址清理器)技术,并解释了为何Ubuntu及其衍生发行版(如Linux Mint)的内核会在系统日志中报告UBSAN检测到的数组越界等未定义行为错误。

Linux内核编译时是否启用了UBSAN?

问题背景:UBSAN(Undefined Behavior Address Sanitizer,未定义行为地址清理器)是一种在编译代码中插入大量检查以检测错误(如越界访问和各种其他不应在代码中发生的操作)的工具。尽管开发者在调试时会使用UBSAN来捕获错误,但通常不会在发布版本中启用它,因为这会带来显著的性能开销。

然而,用户Zebrafish在Linux Mint 22.2 Cinnamon(内核版本6.8.0-87-generic)的系统崩溃日志中发现了UBSAN相关的错误报告,这引发了一个疑问:Linux内核的发布版本是否真的编译时启用了UBSAN?

崩溃日志摘录

以下是用户从journalctl中提取的部分崩溃日志,显示了由UBSAN检测到的数组越界错误:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Nov 27 09:38:17 kernel: UBSAN: array-index-out-of-bounds in /var/lib/dkms/8812au/5.6.4.2_35491.20191025/build/core/rtw_wlan_util.c:1905:48
Nov 27 09:38:17 kernel: index 1 is out of range for type 'u8 [1]'
Nov 27 09:38:17 kernel: UBSAN: array-index-out-of-bounds in /var/lib/dkms/8812au/5.6.4.2_35491.20191025/build/core/rtw_wlan_util.c:1910:75
Nov 27 09:38:17 kernel: index 2 is out of range for type 'u8 [1]'
Nov 27 09:38:17 kernel: UBSAN: array-index-out-of-bounds in /var/lib/dkms/8812au/5.6.4.2_35491.20191025/build/core/rtw_wlan_util.c:1916:76
Nov 27 09:38:17 kernel: index 2 is out of range for type 'u8 [1]'
Nov 27 09:38:17 kernel: UBSAN: array-index-out-of-bounds in /var/lib/dkms/8812au/5.6.4.2_35491.20191025/build/os_dep/linux/ioctl_cfg80211.c:1682:110
Nov 27 09:38:17 kernel: index 16 is out of range for type 'u8 [*]'
Nov 27 09:38:17 kernel: UBSAN: array-index-out-of-bounds in /var/lib/dkms/8812au/5.6.4.2_35491.20191025/build/os_dep/linux/ioctl_cfg80211.c:1683:110
Nov 27 09:38:17 kernel: index 24 is out of range for type 'u8 [*]'

这些错误指向了一个名为8812au的第三方内核模块(DKMS构建的无线网卡驱动)。用户原本认为UBSAN的检测报告只会出现在为调试目的而编译的代码中,因此对在稳定版内核中看到这些信息感到困惑。

技术解答

用户Stephen Kitt在回答中明确指出:是的,Ubuntu内核(Linux Mint基于此)在构建时启用了UBSAN。

验证方法很简单,可以在终端中运行以下命令来检查当前运行内核的配置:

1
grep UBSAN /boot/config-$(uname -r)

具体来说,生成上述错误消息所涉及的内核配置选项是CONFIG_UBSANCONFIG_UBSAN_BOUNDS

性能与实用性的讨论

用户Zebrafish在评论中表达了惊讶和担忧,认为启用UBSAN会带来巨大的性能开销,因此通常只应在开发和调试阶段使用。

对此,Stephen Kitt引用了一个内核开发者的请求作为佐证,表明内核维护者认为其开销是可接受的。Fedora的内核同样启用了UBSAN。

另一位用户Peter Cordes补充了技术细节:内核可能并未启用完整的-fsanitize=undefined(这确实会产生大量额外的机器码,例如检查所有有符号整数运算的溢出),而可能只启用了其子集,例如边界检查(bounds checking)。这种选择性的启用能在安全性和性能之间取得平衡。

用户TooTea也解释道:与ASan(Address Sanitizer)这类为每次内存访问都插入复杂检测的工具不同,UBSAN实际上非常轻量,它只是在关键位置(如整数溢出、无效值、边界检查)插入额外的条件检查。而且,UBSAN生成的这类检查在内核中本就随处可见(算术运算需要防溢出、值必须验证有效性、边界必须始终检查等)。因此,区别仅在于这些检查是由编译器自动生成还是由开发者手动编写。

结论

  1. 主流发行版的内核确实启用了UBSAN:为了增强内核的健壮性并主动捕获未定义行为,Ubuntu、Fedora等发行版的官方内核在编译时启用了UBSAN的部分功能,特别是边界检查。
  2. 性能开销可控:与普遍认知不同,UBSAN(尤其是选择性启用时)的性能开销被认为在可接受的范围内,其价值在于能够在内核运行时检测到驱动或模块中的潜在严重错误。
  3. 错误来源的定位:在用户案例中,UBSAN报告的错误并非来自主线内核本身,而是来自一个第三方构建的DKMS内核模块(8812au无线网卡驱动)。这说明了UBSAN在捕捉外部模块缺陷方面的作用。

这一实践反映了现代Linux内核开发对安全性和稳定性的持续追求,即使是在发布版本中,也愿意引入适当的运行时检测机制来提前发现问题。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计