GrapheneOSがARM MTEで発見したBluetoothメモリ破損
(grapheneos.social)- Pixel 8・Pixel 8 Proのハードウェアメモリタグ付け対応により、Android 14 QPR2でBluetooth LEに新たに入ったメモリ破損バグが明らかになった
- 原因はBluetooth LEのupstream use-after-freeバグと確認され、GrapheneOSは修正パッチを作成して次回リリースに含める予定
- Samsung Galaxy Buds2 ProのBluetooth LEモードで問題を再現していたユーザーが、修正が機能することを確認しており、stock Pixel OSも影響を受ける
- GrapheneOSは、Bluetoothが大きな攻撃面である以上、このプロセスのメモリタグ付けを無効にする回避策は短期的にも不適切だとみている
- Android Bluetoothコードの一部はRustへ移植されたが、残るコードの移植とHWASan・MTEビルドの実機テストがなお必要
Android 14 QPR2のBluetooth LEバグと修正
- GrapheneOSは、Pixel 8およびPixel 8 Proのハードウェアメモリタグ付け対応により、Android 14 QPR2のBluetooth LEメモリ破損バグを発見した
- すべてのBluetooth機器で発生する問題ではなく、特定のBluetooth LE機器でのみ再現される
- GrapheneOSは、新たに導入された機能を修正するか、一時的に無効化する方法を調査している
- 原因はBluetooth LEのupstream use-after-freeバグと確認され、GrapheneOSはパッチを開発した
- 優先事項は、この修正を含むGrapheneOSリリースを迅速に配布すること
- Androidのセキュリティバグとして報告する予定
- この修正はBLEオーディオのリグレッションも解決するとみられる
- Samsung Galaxy Buds2 ProをBluetooth LEモードで使用して問題を再現していたユーザーが、修正が機能することを確認した
- 同じ問題はstock Pixel OSにも影響する
- GrapheneOSは、hardened_mallocのメモリタグ付け対応でこれを検出し、コピー可能なレポートを含むMTEクラッシュ通知を追加した
MTEの適用方法とAndroid Bluetoothコードの課題
- GrapheneOSは、このプロセスでメモリタグ付けを無効にする回避策を短期的にも適切ではないとみている
- Bluetoothは、このバグの実際の悪用可能性とは別に、大きな攻撃面である
- AndroidはBluetoothコードのかなりの部分をRustへ移植したが、残るコードの移植にもさらに多くのリソースが必要
- 実環境では、さまざまなBluetooth機器を対象にHWASanおよびMTEビルドのテストがさらに必要
- Pixelデバイスは大きなハードウェアセキュリティ機能であるMTEを搭載しているが、標準OSは3.125%のメモリ・キャッシュ使用量削減を理由に有効化していない
- 計算は、タグ付けされたメモリ16バイトあたり4ビットタグに基づく
- heap MTEは非同期モード(async mode)では性能オーバーヘッドがほぼ0%に近く、asymmetric modeではSSPのような既存の緩和策よりコストが低い
- GrapheneOSは、標準OSとの互換性が確認されているユーザーインストールアプリでMTEをデフォルト有効化している
- Settings > Securityで、すべてのユーザーインストールアプリに対してopt-inで有効化できる
- クラッシュレポートをコピーできる通知と、アプリごとのトグルを提供する
- GrapheneOSのhardened_malloc MTE実装は、標準のランダムタグと専用のfreeタグを使い、以前のタグと現在または直前の隣接タグを動的に除外する
- Chromium統合は修正済みで、PartitionAllocも改善予定
1件のコメント
Hacker News の意見
Pixel が MTE という大規模なハードウェアセキュリティ機能を搭載していながら、OS ではメモリ/キャッシュ使用量 3.125% を節約するために有効化していないというのは奇妙
Pixel チームがこの判断をどう正当化しているのか本当に見てみたいし、わずかな性能向上のためにこれほど重要なセキュリティ機能を無効にする思考過程が気になる
ただし GrapheneOS は素晴らしいプロジェクトだが、対応デバイスはおよそ11機種にすぎず、AOSP の変更ははるかに大きな OEM エコシステムを考慮しなければならない
Android をより良くする方法は常に探しているが、特定機能のリリースを批判しながら、より大きな OEM エコシステムを無視するのは視野が狭く見える
この機能が有効化されていない理由がメモリ/キャッシュの 3% のためである可能性は非常に低く、別の考慮事項があったのだと思う
機能を有効にするかどうかを決める際には、おそらくこちらの方が大きな要因である可能性が高い
他の OS も現在 MTE を有効化した状態で出荷 していないというので、有効化するかどうかはもっと微妙な判断である可能性が高い
Google 側の人たちがそもそも ハードウェア MTE を推進しており、ASAN、syzkaller などに関わったチームから始まった
Android チーム所属ではなかったが、Android 側の助力と支援があり、ARM などとの協力も当然あった
当時そのチーム群を率いていた立場なのでトレードオフはよく知っているが、問題はメモリやキャッシュだけではない
MTE は動的にオン/オフでき、性能コストがほぼ 0 に近くなるよう設計されたのは確かだが、当時の主な用途はサンプリングベースのバグ発見だった
デバイス群全体で 1% の時間だけ有効にしても、十分な規模があればバグを非常に素早く見つけられ、内部テストにも使える
つまり、望むときに ASAN に準じる機能 をオン/オフできるようにすることが目標だった
常時有効のセキュリティ緩和策として使うことは副次的な可能性であり、メモリオーバーヘッド以外にも問題がある
例えば、ユーザーに見えるクラッシュが突然大量に発生するが、MTE がある端末ではクラッシュし、ない端末ではクラッシュしない可能性があり、一貫性もない
開発者の立場でも、MTE 有効化端末がほぼ1機種しかない状況で、全員にその端末でテストするよう強制しなければならないので歓迎されないだろう
セキュリティエクスプロイトを防ぐことはでき、悪用の代わりにクラッシュさせることはできる
無害なバグも捕まえられる
しかし前述の通り、使っていないとは仮定しないし、本番環境で常時有効にはしていないと見るのが妥当に思える
このようなハードウェア機能の導入経験がある人なら、システムが起動して実行され、特定の動作でだけ MTE 下でクラッシュするという事実自体が、むしろすでに使っている良い兆候だと言うだろう
使っていなかったなら、おそらく百万回はクラッシュしていただろう
付け加えると、ランタイム緩和策として最善かどうかも明確ではない
標準の Pixel はエンドユーザーにデフォルト有効の状態で提供しないかもしれないが、望めば誰でも開発者向けオプションで Memory Tagging Extensions を有効にできる
オフにするまで維持することも、特定アプリのテスト用に1セッションだけ有効にすることもできる
標準の Pixel OS で開発者向けオプションからメモリタグ付け対応を有効にするのは、利用可能な状態にするだけで、実際には使わない
Android Debug Bridge(ADB) シェルで
setpropにより ヒープメモリタグ付け も有効にする必要がある割り当てにタグを付けなければ、単に有効になっていても何の価値もない
デフォルトのアロケータ実装である Scudo を通じて、標準 OS でユーザー空間ヒープ MTE を完全に有効化することはできるが、現時点では特に強化された実装ではない
MTE バックエンドを使う KASan も
setpropで利用できるが、今は強化用途として設計されておらず、今後そうなるかも明確ではないカーネルには KASan の一部ではない別個の MTE 実装が必要になる可能性が高く、GrapheneOS もまだ行っていないため、現在の MTE 強化はユーザー空間の機能である
GrapheneOS は hardened_malloc のために独自の ハードウェアメモリタグ付け 実装を使っており、セキュリティ特性がより強い
標準 OS でデフォルト有効にするには、この問題を含む複数の問題を修正または回避する必要があった
メインコアで非同期 MTE を使う代わりに、すべてのコアで非対称モードを使用する
非対称モードは書き込みでは非同期、読み取りでは同期式なので、エクスプロイト成功の機会の窓を残さず、適切に遮断する
システムコールで検査され、もう一つの迂回経路になり得る io_uring は SELinux 制限により、Android では中核システムプロセス2つにのみ許可される
fastbootd はインストール中にのみ使われ、snapuserd はアップデート適用後に中核 OS で使用される
GrapheneOS は標準 OS と互換性が確認されたアプリに常にヒープ MTE を使用する
ユーザーインストールアプリのうち、互換性データベースになく、自ら互換性を示していないアプリには、アプリごとの MTE トグルを提供する
ユーザーはユーザーインストールアプリにデフォルトで MTE を強制するよう有効にすることも、互換性のないアプリだけを除外することもできる
これを実際に使えるようにするには、ユーザーに見えるクラッシュ報告システムが必要であり、開発者に有用なクラッシュレポートを簡単にコピーして送れるよう実装した
設定で Memory Tagging Extensions を検索すると存在すると出るので、どこかに隠れているのかと思ったら、Pixel 8 端末専用だった: https://news.ycombinator.com/item?id=38125379
GrapheneOS はセキュリティ面で他よりもはるかに先を行っているので、Pixel ハードウェア以外を選ぶ理由があるのか疑問に思えてくる。
ただ、本当に交換式バッテリーが欲しい。
最近はどうして何もかもこんなに微妙なのか分からない。
他の Android 端末は足元にも及ばない。
ハードウェアのメモリタグ付け対応は、Pixel の大きなセキュリティ上の利点の一つ。
公式のハードウェア要件一覧はこちら: https://grapheneos.org/faq#future-devices
この要件は第8世代 Pixel で完全に満たされている。
第6/第7世代 Pixel に欠けているのは MTE、BTI、PAC だけだが、MTE はハードウェア要件一覧の中で最も価値のある機能。
適切なセキュリティパッチの方が重要で、Pixel 以外では同じ形では提供されない。
Android には月次、四半期、年次のリリースがある。
他の Android OEM は、月次セキュリティバックポートのうち Critical/High 重大度の修正だけはすべて提供するが、個人情報保護に関する修正の大半を含む Moderate/Low 重大度の修正はほとんど提供しない。
代替 OS を使ってこうしたパッチを提供すれば一部は緩和できるが、その端末向けの代替 OS は多くの場合、さまざまな形でセキュリティを後退させる。
ファームウェアや多くの端末対応コードは、実際には OEM から来ている。
Android 12 のカーネル/ドライバの上で Android 14 QPR2 を動かすことは可能だが、OS の大きな部分に対するセキュリティ改善が欠けることになる。
Pixel のバッテリーは端末を損傷させずに交換するのが簡単とは言えないが、公式にサポートされ、公式部品もある: https://www.ifixit.com/Device/Google_Pixel
基本すら備えていない安全でない端末をサポートすることはできない。
私たちのハードウェア要件一覧には、大半の Android OEM が提供していない非常に基本的なものと、今では適切なセキュリティの基本要件と見なしている MTE のような高度な機能の両方が含まれている。
他の端末もサポートしたいが、それらの端末がこの要件を満たす必要がある。
メモリタグ付けは、標準の Cortex ARMv9 コアが対応している基本機能。
Qualcomm がこれを実装せず、対応 SoC を使う OEM も設定していないのは残念。
CPU アーキテクチャには機能があるのに、SoC や OEM のせいで使えないのは悲しいことだ。
それでも Pixel 8 は 7 年サポート予定なのでありがたい。
Apple も Samsung も信頼しにくく、Google がまだ最良ということになった。
GrapheneOSを使っている人に答えてほしい
ただし最近、妻とフロリダ州オーランドのDisney WorldとUniversal Studiosに行ったとき、サンドボックス化されたGoogle Play Servicesでアプリはおおむね動作したものの、厄介な問題があった
My Disney Experienceアプリは位置情報関連のエラーがかなりあり、重要な操作をしようとすると米国かカナダにいる必要があると断続的に表示され、妻のスマートフォンを回避策として使う必要があった
Universalアプリではアカウントにログインできなかったが、妻の標準のSamsung Galaxyではうまくいったので、GrapheneOSの問題に見えた
またGoogle Walletでクレジットカード決済をする場合、GoogleがGrapheneOSを認証していないためサポートされない
Walletの他の機能は動作する
Uberは問題なく動作する
それ以外ではプロプライエタリアプリは使っていない
主に自由/オープンソースアプリを使うつもりなら問題はないはず
ほとんどのプロプライエタリアプリはサンドボックス化されたGoogle Play Servicesで動作するが、その中に業務上非常に重要なアプリがあるなら、Universal StudiosやMy Disney Experienceで経験したような厄介な問題に遭遇する可能性がある
普通のUSBケーブルとLinuxのコマンドラインのadbを使ったが、推奨される方法はChromiumのWebUSBを使う方式で、非技術者にはその方が簡単なはず
ただし自分の場合はそれがうまくいかなかった
サンドボックス化されたGoogle Play Servicesがあるので、現代生活を耐えられるものにしてくれる各種プロプライエタリアプリをインストールできる一方で、Google Playがスマートフォン全体に無制限にアクセスすることはできず、両方の利点をうまく取っている
さらに隔離したければ別ユーザーを作り、Google Play関連の項目をそのアカウントで実行できる
少し試したが、個人的には毎回ユーザーを切り替えるのが面倒だった
スマートフォンがクラッシュしたことは一度もなく、銀行アプリも動作する
端末はPixel 6a
インストール済み端末を買うこともできるが、ほとんど誰でもWebインストーラーを使える
Android、ChromeOS、macOSでは特に簡単で、Windowsはドライバーのインストールが必要なので少し面倒
デスクトップLinuxではudevルールのインストールが必要で、ソフトウェアバージョンが固定された一部のディストリビューションには妨げになるバグのあるサービスがある
非技術者でも可能で、WebUSBをサポートするブラウザーさえあればよい
特別なソフトウェアは不要
日常使いでは、サンドボックス化されたGoogle Playを使えば標準のPixel OSとほぼ同じで、アプリ互換性もほぼ同じ
クラッシュを有意に多く経験する可能性は低い
標準OSにはないユーザー向けクラッシュ報告があるため、本来は気づかなかったクラッシュに気づくことはある
メモリ破損のあるバグの多いアプリは、アプリ別の互換モードを有効にするまでクラッシュすることがあり、特にユーザーがインストールしたアプリ全体にMTEを強制するよう選んだ場合はその可能性が高い
銀行アプリは、銀行がGoogle認証ではないOSを許可していれば動作し、今のところ大半は許可している
ただし銀行はGoogle認証ではないOSを徐々にブロックしつつあり、これは本質的には反競争的な規制問題として扱われるべき
その間、銀行にhttps://grapheneos.org/articles/attestation-compatibility-guideを使うよう説得している
追加のセキュリティ機能、サンドボックス設定、hardened memory allocatorのために少し遅い点は、素のAndroidを使ってデータアクセス要求にすべてOKを押すよりは手間がかかる
初期設定や、GrapheneOSが何が違うのか、セキュリティ機能をどう使うのかを理解するのにも少し時間がかかる
4年使う間にクラッシュはなかった
少なくともシステム全体のクラッシュはなく、何日もデバッグしなければならないようなクラッシュは、Pixelのハードウェアとソフトウェアが互いによく調整されているので、経験上起きない
銀行アプリはアプリ次第
Play Servicesなしでも動くアプリもあり、大半はサンドボックス化されたPlay Servicesで動作し、ごく一部だけがまったく動作しない
どの銀行を使っているか言えば、他のユーザーが動作可否を確認してくれる
難しいインストール方法でも、スマートフォンをUSBで接続し、電源/音量ボタンを少し押してから、ターミナルコマンドを2、3個コピー&ペーストする程度
簡単な方法は、スマートフォンをコンピューターに接続し、Chromeブラウザーで動くワンクリックインストールボタンを押すだけ
Google Pixel 6が出た直後からほぼずっと日常用OSとして使ってきたが、一度もクラッシュしたことはない
バグが出たり、デバッグ、修正、メンテナンスが必要になったこともない
試したすべてのアプリが素のAndroidと同じようにそのまま動作し、正直ほとんど違いを感じない
ときどき、これが元からスマートフォンに入っていたOSではないことさえ忘れる
個人的には銀行アプリのDiscoverは動作するが、他のアプリは確かなことはわからない
それでもGoogle Play servicesがあり、インストール後にブートローダーがロックされるので、おそらく大半は動作すると思う
2024年なら、seL4の精神を受け継ぎつつ、より厳格なレベルの形式検証済みオペレーティングシステム、アプリケーション、ツールが必要
今の時代に、軽くテストされただけの過剰設計のコードベースと脆弱で危険な言語をつなぎ合わせるのは、外国のアクターにハッキングされてユーザーが死ぬ可能性もあり、多くの厄介なバグやマルウェア・ハッキングの攻撃面を生み出すことになる
その上で、きれいに統合されたユーザー体験と実用的な機能も提供しなければならず、そうでなければエンジニアリングはすべて無駄になる
Qubes OSを見ればよい
まともなシングルボードコンピュータで Arm MTE を実装しているものはある? 最新の Raspberry Pi みたいなものだけど
RasPi 5 はクアッド A76 で v8.2 拡張を使っているが、MTE は v8.5
MTE は CHERI と比べるとどうなの?
MTE は潜在的なセキュリティバグを見つけるためのもので、本物の保護ではない
タグは 4 ビットしかなく、アプリケーションが偽造できるので、攻撃者が正しいタグを当てる必要があるとしても、ランダムに選んでも 1/16 の確率で当たる
MTE を適用すると、攻撃者でなくても、ときどき不正なアクセスが発生し、実際には悪い結果を生まないケースを検出できる、という考え方
典型的なバッファオーバーフローを考えると、バッファ末尾の直後にあるワードが別の用途に使われていなければ、実際には動作してしまうこともある
MTE はこうしたアクセスを検出し、武器化されたエクスプロイトになる前に調査してパッチできるようにしてくれる
ただし 予約タグ によって、強い決定的なセキュリティ特性を提供できる
タグ付けされていないものはタグ 0 で、タグ付けされたものはデフォルトの除外により基本的に 0 以外のタグを持つ
他のタグも命令で静的・動的に除外できるが、解放済みメモリのような内部用途にタグ 0 を使うと、どのタグ付きポインタからもアクセスできないという事実を利用できる
hardened_malloc では、割り当てスロットについて隣接タグと以前使ったタグを動的に除外している
これにより、線形オーバーフローと小さなオーバーフローに対して決定的な保護を提供する
use-after-free の場合、解放済みの割り当てを指すポインタは、解放中や再割り当て直後にはアクセスできず、次に再び割り当てられるまで待つ必要があり、そのとき正しいタグである確率は 1/15
これは hardened_malloc の他のセキュリティ特性とうまく組み合わさる
slab 割り当てと仮想メモリに FIFO/ランダムの隔離領域を設け、再利用をさらに遅らせ、決定的でなくしている
128k を超えるまでは、異なる割り当てサイズクラス間でメモリ位置を絶対に再利用せず、各クラスは別々の領域にあり、メタデータはすべてさらに別の予約領域にある
一般的なケースでは、MTE は現在 4 ビットしかないため、回避確率は約 1/15
8 ビット使用のサポートへ簡単に拡張でき、PAC を使わないなら他の空きビットもある
理論上、一般的な 39 ビットアドレス空間では、48 ビット以上のアドレス空間に対して最大 16 ビットの MTE もサポートできる
現在は 4 ビットに固定されているが、ECC パリティメモリに追加ビットを保存できるようにするため、8 ビットではなくそれを選んだと聞いた
ただし CHERI ほど信頼性高く検出できるわけではない
タグに対する保護がなく、タグ空間も小さい(2^4)
メインストリームのハードウェアが 2015 年の Solaris SPARC やそれ以前の メモリタグアーキテクチャ に追いつき、最終的にメモリ破損問題を制御できる日が待ち遠しい
もちろん、その問題は腕の悪い開発者だけが作るものだ、というふうに片づけられがちだけど
これは「腕の悪い開発者」の問題ではなく、全員の問題
メモリ破損は事実上 C/C++ の言語機能に近い
愚かな人たちのせいだという信念を広めないほうがいい
自分を愚かだと思っている人はほとんどいないし、本当に優秀なコーダーたちもおかしなメモリバグを作るのを見てきた
単にそれらの言語の領域に含まれることであり、「もし」ではなく「いつ」の問題
Android が Bluetooth コードのかなりの部分を Rust に移行しており、残りのコードも Rust に移すためにもっとリソースを投入すべき理由を示している、という一文がさりげなく入っているのが気に入った
C と C++ は何年も使ってきたが Rust の経験はないので、C から Rust へポーティングするときにどれくらいリファクタリングが必要なのか気になる
質問を分けると、1. C は Rust にどれくらい直接翻訳できるのか? Rust は構造の再整理やリファクタリングを要求するのか?
2. Google はこれにどう取り組んでいるのか? できるだけ近い形で「翻訳」しようとしているのか、それとも大規模な書き直し・リファクタリングの機会と見ているのか気になる
Android Bluetooth スタックがいつか標準的な Linux ディストリビューションのデスクトップシステムで使えるようになるのかも気になる
循環参照が多く、モジュール同士がコールバック付きの「シグナルバス」を通じて通信する構造で、Rust が助けになるというより格闘しなければならない感じが強かった
C++ ではデータをインラインに保存できていた場所にも、ヒープポインタである Box がたくさん必要だった
結論として、単純なコマンドラインツールやリクエスト・レスポンス構造なら、Rust への移植はおそらく簡単
より一般的に、コード構造が アリーナ割り当て と相性が良いなら、参照にライフタイムタグを付ける形へ移すのは大きな問題ではない
しかし C プログラマ流に循環参照のある、認めざるを得ないが醜いコードを書いているなら、Rust は上り坂のように感じられ、そこから始めるべき場所ではない
まず C++ コードの構造を変えて、所有権を共通の親へ移す必要がある
そうすれば良くなると思う
Rust を繰り返しさらに学びつつ、コードが Rust とよりよく対応するようになるまでは C++ で作業を続けるつもり
書いているもののかなりの部分を再アーキテクチャ化する必要があるだろう
Rust が メモリ安全性 を保証する仕組みのため、これは避けられない