熊猫です。 今年は syzbot が見つけたいろいろな問題を修正しています。その中でも苦労しているのが、 loop モジュールのバグ、 OOM 発生時のバグ、 printk() されたメッセージの読める化です。 syzbot が色々なファイルシステムを試験するための基盤として使っている loop モジュールには、 スレッドセーフではないことによりクラッシュしたり、ロックの依存関係を lockdep の検査から 見えないようにしていることで静かにデッドロックしたりするバグがあります。1年以上前に 報告されていた2つのバグを修正するパッチが、ようやく linux-next.git に到達 ( https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/log/drivers/block/loop.c?h=next-20181109 )し、 loop モジュールが原因かもしれない他のバグについても効果があるかどうかを観察している状態です。 OOM に関しては、何年も前から問題を修正するための死闘が続いています。議論への参加者が絶望的なまでに 少なく、最悪のケースを考慮していない/全くテストしていないという状況です。 MM というのは Memory Management の頭文字なのですが、 Out Of Memory 時の挙動に関しては全く Management になっていないです。 パッチの正しさもチェックされずにマージされていくという危険な流れを、1つ1つ再現プログラムを作りながら、 修正パッチのバグを修正しています。やっとのことで、 workqueue がスリープしないことが原因でハングアップする 問題を修正するパッチが 4.20-rc1 に到達 ( https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/mm/page_alloc.c?h=v4.20-rc1&id=15f570bf3d13aa94a97234538a5110d18df03aa3 ) しました。 別の問題として、早急に MMF_OOM_SKIP フラグをセットしてしまうのが原因で、 OOM killer が必要以上に プロセスを殺してしまうという問題が残っています。緩和策として、 MMF_OOM_SKIP フラグをセットする処理を 終了処理を行うスレッドに委任するという方法が提案されていますが、誰も議論に参加してくれないため 見落としによるバグが無いことが証明されておらず、停滞しています。別の緩和策として、見落としによる バグが無いことを証明可能な、タイムアウトにより MMF_OOM_SKIP フラグをセットする方法も提案 ( https://lkml.kernel.org/r/15400****@I-lov***** ) されており、対立が続いています。 そして、 syzbot が普通ではないケースをテストすることにより、カーネル内でのメモリリークに起因した ロックアップや、 memcg OOM に起因してメッセージの洪水が起こる問題も発見されるようになりました。 前者はメモリ枯渇時に printk() が大量に呼ばれることによる問題です。 printk() の処理は遅いため、 大量に呼ばれると処理が遅延し、場合によってはロックアップしてしまいます。後者は memcg OOM killer が 「強制終了させるプロセスを見つけることができなかった」際に printk() が永遠に呼ばれてしまうことによる 問題です。これも、 printk() が呼ばれる頻度を減らす方法についての対立が続いています。 printk() は、ユーザランドで動作するプログラムにおける printf() に相当する関数です。 printk() は、1行分のメッセージを1回の呼び出しで出力する場合は問題ないのですが、 1行分のメッセージを複数回の呼び出しに分けて出力する場合、複数のスレッドが同時に printk() を呼び出すことにより、異なるメッセージが混ざってしまったり、必要以上に 改行されてしまったりすることで、解読することが難しくなるという問題があります。 fuzzing test では普段行われないような動作を繰り返したり、普段指定しないような値を意図的に 指定したりするので、大量のメッセージが出力されます。そして、問題が発生したかどうかを判定 するためには、問題に関連したメッセージを確実に抽出できることが重要になります。 ユーザランドで動作するプロセスの場合、 stdout というグローバル変数が共有される範囲は 当該プロセスに閉じられます。しかし、カーネルの世界では、システム上に存在する全ての スレッドで stdout 相当のグローバル変数を共有しています。そのため、メッセージが混在 しないようにするためには、 printk() を呼ぶ可能性のある全ての関数に対して FILE *fp に 相当する引数を渡してやる必要がある訳です。例えば https://lkml.kernel.org/r/3786f****@i-lov***** のように。(あるいは、 printk() を呼ぶ前に snprintf() 相当の処理を行うことにより、 1行分のメッセージを1回の printk() 呼び出しで出力できるようにしてやる必要がある訳です。) 超巨大なプログラムであるカーネルのソースコードに対して、 printf() を全て fprintf(fp) に 置き換えるのは大変な修正であることは想像に難くないでしょう。 修正が大変である割に得られるメリットが少ないため、 printk() を提供する側が API を 提供したとしても、 printk() を利用する側がその API へ移行してもらえる可能性は 低いのではないかと考えています。そこで、 printk() の中で呼び出し元スレッドを識別し、 あたかも複数の stdout が存在するかのように振る舞わせる方法を提案 ( https://lkml.kernel.org/r/07dcb****@i-lov***** ) したところです。 さてさて、この先、どんな展開を辿るでしょうか?