第212回 大騒ぎのSpectreとMeltdownの脆弱性をざっくりと解説頭脳放談

2018年の年明けから大騒ぎとなっているSpectreとMeltdownの脆弱(ぜいじゃく)性をざっくりと解説する。プロセッサの何が問題となって脆弱性が起きているのだろうか?

» 2018年01月23日 05時00分 公開
「頭脳放談」のインデックス

連載目次

 2018年正月早々、プロセッサ業界に騒動が起きている。すでにあちらこちらで報道されているのでご存じの方も多いと思うが、Spectre(スペクター)とMeltdown(メルトダウン)と呼ばれるプロセッサの脆弱性の問題である(脆弱性の詳細は、Google Project Zeroの「Reading privileged memory with a side-channel 」参照のこと)。

 脆弱性は、以下の3つである。「Variant 1」と「Variant 2」がSpectreと呼ばれるもの、「Variant 3」がMeltdownと呼ばれる脆弱性だ。

  • Variant 1: bounds check bypass (CVE-2017-5753)
  • Variant 2: branch target injection (CVE-2017-5715)
  • Variant 3: rogue data cache load (CVE-2017-5754)

 いずれも「本来読めてはいけない秘密のデータなどが格納されているメモリを権限の低い別のプロセスから読めてしまう」というセキュリティ上の大問題だ。「ああ、またまた、いつものソフトウェア脆弱性の騒動か」と高をくくる人もいるかもしれないが今回は違う。OSやソフトウェアに関わりなく、「地球上のほとんどのプロセッサ」が原因である。

 根本的にはハードウェアを変えるしかないと思うのだけれど、そんなことはすぐにはできない。コンピュータを使わないわけにもいかない。取りあえず、OSやWebブラウザのパッチなど、ソフトウェア的な対策で逃げるしかないのだ。けれども、それによる速度低下がばかにならない、ということでほぼIT業界総力を挙げての「緊急」対応になっている。

 こういうドタバタ対応になってしまった理由としては、英国のIT情報サイト「The Register」のリークが原因らしい。この問題は、2017年の夏にGoogleの脆弱性調査専門チーム「Project Zero」が発見したらしいが、問題が問題なだけにひそかにプロセッサやOSなどの主要各社に知らせたらしい。

 そこで各社はひそかに対策を練っていたようだが、この予定外のすっぱ抜きがあり、もくろんでいたシナリオとは異なる形での対応を余儀なくされた、ということのようだ。

 初期の報道では「Intelのプロセッサのみ対象」という報道がなされたが、これについてはIntelが反論している。Intelだけというのは違う。他社プロセッサでも起こる。ただ、「たまたま」Meltdownという名の問題は、Intel製の方が起こしやすかった、という程度だろう。「クライアントもサーバもみんな問題」という指摘は大筋で正しいが、「地球上の全て」というと、少々言い過ぎだと思う。まずは、どんなプロセッサが対象になっているのか、どういうメカニズムでセキュリティ上のリスクが顕在化するのかを考えてみる。

脆弱性の対象プロセッサは?

 対象のプロセッサを簡単に要約するならば、「投機的実行(speculative execution:スペキュラティブイクゼーキューション)」「アウトオブオーダ実行」できるプロセッサ、である。確かに、サーバも、PCやスマートフォン(スマホ)などのクライアントも、現在主力で使用されているプロセッサのほとんどがこの範囲に入る。

 しかし、明確にこの範囲に入らないものも多数ある。制御用のいわゆるマイコン系では、だいたい投機的実行もアウトオブオーダ実行もしない、スーパースカラですらないことも多い。筆者の愛してやまないマイコンは全然気にしなくてよい。ARMなども、「M」などの型番の付いた下位モデルは、範囲外だろう。また、複数命令を同時に実行できるスーパースカラであっても、初代のPentiumのように投機的実行もアウトオブオーダ実行でもないマシンも対象外だ。

 近年のx86、ARMの上位機種、PowerPCなどほぼ全てのメジャーな主力モデルは、ほとんどが対象となる。原理からいうと、SpectreもMeltdownもどこのプロセッサで起きてもおかしくないと思う。

 実際、Spectreの方は、Intelであろうが、AMDであろうが、ARM、IBM全てで起きているようだ。一方、Meltdownは、Intel製のx86では起きて、AMDでは起きていないらしい。しかし、ARMの主力機種の1つである「Cortex-A75」などでは、Meltdownを起こすことができたらしい(ARMのホワイトペーパー「Vulnerability of Speculative Processors to Cache Timing Side-Channel Mechanism」)。

 どうも「起こしやすい」マイクロアーキテクチャと「起こしにくい」マイクロアーキテクチャがあるのではないかと思う。Meltdownはかなり実行タイミングに依存するようなので、もしかすると運よくAMDのマイクロアーキテクチャにはそういうタイミングがなかったのか。まぁ念のため、アウトオブオーダ実行のプロセッサだったら、「大丈夫か?」とうたがってみるべきだろう。

まずは投機的実行とアウトオブオーダ実行のおさらい

 投機的実行とは、プロセッサの高速化技術の一種であり、条件判断(条件分岐)を含むコードの実行速度を高めることができる。

 本来、下の図で条件判断が確定しないとルートAにもルートBにも進むことができない。

投機的実行とは 投機的実行とは

 しかし、条件判断の確定を待っているとプロセッサのリソースが遊んでしまう(だいたい最近のプロセッサは「完了」する命令の数十命令先の命令まで「着手」している程度の仕組みになっている)。そこで、「実行されそうな」ルートに「賭けて」、先行して着手してしまうのだ。条件判断の結果が当たっていれば、先行して着手したルートの結果をそのまま使えば結果オーライだ。外れた場合は、結果を捨てて別ルートでやり直す。

 ともかく条件判断の予想を間違わないようにするのが肝心である。そこで条件判断の過去の履歴を状態条件の予想に使用するのが一般的だ。

アウトオブオーダ実行とは

 アウトオブオーダ実行もプロセッサの高速化技術の一種で、コード実行の順序を工夫することで実行速度を高めることができる。

 ソフトウェアから見たら、命令は順番に実行されているようでないと、つじつまが合わなくなる。しかし、ある「命令1」に時間がかかっている場合、それを待っていると以降の命令の開始が全て遅れるので、「命令1」の完了を待たずに、先行して「命令2」以降の実行可能な命令に着手してしまう。ただし、命令の完了は順番にしないとまずいので、「命令2」以降は、「命令1」の完了前に実行されるが「完了」の順番待ちというような状態に置かれる。

アウトオブオーダ実行とは アウトオブオーダ実行とは

 万が一、「命令1」で例外が発生などすると、「命令2」以降の実行結果は取り消されることになる(上図の点線内)。

脆弱性が起きるメカニズムをざっくりと解説しよう

 さて、上記のような機構を悪用するSpectreとMeltdownであるが、基本的な考え方は似ている。

  1. プロセッサ高速化のための機構にいわば「勇み足」でマズイところにアクセスさせる。
  2. 「勇み足」に気付いたプロセッサはそれをなかったことにするためにソフトウェアから見えるような変更をキャンセルする(通常のソフトウェアからすると「1」に気付くことはない)。
  3. しかし「勇み足」の履歴は、ひそかにキャッシュに残っているので、そこから間接的な方法で推定するとマズイところを読み取った結果が分かってしまう。

 SpectreとMeltdownの違いは「勇み足」を引き起こす手法の部分である。

Spectreの大ざっぱ過ぎる仕組み

 ターゲットは何らかのパスワードを取り扱うようなAPIなどと想定しよう。こうしたAPIに以下のようなコードがあったとする(このコードは脆弱性を発見したProject Zeroの論文「Spectre Attacks: Exploiting Speculative Execution」に例として記載されている)。

if (x < array1_size)
y = array2[array1[x] * 256];

参照先のセル範囲をそのまま記述した場合

  1. このコードに対して、xの値として最初は「array1_size」よりも小さなものを与え、何度か実行を繰り返すことで、次の「y = array2[array1[x] * 256];」が投機的実行されるように(「if (x < array1_size)」が確定するよりも前に実行されるように)仕込んでおく。
  2. 次にxの値として、array1_sizeよりも大きな値を入れて攻撃を実行する。本来は「if (x < array1_size)」の条件文によって、「y = array2[array1[x] * 256];」は実行されないが、投機的実行が行われ、xの値がarray1_sizeよりも大きくてもこのコードが実行されてしまう。
  3. 「array2[array1[x] * 256]」がキャッシュにロードされるものの、「x < array1_size」が確定し、結果、この投機的実行は破棄される(xがarray1_sizeよりも大きいため)。
  4. 別のプロセスから、キャッシュを読み出せば、「array2[array1[x] * 256]」でロードされたデータを知ることができる。なお、キャッシュラインのヒット/ミスは、処理にかかったクロック数を測るなどすれば(ほとんどプロセッサはそのためのカウンタを持っている)簡単に判断できる(ミスするとクロック数が増大する)。

 そもそもこの脆弱性は、キャッシュがプロセス間で共有されており、毎回キャッシュラインをフラッシュするようなことをしていなければ、別のプロセスのキャッシュラインに対する接触情報がプロセスの垣根を越えて見えてしまう、というところにある(もちろん、毎回キャッシュラインをフラッシュしていると大幅に性能が低下してしまう)。

Meltdownの大ざっぱ過ぎる仕組み

 Meltdownの場合は、特にターゲットとするAPIなどがなくても攻撃コードのみでよい。

  1. 権限のないメモリ領域のアクセスなどを行う命令を発行。ただしこの命令の完了には多少時間がかかる。ここで完了すれば権限なし。例外発生ということで、完了までにアウトオブオーダ実行された結果はキャンセルされる。
  2. 1」の完了前の「微妙なタイミング」で「1」からフォワードされたデータをアドレスに見立てたメモリアクセスなどを行う。

【ポイント】「2」の結果はプログラムの実行上は捨てられて、ソフトウェアからは見えないはずだが、キャッシュの履歴を変えてしまった部分まではキャンセルされない。Spectre同様、間接的に調べていくことで、どのようなデータが読み出されたのかが決定できる。なお、効率的に実行するためには、例外発生でOSにトラップされないような仕組みも入れておく必要がある。


アウトオブオーダ実行とは Meltdownの脆弱性


 こうして書くとどちらの脆弱性も簡単に攻撃が可能そうだが、何もないところから実際にちゃんと動作するコードを書くのは相当難しそうだ。さらに、緊急のパッチなどの対策でさらに難易度は高くなっていると思われる。

 しかし、これはプロセッサのハードウェアに起因するもので、かつ現状のプロセッサのほとんどが頼り切っている技術であるだけに、根本対策は結構難しそうである。

 ふと、昔を思い出した。プレスリリースの下書きをしているときに「speculative execution」という用語を訳さなければならなかったことがある。その際、何も考えずに、一般的に日本語訳として使われている「投機的実行」としたら、その原稿を読んだ当時筆者が在籍していたA社のN社長から「投機はよくない!」とお叱りを受けたことがある。そんなことを言われても、訳語としては定番なものだしね……。弱って何か適当な用語に置き換えて書いた後に小さく(投機的実行)としたのだった。やっぱりN社長が言ったように「投機はよくなかった」のだな。

筆者紹介

Massa POP Izumida

日本では数少ないx86プロセッサのアーキテクト。某米国半導体メーカーで8bitと16bitの、日本のベンチャー企業でx86互換プロセッサの設計に従事する。その後、出版社の半導体事業部などを経て、現在は某半導体メーカーでヘテロジニアス マルチコアプロセッサを中心とした開発を行っている。


「頭脳放談」のインデックス

頭脳放談

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。