Microsoft Update時に再起動が求められる理由 初級編

Windows Update/Microsoft Updateの時、システムの再起動を求められることが多い。なぜ再起動が必要なのか、再起動しなくてはならないものなのか、ちょっと書いてみる。

といっても、私は別にWindowsについて詳しくないし、OSや情報セキュリティについて正式な勉強をしたことのない人なので、突っ込んだ話はできない。

なので、Windowsに限らずMac OS XLinuxでも通用するだろう、一般的なことを大雑把に書く。例のごとく、より詳細かつマニアックな中級編以降を誰か書いてくれるものと期待している。

推奨読者レベル:基本情報技術者レベルの「テクノロジ系:コンピュータシステム」の知識があること。

前提知識

現代の大抵のコンピュータでは、プログラム内蔵方式が採用されている。実行するプログラムは主記憶装置に置かれていて、CPUは「主記憶装置から命令を1つ読み出し、実行し、次の命令を1つ読み出し……」といった按配にプログラムを実行していく(逐次制御方式)。

この構図は、WindowsMac OS Xなどのオペレーティングシステム(以下、OS)も、OS上で実行されるアプリケーション・ソフトウェア(以下、アプリケーション)も同じだ。どちらも、CPUが実行する命令は主記憶装置上にある*1

WindowsMac OS Xを動かすようなコンピュータでは、主記憶装置として半導体メモリが使われている。半導体メモリは安価で高速だが、通電状態でないとデータを保持できない。電源を止めると、データは消えてしまう。

そこで、OSもアプリケーションもハードディスクドライブ(HDD)やソリッドステートドライブ(SSD)などの補助記憶装置にプログラムを保存しておき、実行する時に主記憶装置に読み出すようになっている。

OSは、コンピュータ起動時に補助記憶装置から主記憶装置に読み出され、実行される。アプリケーションは、OSの起動が完了した後に、任意のタイミングで補助記憶装置から主記憶装置に読み出され、実行される。

つまり、プログラムAを実行している時、その実体は、補助記憶装置上のプログラムa(雛形のようなもの)と、主記憶装置に読み出されたプログラムa'(いわゆる「計算プロセス」とでもいうべきもの)の2つが存在することになる。多重起動可能なアプリケーションの場合、主記憶装置上にプログラムa'が2つ以上存在する状況もありうる。

セキュリティ更新では何をしているか?

Windows Updateや、Mac OS Xのセキュリティ・アップデートでは、概ね次のようなことをやっている。

  1. 補助記憶装置上のプログラムの差し替え
  2. 実行中のプログラムの終了と再実行(≒再起動)

プログラムは補助記憶装置から主記憶装置に読み出され、実行される。なので、補助記憶装置上のプログラムを脆弱性対策が施された新しいものに置き換えてしまえば、少なくとも次回プログラムを起動した際には、更新されたバージョンが実行されることになる。

ただし、実行中のプログラムについては、CPUが実行している実体は、補助記憶装置上のプログラムが更新される前に主記憶装置に読み出された「更新前のプログラム」だ。補助記憶装置上のプログラムを更新しただけでは、主記憶装置上のプログラムは更新されない――つまり、実行中のプログラムには脆弱性が残ったままだ。

では、主記憶装置上のプログラムを更新するには、どうすればよいか?

一般的には、実行中のプログラムを一旦終了し、再度起動することで、脆弱性対策が施されたバージョンが実行されるように対応することが多い。最近のブラウザなどの、自動更新機能を持つアプリケーションにおいて、バックグラウンドで自動更新処理が実行された後にアプリ自体の再起動が促されるのは、この理由による。またLinuxのパッケージ管理コマンドでシステムを更新した際に、ログ等にデーモンの再起動が記録されているのも、同じような理由からだ。

OSへのセキュリティ更新適用と、システムの再起動

OSへのセキュリティ更新適用でも、やることは「補助記憶装置上のプログラムの差し替え」と「実行中のプログラムの終了と再実行」だ。

問題は、「実行中のプログラムの終了と再実行」をいつ行うかだ。というのも、OSはコンピュータ起動時に実行が開始され、シャットダウン時に終了する。OSは、コンピュータ稼働中にシステムのリソース管理を行うプログラムだ。コンピュータを稼働させたまま、途中でOSのみを再起動することはできない。

そこで、補助記憶装置上のOSのプログラムを更新した後にコンピュータを再起動させる。OSは、コンピュータ起動時に補助記憶装置から主記憶装置に読み出されので、再起動後の主記憶装置上のOSのプログラムは、補助記憶装置上のものと同様の、脆弱性対策が施された更新版になる。

この点は、WindowsMac OS XLinuxFreeBSDなどのいずれも同じだ。OS(正確にはカーネル)が変更されたなら、更新されたOSが実行されるように、コンピュータを再起動する必要がある*2(ただし、OSの種類によってカーネルの更新が発生する頻度に差がある*3)。

主記憶装置上のプログラムを直接修正できないか?

さて、補助記憶装置上のプログラムを差し替えた後に、主記憶装置上のプログラムを修正版にするために、プログラムを再起動するのだった。では主記憶装置上のプログラムを直接修正することはできないだろうか? 主記憶装置上のプログラムを修正できるなら、再起動は不要になるはずだ。

技術的には、主記憶装置上のプログラム自体に修正を適用する方法も無くはない。ただし、以下ような制約があるため、この方法は一般的ではない。どちらかといえば、エンタープライズ向けの、高可用性が要求されるプログラム(それだけコストをかけることが許される環境)向けの話となるし、そのような環境でも常にその方法が使えるとは限らない。

  1. そのプログラムを実行しているプラットフォームのサポートが必要。
    • OSのメモリ保護の観点では、あるプロセスが他のプロセスのメモリ領域を読み書きすることはできない(そうしないと、暴走したプロセスが他のプロセスを巻き込んで破壊できてしまう)。主記憶装置上のプログラムに変更を加えることは、上記のような「あるプロセスが別のプロセスのメモリ領域を読み書きする」に該当する作業なので、OSがこのような処理をサポートしている必要がある。仮にサポートしていたとしても、悪用できないように何らかの制約が設けられているはずだ。
    • Windows XP SP1/Windows Server 2003 SP1以降なら、ホットパッチが可能。
    • LinuxのKernel 4.0以降なら、ライブパッチ機能が組み込まれているので、Kernelのアップデートにて利用することができる(この機能を有効にした設定でKernelがビルドされている必要がある)。
  2. 技術的な制限により、この方法では対応できないケースがある。
    • そもそも、プログラム・カウンタを含めて、機械語レベルの命令の実行位置や、関数呼び出しでのジャンプ先/復帰先をアドレス値で管理している以上、プログラム実行中に、プロセス中の機械語のレイアウトが急に変更されてしまうと、プログラムの実行が滅茶苦茶になってしまう訳で。
    • あと、やはり実行中に、プロセス中の大域変数が配置されている部分のオフセットが変化したり、内部変数の追加で「スタックに詰まれているデータ」と「スタックに詰まれるべきデータ」に齟齬が生じた場合も、大惨事になりかねない。
    • Windowsのホットパッチや、Linuxのライブパッチ機能(kpatch)では、原理的には「関数呼び出し部分にて、何らかの方法で『本来呼び出されるはずの関数』から『実際に呼び出されてほしい関数』に挿げ替える」ということを行っていて、これによって「不具合/脆弱性が含まれる機械語群」ではなく「不具合/脆弱性が解消された機械語群」が実行されるようにしている。
    • 逆に言えば、上記のような「関数の挿げ替え」レベルでの対応が難しいケースでは……。
  3. パッチを提供する側のコストが高くなる。
    • 主記憶装置として半導体メモリを使っている以上、主記憶装置上のプログラムに適用された修正はシャットダウン時にプログラムごと消えてしまう。補助記憶装置上のプログラムも忘れずに差し替えておく必要がある。
    • 主記憶装置上のプログラムと補助記憶装置上のプログラムでは、修正を適用するアプローチが違うため、それぞれ別々のパッチが必要となる。つまり、1つの問題に対して2つのパッチを開発・テストしなくてはならない。単純に考えると、コストが2倍となる。要求品質が高いプログラムの場合、開発した2つのパッチについて、「パッチ適用後のプログラムの振る舞いが全く同じか?」ということまで検証しなくてはならないかもしれない(その分だけ、新たな開発コストが発生する)。
    • 主記憶装置上のプログラムに修正を適用するためのパッチの開発コストは、補助記憶装置上のプログラムへのパッチよりも高い。

例えば、LinuxのKernel 4.0以降には、ライブパッチ機能が組み込まれている。今後、開発が進むことで、Linuxディストリビューションにてライブパッチによる修正が提供されるようになるかもしれない。

かもしれないのだが……例えばRHELで何らかの契約を締結している顧客向けに提供されることはありうるように思うが、果たして全くのコミュニティ・ベースで無償で提供されるものだろうか? 個人的には、ちょっと考えにくい気がする。

Windows特有の事情1:どこまでがOS?

Windows特有の事情として、LinuxなどではOSとは独立したプログラムとして提供されている機能が、OSと比較的密接なプログラムとして提供されることがある、という点がある。

現在は割合と改善されているが、かつてはInternet ExplorerのようなブラウザもWindows内部との結合度が高く、IEの巻き添えでWindowsまで落ちるようなことがあった。

このような環境では、他のOSではアプリケーション単体の更新・再起動で済むようなケースでも、WindowsではOS自体の再起動まで必要となることがある。

一方、LinuxFreeBSDなどでは、WindowsではOSが提供している機能が、OSとは独立したデーモンなどとして提供されていることが多い。この場合、当該デーモンの再起動だけで済む。OS本体(カーネル)が変更されない限りコンピュータ自体の再起動は不要となる。

今ではLinuxでも再起動が必要な機会が増えた感じがあるが、少なくともかつての「OS再起動の回数」の差は、OSの責務の幅による部分も大きかったように思う。

Windows特有の事情2:実行ファイルのロック

これまたWindows特有の事情として、「実行中の実行ファイル(*.exeや*.dllなど)への上書きや削除ができない」というものがある。*4

このため、Windows Updateにて補助記憶装置上のOS絡みの実行ファイルを完全に差し替えることが難しい。なぜなら、これらのファイルはOSの実行中――コンピュータを起動してからシャットダウンするまでの間ずっと、OS関連のプログラムが使用している可能性があるからだ。

これは推測だが、上記の問題を回避するために、システムを更新する際には、シャットダウン中や再起動時のブート中に、通常のOS稼動状態とは異なる状態に一旦遷移して更新処理を行っているようだ。

まとめ

セキュリティ更新を「現在実行中のプログラム」にまで適用させるには、プログラムを再起動する方法が確実で、これがOSの場合「プログラムを再起動する=コンピュータを再起動する」となるのです。

ということで、Windows Updateなどのセキュリティ更新を実行して、OSやアプリケーションの再起動が要求されたら、なるべく早めに再起動すること。でないと脆弱性の修正が「実行中のプログラム」にまで適用されず、危険なままとなってしまう。

*1:仮想記憶が絡んでくると微妙なのだが、今回のテーマでは気にする必要がないので無視する。後段に書いているが、仮に仮想記憶で一時的に補助記憶装置にデータを置いていたとしても、コンピュータをシャットダウンする際に削除されるので、主記憶装置上のデータと同じ条件だといえるからだ。

*2:ただしLinuxについては、Kernel 4.0にてライブパッチ機能が組み込まれたこともあり、今後事情が変わってくる可能性がある。

*3:とはいえ、同じLinuxでも、Ubuntuのデスクトップ版を使うのと、Debianのstableをサーバとして運用するのでは、再起動の頻度は違うからなあ(Ubuntuのデスクトップ版では意外と再起動の機会がある気がする)。この辺は各ディストリビューションのポリシーによるところが大きくて、新機能をガンガン取り入れていくポリシーだとカーネルも頻繁に更新されるし、逆に保守的なポリシーだと脆弱性対応のバックポートしか受けつけないので更新頻度は低くなる。

*4:正確には、OS本体ではなくファイルシステムに起因する制限であるようだ。