OOT驱动内存隔离使用说明

重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

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驱动内存隔离功能。

  • 通过添加参数控制隔离。

    1. 在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>"
    2. 重启服务,使配置生效。

      警告

      重启实例将导致您的实例暂停运行,这可能引发业务中断和数据丢失。因此,建议您在执行此操作之前备份关键数据,并选择在非业务高峰期进行。

      sudo reboot
  • 通过echo命令控制隔离。

    1. /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'
    2. 重新加载模块使配置生效。

      <OOT模块>需替换为具体的模块名。

      rmmod <OOT模块>.ko
      insmod <OOT模块>.ko
说明

在加载OOT驱动时,如果模块被隔离了,内核日志会输出以下信息,据此可以得到使用独立的slab(内存区域)的index

Module <模块名> use oot-kmalloc-<index>.

OOT驱动内存隔离风险处理

被OOT驱动使用的内存被视为“脏内存”。如果这些内存被其他地方使用,则存在被覆盖的风险。默认情况下,OOT驱动使用过的页将被缓存以供自身使用,而不会返还给buddy子系统。这可能导致一定数量的内存开销。若需调整这一行为,则可以通过以下方式。

  • 通过调整module.oot_page_limit参数来控制缓存使用上限。

    1. 查看<size>,指slab具体的大小(8 B~8 KB)。

      sudo cat /proc/slabinfo
    2. 获取order值。

      <index><size>需替换为实际的值。

      /sys/kernel/slab/oot-kmalloc-<index>-<size>/order
    3. 控制缓存使用上限。

      <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来控制缓存使用的上限。

    1. 查看<size>,指slab具体的大小(8 B~8 KB)。

      sudo cat /proc/slabinfo
    2. 获取order值。

      <index><size>需替换为实际的值。

      /sys/kernel/slab/oot-kmalloc-<index>-<size>/order
    3. 控制缓存使用上限。

      <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缓存的单位页。

    1. 查看<size>,指slab具体的大小(8 B~8 KB)。

      sudo cat /proc/slabinfo
    2. 查看特定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,两者不一致导致出现该问题。