本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。
OOT(Out Of Tree)驱动质量存在差异,常常出现use after free内存错误以及越界访问等问题。通过对OOT驱动的内存池进行隔离,可以确保被破坏的内存不会被分配至其他模块,从而使得OOT驱动破坏的仅为其自身的内存。从而能够快速界定问题,减少排查与修复所需的时间,进而降低业务损失。
限制说明
- 操作系统限制 - Alibaba Cloud Linux 3在 - 5.10.134-17.al8及以上版本的内核中支持配置OOT驱动内存隔离功能。
- 功能限制 - 仅支持以下函数来分配KMALLOC_NORMAL类型的内存(且内存大小为8 B~8 KB)进行隔离。 - kmalloc 
- kzalloc 
- kmalloc_node 
- kzalloc_node 
- kmalloc_array 
- kmalloc_array_node 
- kcalloc 
- kcalloc_node 
 
- 数量限制 - 仅支持隔离OOT驱动的数量上限为16。 
配置OOT驱动内存隔离
您可以根据实际需要,选择以下任一方式配置OOT驱动内存隔离功能。
- 通过添加参数控制隔离。 - 在kernel cmdline中添加 - module.oot_list=<OOT模块1>,<OOT模块2>参数控制隔离。- <OOT模块1>,<OOT模块2>需替换为具体的模块名。若有多个模块,请在后面依次添加模块名称,用- ,隔开。- sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="module.oot_list=<OOT模块1>,<OOT模块2>"
- 重启服务,使配置生效。 警告- 重启实例将导致您的实例暂停运行,这可能引发业务中断和数据丢失。因此,建议您在执行此操作之前备份关键数据,并选择在非业务高峰期进行。 - sudo reboot
 
- 通过 - echo命令控制隔离。- 在 - /sys/module/module/parameters/oot_list中写入- <OOT模块1>,<OOT模块2>。- <OOT模块1>,<OOT模块2>需替换为具体的模块名。若有多个模块,请在后面依次添加模块名称,用- ,隔开。- sudo sh -c 'echo <OOT模块1>,<OOT模块2> > /sys/module/module/parameters/oot_list'
- 重新加载模块使配置生效。 - <OOT模块>需替换为具体的模块名。- rmmod <OOT模块>.ko insmod <OOT模块>.ko
 
在加载OOT驱动时,如果模块被隔离了,内核日志会输出以下信息,据此可以得到使用独立的slab(内存区域)的index。
Module <模块名> use oot-kmalloc-<index>.OOT驱动内存隔离风险处理
被OOT驱动使用的内存被视为“脏内存”。如果这些内存被其他地方使用,则存在被覆盖的风险。默认情况下,OOT驱动使用过的页将被缓存以供自身使用,而不会返还给buddy子系统。这可能导致一定数量的内存开销。若需调整这一行为,则可以通过以下方式。
- 通过调整 - module.oot_page_limit参数来控制缓存使用上限。- 查看 - <size>,指slab具体的大小(8 B~8 KB)。- sudo cat /proc/slabinfo
- 获取 - order值。- <index>和- <size>需替换为实际的值。- /sys/kernel/slab/oot-kmalloc-<index>-<size>/order
- 控制缓存使用上限。 - <limit>替换为要写入的缓存,单位:- page。例如写入 - limit为100,- order为2,内存使用上限为- page。 - sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="module.oot_page_limit=<limit>"
 
- 通过 - /sys/module/module/parameters/oot_page_limit来控制缓存使用的上限。- 查看 - <size>,指slab具体的大小(8 B~8 KB)。- sudo cat /proc/slabinfo
- 获取 - order值。- <index>和- <size>需替换为实际的值。- /sys/kernel/slab/oot-kmalloc-<index>-<size>/order
- 控制缓存使用上限。 - <limit>替换为要写入的缓存,单位:- page。例如写入 - limit为100,- order为2,内存使用上限为- page。 - sudo sh -c 'echo <limit> > /sys/module/module/parameters/oot_page_limit'
 
- 通过 - /sys/kernel/slab/oot-kmalloc-<index>-<size>/oot_page查看特定slab缓存的单位页。- 查看 - <size>,指slab具体的大小(8 B~8 KB)。- sudo cat /proc/slabinfo
- 查看特定slab缓存的单位页。 - <index>和- <size>需替换为实际的值。- /sys/kernel/slab/oot-kmalloc-<index>-<size>/oot_page
 
定向开启KFENCE
OOT驱动使用独立的slab,因此可以开启定向的KFENCE来捕捉内存越界读写的情况。具体操作,请参考捕获内核的内存污染问题(KFENCE)。
使用以下命令开启KFENCE监控独立的slab。
#关闭所有slab类型的监视
for file in /sys/kernel/slab/*
do
    sudo sh -c 'echo 0 > $file/kfence_enable'
done
#关闭order0_page的监视
sudo sh -c 'echo 0 > /sys/module/kfence/parameters/order0_page'
#打开需要的slab类型的监视
sudo sh -c 'echo 1 > /sys/kernel/slab/oot-kmalloc-<index>-<size>/kfence_enable'常见问题
为什么会出现no symbol version for __kmalloc/__kmalloc_node/kmalloc_caches报错信息?
该报错因为无侵入改动,故编译OOT驱动时无法获取是否被隔离,因此不会在模块的信息里加上oot相关符号的crc校验值,无法对符号进行校验,这个问题暂无规避方案,基于__kmalloc的相关内核API较稳定,因此该问题风险不大,出现时请忽略即可。
- 问题现象 - 第一次加载要隔离的OOT模块时,会出现 - no symbol version for __kmalloc/__kmalloc_node/kmalloc_caches报错信息,该问题只会出现一次,属于正常现象。
- 可能原因 - Linux内核在加载模块的时候会对模块的 - UND类型的符号进行重定位并校验- crc值来确保兼容性,而OOT驱动隔离的原理是在对__kmalloc等符号重定位时,把符号定位到给OOT驱动专用的oot___kmalloc等符号,因此,在编译OOT驱动时,OOT驱动代码里使用的是__kmalloc,导致UND符号里包含的是__kmalloc,但实际重定位到的是oot___kmalloc,两者不一致导致出现该问题。