すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Linux:KFENCEを使用したカーネルメモリ汚染の検出

最終更新日:Nov 01, 2024

Kernel Electric-Fence (KFENCE) は、オンライン環境で有効にできる組み込みのLinuxカーネルツールです。 KFENCEは、カーネルモジュールとカーネルモジュールのメモリ汚染問題を検出します。 KFENCEがメモリ汚染の問題を検出すると、KFENCEは問題の詳細を含むエラーメッセージを生成します。 Alibaba Cloud Linux 3で強化されたKFENCE。 KFENCEを柔軟かつ動的に有効または無効にし、KFENCEを使用してメモリ汚染の問題を包括的に検出し、オンライン検出とオフラインデバッグの要件を満たすことができます。

オペレーティングシステムの制限

  • x86アーキテクチャ

    カーネルバージョンが5.10.84-10以降のAlibaba Cloud Linux 3

  • アームアーキテクチャ

    カーネルバージョンが5.10.134-16以降のAlibaba Cloud Linux 3

説明
  • カーネルモジュールまたはカーネルモジュールの開発者であれば、KFENCEを使用して、カーネルモジュールまたはカーネルモジュールでメモリ汚染が発生しているかどうかを確認できます。

  • 通常のユーザーでカーネルクラッシュが発生した場合、KFENCEを使用して、Alibaba Cloudまたはサードパーティのドライバー開発者がカーネルクラッシュに関する詳細情報を収集できるようにすることができます。

用語

期間

説明

メモリ汚染

プログラムの実行中にメモリ領域が誤って変更または破損し、プログラムで例外またはクラッシュが発生するという問題。 メモリ汚染は、プログラミングエラー、ソフトウェアの脆弱性、マルウェア、またはハードウェア障害によって引き起こされる可能性があります。

スラブ

スラブ割り当ては、Linuxカーネルの効率的なメモリ割り当てメカニズムです。 カーネルは、スラブを使用して、メモリキャッシュプール内の特定の数のメモリオブジェクトを事前に割り当てて、メモリの割り当てと解放を迅速に行います。 スラブを使用して、頻繁なメモリ割り当てとリリース操作を防ぎ、メモリ割り当ての効率を向上させることができます。

注文-0ページ

Order-0ページ割り当ては、Linuxカーネルのメモリ割り当てメカニズムです。 メモリは、ページフレームと呼ばれる固定サイズのブロックに分割される。 ほとんどの場合、オーダー0ページフレームのサイズは4 KiBである。 オーダー0ページは、4 − KiBページフレームであり、メモリ割り当ての基本単位である。 アプリケーションまたはカーネルがメモリの小さなブロックを必要とする場合、メモリは0次ページによって割り当てられます。

KFENCEの有効化

KFENCEは、次のビジネスシナリオで使用されます。

オンライン検出シナリオ

シナリオ1: KFENCEを使用してメモリ汚染の問題が発生したかどうかを検出する

説明

次の例では、KFENCEは最大2 MiBのメモリを消費し、パフォーマンスには影響しません。

  • kfence.sample_intervalパラメーターを追加してKFENCEを有効にします。

    <kfence.sample_interval> を指定する値に置き換えます。 たとえば、値が100の場合、次回のシステム起動時にKFENCEデバッグツールが自動的に有効になり、サンプリング間隔が100イベントに設定されることを指定します。

    sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.sample_interval=<kfence.sample_interval>"
  • kfence.booting_maxパラメーターを追加して、メモリ仕様に基づいてKFENCEが消費できるメモリの最大量を制限します。

    説明
    • カーネルバージョン5.10.134-17以降では、デフォルト設定kfence.booting_max=0-2G:0,2G-32G:2M,32G-:32Mboot commandlineパラメーターリストに追加されます。 上記の既定の設定は、num_objectsパラメーターの既定値 (255) と組み合わせて使用し、KFENCEのメモリオーバーヘッドがすべてのメモリ仕様の合計メモリの1 ‰ を超えないようにします。 上記のデフォルトの設定と値を使用すると、KFENCEは、標準の4-KiBメモリページを使用する場合は最大2 MiBのメモリを消費し、64-KiBの巨大ページを使用する場合は最大32 MiBのメモリを消費します。

    • kfence.booting_maxパラメーターは、KFENCEが消費できるメモリの最大量のみを制限します。 このパラメータはnum_objectsパラメータの制約であり、実際のメモリオーバーヘッドを表すものではありません。 実際のメモリオーバーヘッドは、kfence.booting_maxパラメーターの値以下です。

    <kfence.booting_max> を、指定する値 (0-128M:0,128M-256M:1M、256M-:2Mなど) に置き換えます。サンプル値のセグメントの説明:

    • 0-128 M:0: 使用するマシンの合計メモリのサイズが128 MiB未満の場合、KFENCEは無効になります。

    • 128 M-256 M:1 M: 使用するマシンの合計メモリのサイズが128 MiB以上256 MiB以下の場合、KFENCEは最大1 MiBのメモリを消費できます。 num_objectsパラメーターの値は127を超えることはできません。

    • 256 M-:2 M: 使用するマシンの合計メモリのサイズが256 MiBより大きい場合、KFENCEは最大2 MiBのメモリを消費する可能性があります。 num_objectsパラメーターの値は255を超えることはできません。

    sudo grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="kfence.booting_max=<kfence.booting_max>"

    上記の構成は、システムの起動時にパラメーターをboot commandlineパラメーターリストに追加することでKFENCEを起動するシナリオにのみ適用されます。 システム起動後にKFENCEを設定するシナリオ2では、設定は有効になりません。

この設定は、次回システムの起動時に自動的に有効になります。

シナリオ2: KFENCEを使用してメモリ汚染の問題が発生したかどうかを検出する

重要

このシナリオでは、GiBレベルで大量のメモリが消費されます。 小さなメモリマシンを使用するときは注意してください。

  1. メモリ割り当てスクリプトを作成し、次の内容を追加します。 次の例では、スクリプトの名前はkfence.shで、監視対象のスラブタイプは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) 2>/dev/null || echo 1 > $file/skip_kfence
    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) 2>/dev/null || echo 0 > $SLAB_PREFIX/$i/skip_kfence
    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!"

    スクリプトは、スラブのアクティブなオブジェクトの数を検出し、その数に基づいて適切なKFENCEプールサイズを推定し、その後、KFENCEがすべてのスラブのメモリ割り当てに関する情報を取得できるようにするために使用されます。

    説明

    スラブは、メモリの割り当てと解放操作を最適化するためにメモリ管理で一般的に使用されます。 これにより、システムのパフォーマンスと効率が向上します。 KFENCEはスラブを監視し、0ページを注文できます。

  2. 次のコマンドを実行して、スクリプトを実行し、メモリ汚染検出を開始します。

    sudo bash ./kfence.sh kmalloc-64

オフラインデバッグシナリオ

x86アーキテクチャのパラメーターを指定してKFENCEを有効にする

  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プールのサイズ。KFENCEが監視できるスラブオブジェクトの最大数です。

      • num_objectsパラメーターの値が131071以下の場合、KFENCEが消費できる最大メモリ量は、(num_objects + 1) × 8 KiBの式で計算されます。

      • num_objectsパラメーターの値が131071より大きい場合、KFENCEが消費できる最大メモリ量は、次の式を使用して計算されます。⌈ num_objects/131071 ⌉ GiB。 ⌈⌉ 記号は、計算結果を最も近い整数に切り上げることを指定します。

        説明

        num_objectsパラメーターは、使用可能な最大メモリの10% に設定することを推奨します。 たとえば、パラメーターnum_objectsを1,000,000に設定すると、KFENCEは最大8 GiBのメモリを消費できます。これは、次の式を使用して計算されます。⌈ 1,000,000 131071 GiB = 8 GiB。

    • sample_interval: メモリが監視される間隔。 有効な値:

      • 0: KFENCEは無効で、メモリを監視しません。

      • 正の数: ミリ秒単位のサンプリング間隔。 たとえば、値100は、KFENCEが100ミリ秒ごとに割り当てられたメモリを監視することを指定します。

      • 負の番号: フルモード。 KFENCEは、特定のスラブタイプなど、特定の条件を満たすすべてのメモリを監視します。

    • fault: このパラメーターはカーネルバージョン5.10.134-16で導入されています。 デフォルト値: report。 faultパラメーターをpanicに設定すると、問題が検出されたインスタンスでダウンタイムが発生し、問題発生時に生成されたコアダンプファイルが保持されます。

  2. オペレーティングシステムを再起動して、設定を有効にします。

    詳細は、「インスタンスの再起動」 をご参照ください。

スクリプトを使用してKFENCEを有効にするx86またはArmアーキテクチャ

説明
  • スクリプトを実行してKFENCEを有効にすると、KFENCEはカーネルの起動中に発生する可能性のあるメモリ汚染の問題を検出できません。

  • KFENCEを有効にした後にnum_objectsまたはsample_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プールのサイズ。KFENCEが監視できるスラブオブジェクトの最大数です。 KFENCEが消費できるメモリの最大量は、次の式を使用して計算されます。⌈ num_objects/131071 ⌉ GiB。 ⌈⌉ 記号は、計算結果を最も近い整数に切り上げることを指定します。

    説明

    num_objectsパラメーターは、使用可能な最大メモリの10% に設定することを推奨します。 たとえば、パラメーターnum_objectsを1,000,000に設定すると、KFENCEは最大8 GiBのメモリを消費できます。これは、次の式を使用して計算されます。⌈ 1,000,000 131071 GiB = 8 GiB。

  • sample_interval: メモリが監視される間隔。 有効な値:

    • 0: KFENCEは無効で、メモリを監視しません。

    • 正の数: ミリ秒単位のサンプリング間隔。 たとえば、値100は、KFENCEが100ミリ秒ごとに割り当てられたメモリを監視することを指定します。

    • 負の番号: フルモード。 KFENCEは、特定のスラブタイプなど、特定の条件を満たすすべてのメモリを監視します。

  • fault: このパラメーターはカーネルバージョン5.10.134-16で導入されています。 デフォルト値: report。 faultパラメーターをpanicに設定すると、問題が検出されたインスタンスでダウンタイムが発生し、問題発生時に生成されたコアダンプファイルが保持されます。

    説明

    カーネルのバージョンが5.10.134-16より前の場合、上記のコマンドを実行するとエラーメッセージが報告されます。 このエラーはKFENCEには影響しません。 エラーメッセージは無視できます。

結果の表示

KFENCEがメモリ汚染の問題を検出した後、問題の数と詳細なエラーメッセージを表示できます。

  • 検出されたメモリ汚染の問題の数を表示します。

    sudo cat /sys/kernel/debug/kfence/stats

    次の図は、合計バグ数が増加したことを示すコマンド出力を示しています。

    image.png

  • エラーメッセージの詳細を表示します。

    dmesg | grep -i kfence

    次の図は、1つのエラーメッセージが返されたことを示すコマンド出力を示しています。

    image.png

KFENCEの無効化

  • 次のコマンドを実行してKFENCEを無効にします。

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

    KFENCEを無効にすると、メモリ割り当ての問題が検出されなくなります。 プール内のすべての監視対象メモリが解放されると、KFENCEは1 GiBの粒度でメモリをカーネルバディシステムに返します。

  • ブートコマンドラインパラメータリストにパラメータを追加してKFENCEを起動するシナリオでは、次のコマンドを実行してパラメータを削除できます。 そのため、次回のシステム起動時にKFENCEは自動的に有効になりません。

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

よくある質問

  • KFENCEがメモリとパフォーマンスに与える影響は何ですか?

    • メモリへの影響

      KFENCEは、パフォーマンスの干渉を減らすために多数のメモリオーバーヘッドを交換し、大量のメモリを消費します。 再起動がトリガーされたサンプリングモード (Linuxコミュニティでサポート) が使用されている場合は、メモリを節約するためにnum_objectsパラメーターを小さい値に設定できます。 フルモードを使用するか、KFENCEを動的に有効にすると、GiBレベルのメモリが消費されます。 小さなメモリマシンを使用するときは注意してください。

    • パフォーマンスへの影響

      • サンプリングモードでは、パフォーマンスへの影響は少なくなります。

      • フルモードでは、特定の条件を満たすメモリを監視すると、パフォーマンスへの影響は許容されます。 例えば、特定のスラブタイプのメモリが監視される。

      説明
      • 実際のビジネスシナリオに基づいて段階的なテストを実行して、実際のビジネスパフォーマンスに対するKFENCEの有効化の影響を観察し、その後の展開を決定することを推奨します。

      • オフラインデバッグのシナリオでは、フルモードを使用してすべてのタイプのスラブのメモリを監視すると、パフォーマンスとメモリ使用量が大きく影響します。 ただし、このシナリオでは、パフォーマンスへの影響に関係なく、KFENCEを使用して問題を特定します。

  • KFENCEとKernel Address Sanitizer (KASAN) の違いは何ですか?

    KFENCEとKASANは、メモリ汚染を検出する組み込みのLinuxカーネルツールです。 Alibaba Cloud拡張KFENCE (カーネルバージョン5.10) KFENCEは、より柔軟な方法で有効化および無効化でき、サンプリングをサポートし、オンラインビジネス環境で実行できます。 次のセクションでは、KFENCEとKASANの機能の違いについて説明します。

    • KFENCEは、kmalloc-4kやオーダー-0ページなど、最大4 KiBのスラブのモニタリングをサポートしています。 KASANは、すべてのタイプのスラブのメモリ、メモリのページ、スタックメモリ、グローバルメモリなど、より多くのタイプのメモリを監視できます。

    • KFENCEは、監視範囲内での異常な記憶動作の検出の成功率がKASANよりも高くなっています。

    • KFENCEはKASANよりも多くのメモリオーバーヘッドを持っています。 ただし、KFENCEはKASANよりもサービスパフォーマンスへの影響が少ない。

    ほとんどの場合、KFENCEとKASANを同時に使用しないことをお勧めします。 KFENCEがKASANの監視オブジェクトを引き継ぎます。

  • KFENCEはどれくらい安定していますか?

    既知の問題は、カーネルバージョン5.10.134-15以前に存在します。 KFENCEが0オーダーのページとスラブのメモリを監視する場合、特定のシナリオでダウンタイムが発生する可能性があります。 この問題を防ぐには、次のコマンドを実行して、KFENCEがオーダー0ページのメモリを監視できないようにします。

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