2015年02月17日

第15回「フォールトインジェクションノススメ」が掲載されました。

今回は SystemTap を使ってエラーを発生させるというちょっと怖いお話です。でも、ここでは目を離した隙にカーネルの挙動が変化しているという別の方向で怖い話をしたいと思います。

使い方1で紹介した不具合は RHEL 6/7 カーネルにバックポートされましたが、修正されたことを知るのは面倒です。というのも、エラータのページにはセキュリティ上の不具合の修正内容についてしか言及されておらず、「その他の修正内容についてはテクニカルノートを見てね」と書かれているのに従ってテクニカルノートのページをチェックしても、今回の不具合に該当しそうな内容が見当たりません。実は、 rpm パッケージの変更履歴の説明文をチェックするかソースコードの差分をチェックして初めて、「あ、修正されたんだな」と判るようになっていたのです。テクニカルノートには全修正内容が網羅されていると思っていると、落し穴にはまるかもしれないということですね。

使い方2で紹介した挙動は、 3.19-rc6 がリリース(1/25)されてから 3.19 がリリース(2/8)されるまでの僅かな間に、 __GFP_FS が含まれていない場合にはメモリ枯渇時に OOM Killer を発動させずに諦めるという変更が適用されてしまいました。 linux-next.git でテストを行って問題が無いことを確認してから linux.git の -rc1 がリリースされるまでにマージするというのが本来の手順なのですが、その手順をすっとばされた訳です。ファイルシステムより低いレイヤーでのメモリ割り当てがメモリ枯渇時に失敗してしまうようだと、ファイルシステムエラーが多発してしまうので困ると思うのですが。そういう変更を適用するのなら、 __GFP_NOFAIL が必要な個所を全て洗い出して修正してからにしてほしかったんだけどなぁ。

posted by 熊猫さくら at 22:56| Comment(0) | TrackBack(0) | Linux

2015年02月03日

緊急コラム「 glibc 脆弱性( CVE-2015-0235 )の影響範囲の調査方法について」が掲載されました。

ルイージ@お化け屋敷のような、次から次へと襲い掛かってくる問合せの嵐に困っています。

glibc の不具合や脆弱性が見つかる度に、影響を受けるプログラムの調査方法を説明するというのも馬鹿げた話ですので、今回の glibc 脆弱性を機に、緊急コラムとして調査方法を説明することにしました。
一昨日考えた内容を急いでダンプしたものなので、全てを網羅できている訳ではありません。でも、影響範囲を調査することの難しさに気付いて素直にアップデートをしていただけるのなら、この記事を作成した意味はあると考えています。

今回の脆弱性については幽霊に振り回されすぎている感がありますが、システムの動作を再確認してみるチャンスかもしれません。任意のホスト名を渡すことができるかどうかをソースコードレベルで網羅的に調査してみたら、OSコマンドインジェクションのような、今まで見落としていた脆弱性が見つかってもおかしくないと思っています。

posted by 熊猫さくら at 21:15| Comment(0) | TrackBack(0) | Linux

2015年02月02日

第14回「 SystemTap ノススメ」が掲載されました。

今回は SystemTap を使って挙動を追跡するというお話です。

とあるシステムで、 Pacemaker を起動してから約70秒後にシステムが予期せぬ再起動をしてしまうというトラブルが発生していました。再起動する直前だけディスクI/Oが多いという傾向が見られたため、何らかのディスクI/Oに関するトラブルに巻き込まれた結果、 /dev/watchdog への書き込みが遅延して watchdog タイムアウトによるリセットが発生しているのではないかと疑ったのですが、原因を掴めませんでした。

そこで、再起動要求が発生した時点の kdump を取得することにより、誰が再起動要求を発生させているのか/どうして再起動要求を発生させることになったのかを調査するために、今回紹介した SystemTap スクリプトを仕掛けて事象を再現してもらいました。すると、何と sfex_daemon という監視プログラムが /proc/sysrq-trigger に b を書き込んだことによるリセットが発生していたことが判明したのです。これをきっかけに、設定の誤りが予期せぬ再起動の原因であったことが判明し、トラブルを解決することができました。

sfex_daemon のソースコードによると、普段はスリープしながら、定期的に update_lock() を呼び出してロックの状態を確認し、異常を検知した場合には failure_todo() を呼び出しているようです。 failure_todo() は cl_log() という関数の中から syslog() 関数を呼び出すことで syslog デーモンにログメッセージを送信後、 /proc/sysrq-trigger に b を書き込んでいるようです。では、何故 syslog() 関数を介して送信した筈のログメッセージが、 syslog ファイルに残っていなかったのでしょうか?実は、 syslog() 関数を介して syslog デーモンにログメッセージを出力する方法には、2つの落し穴があったのです。

1つ目の落し穴は、 rsyslog の version 3 からは、デフォルトで非同期書き込みを行うようになっている点です。そのため、非同期書き込み後すぐに再起動要求が発生してしまっては、ログに残すことができません。2つ目の落し穴は、 syslog() 関数を介して syslog デーモンにログメッセージを出力する方法では、 syslog デーモンがログメッセージを出力するよりも前に syslog() 関数が復帰してしまうため、たとえ同期書き込みを行うようになっていたとしても、同期書き込みの完了を待たずに再起動要求が発生してしまい、ログに残すことができません。

RHEL 7 の登場により、3つ目の落し穴も発見されました。 RHEL 7 の場合はデフォルトのファイルシステムが ext4 から xfs に変更されたため、 ext 系ファイルシステムで行われていた5秒毎の writeback も行われなくなっています。そのため、 RHEL 7 においては、明示的に sync() 関数などを呼ばないと、問題事象発生直前のログが残っていないという可能性が一層高くなっています。

再起動直前のログを確実に残したい場合、 syslog デーモンに頼っていては駄目なのかもしれません。今後 RHEL 7 のシステムが増えてくるに従い、問題事象発生時のログを如何に残すかという課題が出てきそうです。カーネルメッセージであればシリアルコンソールや netconsole 経由で取得するという方法が使えます。アプリケーションのメッセージについても自力でローカルとリモートの両方にログを出力するとかしたほうが良いのかなぁ?

明日は節分ですって? 幽霊は〜外〜。アップデートは〜内〜。(謎)

posted by 熊猫さくら at 22:00| Comment(0) | TrackBack(0) | Linux