本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。
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,两者不一致导致出现该问题。