文档

捕获内核的内存污染问题(KFENCE)

更新时间:

Alibaba Cloud Linux 3在内核版本5.10.84-10(x86架构)和5.10.134-16(ARM架构)开始支持KFENCE功能。本文为您介绍KFENCE的功能和使用方法等。

功能介绍

KFENCE(Kernel Electric-Fence)是Linux内核内置的、可在线上环境开启的工具,用于捕获内核及内核模块的内存污染问题,在上游Linux内核社区5.12版本中引入。KFENCE的目标是通过在内存边界附近插入特殊的标记(fence),来检测对已释放或未分配内存的访问。当发生内存污染问题时,KFENCE会检测到并触发错误报告,并提供有关问题的详细信息。关于KFENCE的更多信息,请参见KFENCE文档龙蜥社区

阿里云在Alibaba Cloud Linux 3中对KFENCE功能进行了增强,能够灵活动态开关KFENCE和全量捕获内存污染问题,从而兼顾线上探测和线下调试。

说明

如果您是一名内核或内核模块的开发者,可以用此工具来检查您开发的内核或内核模块是否存在内存污染问题。如果您是一名普通用户但遇到了内核崩溃的问题,也可以打开KFENCE帮助我们或第三方驱动模块的开发者收集更多信息。

开启KFENCE

在以下业务场景中经常会用到开启KFENCE功能,具体说明如下:

线上探测场景

场景1:使用KFENCE探测内存是否存在污染

说明

该场景占用2 MiB内存,基本不会影响性能。

运行以下命令,开启KFENCE(添加boot commandline参数的方式)。

sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.sample_interval=100"

该场景下,系统在下次重启时,配置会自动生效。

场景2:使用KFENCE捕获内存污染问题

重要

该场景将消耗以GiB为单位的大量内存,小规格机器慎用。

  1. 新建一个内存分配脚本,并添加如下内容(以脚本名称kfence.sh,监控的目标slab类型是kmalloc-64为例)。

    #!/bin/bash
    # usage: ./kfence.sh kmalloc-64
    
    SLAB_PREFIX=/sys/kernel/slab
    MODULE_PREFIX=/sys/module/kfence/parameters
    
    if [ $# -eq 0 ]; then
    	echo "err: please input slabs"
    	exit 1
    fi
    
    #check whether slab exists
    for i in $@; do
    	slab_path=$SLAB_PREFIX/$i
    	if [ ! -d $slab_path ]; then
    		echo "err: slab $i not exist!"
    		exit 1
    	fi
    done
    
    #calculate num_objects
    sumobj=0
    for i in $@; do
    	objects=($(cat $SLAB_PREFIX/$i/objects))
    	maxobj=1
    	for ((j=1; j<${#objects[@]}; j++)); do
    		nodeobj=$(echo ${objects[$j]} | awk -F= '{print $2}')
    		[ $maxobj -lt $nodeobj ] && maxobj=$nodeobj
    	done
    	((sumobj += maxobj))
    done
    echo "recommend num_objects per node: $sumobj"
    
    #check kfence stats
    if [ $(cat $MODULE_PREFIX/sample_interval) -ne 0 ]; then
    	echo "kfence is running, disable it and wait..."
    	echo 0 > $MODULE_PREFIX/sample_interval
    	sleep 1
    fi
    
    #disable all slabs catching
    for file in $SLAB_PREFIX/*
    do
    	echo 0 > $file/kfence_enable
    done
    
    #disable order0 page catching
    echo 0 > $MODULE_PREFIX/order0_page
    
    #enable setting slabs catching
    for i in $@; do
    	echo 1 > $SLAB_PREFIX/$i/kfence_enable
    done
    
    #setting num_objects and node mode
    echo $sumobj > $MODULE_PREFIX/num_objects
    echo node > $MODULE_PREFIX/pool_mode
    
    #start kfence
    echo -1 > $MODULE_PREFIX/sample_interval
    if [ $? -ne 0 ]; then
    	echo "err: kfence enable fail!"
    	exit 1
    fi
    echo "kfence enabled!"

    该脚本将探测目标slab的活跃对象数量,并根据该数量估算出合适的KFENCE池子大小,然后启用KFENCE以捕获所有目标slab的分配。

    说明

    slab是内存管理中常用的概念和技术,用于优化内存的分配和释放操作,提高系统的性能和效率。KFENCE支持监控slab以及order 0单页。更多信息,请参见基本概念

  2. 运行以下命令,执行脚本,开始探测。

    sudo bash ./kfence.sh kmalloc-64

线下调试场景

通过添加参数开启KFENCE(x86架构

  1. 运行以下命令,开启KFENCE。

    sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.num_objects=1000000"
    sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.sample_interval=-1"
    sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.fault=panic"
    • num_objects:决定了KFENCE池子的大小,其占用内存为(num_objects+1)* 8 KiB,一般建议该值配置为最大可用内存的10%。例如设置num_objects为1,000,000时,占用内存为(1,000,000+1)* 8 KiB,计算结果向上取整为8 GiB。

    • sample_interval:取值包含以下三种情况。

      • 0:表示关闭KFENCE功能。

      • 正数:采样间隔(ms),例如设置为100时,表示每隔100 ms分配的内存将进入KFENCE的监控范围内。

      • 负数:全量模式,所有符合(slab类型筛选)条件的内存均将进入KFENCE的监控范围内。

    • fault:自内核版本5.10.134-16开始新增的参数,默认是report。当设置为panic时,会在捕获问题的现场宕机,以保留第一现场的内核转储文件。

  2. 重启系统使配置生效。

    具体操作,请参见重启实例

通过配置脚本开启KFENCE(x86/ARM架构

说明
  • 通过该方式开启KFENCE时,无法捕获内核启动过程中可能出现的内存污染问题。

  • 开启KFENCE后,如果需要修改num_objectssample_interval配置,需先关闭KFENCE再进行修改。

运行以下命令,开启KFENCE。

sudo sh -c 'echo 1000000 > /sys/module/kfence/parameters/num_objects'
sudo sh -c 'echo -1 > /sys/module/kfence/parameters/sample_interval'
sudo sh -c 'echo panic > /sys/module/kfence/parameters/fault'
  • num_objects:决定了KFENCE池子的大小,其占用内存为(num_objects+1)* 8 KiB,一般建议该值配置为最大可用内存的10%。例如设置num_objects为1,000,000时,占用内存为(1,000,000+1)* 8 KiB,计算结果向上取整为8 GiB。

  • sample_interval:取值包含以下三种情况。

    • 0:表示关闭KFENCE功能。

    • 正数:采样间隔(ms),例如设置为100时,表示每隔100 ms分配的内存将进入KFENCE的监控范围内。

    • 负数:全量模式,所有符合(slab类型筛选)条件的内存均将进入KFENCE的监控范围内。

  • fault:自内核版本5.10.134-16开始新增的参数,默认是report。当设置为panic时,会在捕获问题的现场宕机,以保留第一现场的内核转储文件。

    说明

    如果您的内核是5.10.134-16之前的版本,执行该条命令会报错,您直接忽略即可,不影响开启KFENCE。

查看结果

KFENCE捕获到内存污染问题后,您可以查看捕获到的问题个数以及详细的错误信息。

  • 如下图所示,运行sudo cat /sys/kernel/debug/kfence/stats命令,查看到的total bugs计数会增加。

    image.png
  • 系统会将信息打印在dmesg中,您可以运行dmesg | grep -i kfence命令查看与KFENCE相关的错误日志信息。如下图所示,查询到1条错误信息。

    image.png

关闭KFENCE

运行以下命令,关闭KFENCE。

sudo bash -c 'echo 0 > /sys/module/kfence/parameters/sample_interval'

关闭KFENCE功能后,KFENCE不再捕获任何内存分配。当池子内监控的所有内存均释放后,KFENCE将以1 GiB单位为粒度向内核伙伴系统返还内存。

对于通过添加boot commandline开启KFENCE的场景,您可以运行以下命令移除这些参数,下次系统重启将不再自动打开KFENCE。

sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --remove-args="kfence.sample_interval"

FAQ

  • KFENCE功能对内存和性能有什么影响?

    • 对内存的影响

      KFENCE采用以大量内存开销换取较小的性能干扰的思路,占用的内存较高。对于重启开启的采样模式(上游Linux同款)可设定任意较小的num_objects来节约内存。其他情况(全量模式及动态开启)则需消耗GiB级别的内存,所以小规格机器慎用。

    • 对性能的影响

      • 采样模式下,对性能影响较小。

      • 全量模式如果筛选得当(例如只打开某种类型的slab监控),一般来说对性能的影响也可以接受。

      说明
      • 建议您根据实际的业务场景先进行灰度测试,观察开启KFENCE后对实际业务性能的影响,再决定后续部署情况。

      • 在线下调试场景中,如果采用全量模式以及全种类监控,性能和内存影响都很大,但是这种场景一般是为了锁定问题,无需关注性能影响。

  • KFENCE功能与KASAN功能有什么区别?

    KFENCE与KASAN都是Linux内核自带的用于捕获内存污染的工具。阿里云在5.10内核中对KFENCE功能进行了增强,除了开关更加灵活、支持采样从而可以在线上的业务环境运行之外,它们在功能上还有以下区别:

    • KFENCE支持监控大小不超过4 KiB的slab(例如kmalloc-4k)以及order 0单页。而KASAN支持监控的内存种类更多,包括所有的slab、page、堆栈和全局内存等。

    • KFENCE对于监控范围内的内存异常行为捕获成功率高于KASAN。

    • KFENCE在内存开销方面高于KASAN,但通常情况下对业务的性能的影响小于KASAN。

    通常情况下,不建议同时使用KFENCE和KASAN功能,KFENCE会接管KASAN的监控目标。

  • KFENCE功能的稳定性怎么样?

    在内核版本5.10.134-15及其之前的版本存在一个已知问题:order 0单页和slab混合监控时,在特定场景下可能造成宕机。您可以运行以下命令,禁用order 0单页监控来预防此问题。

    sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.order0_page=0"

基本概念

KFENCE功能中涉及的基本概念说明如下。

名词

说明

内存污染

指在程序运行过程中,内存区域被错误地修改或破坏,导致程序行为异常或崩溃的问题。内存污染可能是由于编程错误、软件漏洞、恶意软件或硬件故障等原因引起的。

slab

slab是Linux内核中一种高效的内存分配机制。它通过预先分配一定数量的内存对象,组织成一个内存缓存池,用于快速分配和释放内存。slab可以避免频繁的内存分配和释放操作,提高内存分配的效率。

order 0单页

order 0单页也是Linux内核中一种内存分配机制,内存被分割成固定大小的页框(page frame),一般为4 KiB。order 0的单页指的就是一个普通的4 KiB大小的内存页框,它是内存分配的基本单位。当应用程序或内核需要分配一小块内存时,通常会以order 0的方式进行分配。

  • 本页导读 (1)
文档反馈