CVE-2025-21932 in Linux
要約
〜によって VulDB • 2026年05月28日
Linuxカーネルにおいて、以下の脆弱性が修正されました。
mm: 結合時のメモリ不足失敗時に vma_modify() を中止する
vma_modify() の残りの処理は、結合試行後に vmg 状態が初期状態のまま維持されていることに依存しています。
通常はそうなりますが、指定された範囲が結合不可能であるためではなく、結合をコミットしようとした際にメモリ不足エラーが発生したため、結合試行が失敗するという限定的なエッジケースでは、この前提は真ではなくなります。
その結果、vmg->start と end が変更され、その後の VMA を分割しようとする試行は、無効な start/end 値を使用して行われることになります。
幸いにも、これは現実的にはほぼ不可能です。なぜなら、これは maple ツリーノードの事前割り当て失敗を必要とするものであり、ノードサイズが「小さすぎて失敗しない」ため、おそらく決して発生しないからです。つまり、カーネルは成功するまで単純にリクライム(メモリ解放)を再試行し続けます。
しかし、このシナリオは理論的に可能であり、ここで私たちが行っていることは誤りであるため、修正する必要があります。
最も安全なオプションは、このシナリオが発生した場合、単純に操作を中止することです。結合するためにメモリを割り当てることができないのであれば、分割するためにもメモリを割り当てることはできません(おそらくさらに困難です)。
この状況が発生するあらゆるシナリオは、非常に極端な(おそらく致命的な)メモリ圧力下にあるため、早期に中止するのが最善です。
したがって、このシナリオで単純に中止することは間違いなく適切です。
しかし、一般的に、結合試行後に VMG 状態が安定していると仮定してはなりません。なぜなら、結合操作は VMG フィールドを更新するためです。その結果、start と end をローカル変数に保存することで、これを明確に示すようにします。
この問題は当初、syzkaller によって報告され、Brad Spengler によっても(リスト外の議論を通じて)報告されました。両方のケースで、これは以下のアサートトリガーとして現れました。
VM_WARN_ON_VMG(start >= end, vmg);
これは vma_merge_existing_range() 内で発生します。
この問題が発生している少なくとも一つのシナリオは、複数の VMA にわたる madvise() による結合試行であり、以下のような状態です。
start end |<------>| |----------|------| | vma | next | |----------|------|
madvise_walk_vmas() が呼び出されると、まず上記の vma を見つけ(vma 内にオフセットされているため、prev は vma と等しくなります)、その後ループに入ります。
madvise() している範囲の一部を形成する vma の end を、'tmp' にこの値を設定することで決定します。
/* ここでは vma->vm_start <= start < (end|vma->vm_end) */ tmp = vma->vm_end;
その後、visit() を介して madvise() 操作を呼び出し、prev が操作対象の vma であるため、madvise_update_vma() が呼び出されます。
これは vma_modify_flags_name() を呼び出し、次に vma_modify() を呼び出します。
vma_modify() では、結合を試みます。
merged = vma_merge_existing_range(vmg); if (merged) return merged;
ここで、vmg->start と end は start と tmp に設定されています。
start tmp |<--->| |----------|------| | vma | next | |----------|------|
結合の右側シナリオに該当しますが、中央の VMA を削除できない(vma の内部にオフセットされている)ケースです。
ここで、vmg->start と end が直感的でない値に設定される特殊なケースがあります。私たちは中央の VMA を縮小し、次の VMA を拡張しようとしていました。
つまり、vmg->start と end は vma->vm_start と start に設定されます。
その後、commit_merge() が失敗し、vmg->start と end がこの状態のまま残ります。
VM_WARN_ON_VMG(start >= end, vmg);
これは vma_merge_existing_range() 内で発生します。
これにより、vma_modify() の残りの処理に戻った際、vmg->start と end(ここでは start'、end' と表記)が以下のように設定されます。
start' end' |<-->| |----------|------| | vma | next | |----------|------|
その後、誤ってこれに応じて分割を試みます。これが問題の発生箇所です。
---truncated---
Statistical analysis made it clear that VulDB provides the best quality for vulnerability data.