はじめに
はじめまして。2019年3月に入社しました、yosuke2です。
今回はMacアプリの公証(Notarization)に関してハマった経験があり、ネットにもそんなに出てはいないので貴重な知見かなと思ったのでこれをまとめました。
ちなみにXcodeはGUI部分はほぼ使用せず、ほとんどの内容をコマンドラインで行っているため、その辺りはGUIで作業している人は適宜読み替えていただければと思います(Hardened Runtimeの設定をコマンドラインで行っていますがGUIでもできる、などがあるので)。
そもそもMacアプリの公証とは
簡単に言うと、セキュリティを強化してユーザがより安心できるような仕組みをAppleが追加したので、開発者はこの仕組みを使ってAppleの認証を得て安全なアプリを公開しよう、というものです。
2019年5月13日リリースのmacOS Mojave(10.14.5)にてカーネル拡張のみ必須対応になりましたが、通常のアプリなど他の項目に関しては次期メジャーリリースとなるmacOS Catalina(10.15)にて必須対応になるようです。
JITを使うようなMacアプリを公証する際に気を付けるべきこと
そんな公証の対応を業務で行うことになり、macを碌に触ったことのなかった私は分からないことの方が多い状態の中頑張って調べて進めてを繰り返し、最後の関門としてこのJITが原因のエラーが現れました。
JITとは
Just In Timeの略で、この記事の中では「QtWebEngine」の中で使用されている「Chromium」の「V8」というJavaScript実行エンジンの、更に中で使用されているJITコンパイラのことを指します。
ややこしいですね。階層で表すとこんな感じです。階層の深い方が浅い方に内包されています。
- 公証の対象となったアプリ
- QtWebEngine
- Chromium
- V8
- JITコンパイラ
- V8
- Chromium
- QtWebEngine
この階層の1番深いとこにあるJITコンパイラが今回問題となった部分です。
結論から言うと、com.apple.security.cs.allow-unsigned-executable-memory
のkeyを署名時に有効にすることで本問題は解決しました。
keyとは
Hardened Runtimeの設定項目のことで、下記URLに一覧が記載されています。
Hardened Runtimeとは
公証をパスするために必ず有効にしなくてはならないセキュリティの機能で、アプリ毎に合った設定を付与する必要があります。
解決までの道のり
以下は解決までの道のりになります。
発生していたエラー内容としてはBus error: 10
というもので、調べるとメモリ関連のエラーだそうです。
公証時にはHardened Runtime
という設定をアプリ内の実行可能バイナリ全てに付与する必要があるのですが、設定ファイルを用意してアプリ内のバイナリ署名時にそのファイルを指定することで設定を付与できます。
この設定付与を行うようにしてから、署名前は普通に動いていたアプリが署名をすると実行時にこのエラーを出して強制終了し始めました。
そのためHardened Runtime
の設定が適切でないのだろうと考え、Hardened Runtime
とメモリに何か問題があるのだと判断しました。
また、元々署名時には--deep
オプションを使っていたのですが、codesign
の--deep
オプションは特定のディレクトリにしか再帰的署名を行わず、元々あった実行可能バイナリに署名漏れがありました。
このバイナリはQtフレームワークに関するバイナリでQtWebEngineに関わっており、QtWebEngineは内部でJITを使用しているそうです。
この署名漏れの対象であるバイナリを署名しなければ実行時エラーは発生しなかったため、このバイナリに何か原因があるのだと判断しました。
これらを手掛かりに調べてもなかなか似た事例が見つからなかったのですが、Qtのフォーラムにて調べるとそれらしき情報が見つかりました。
上記URLの1番下のコメントにあるように、com.apple.security.cs.disable-executable-page-protection
のkeyを署名時に有効にすることで確かに実行できるようになりました。
ですが、メモリの保護を無効にするものなので本当にこれで良いのだろうか?と疑問に思い、他にも有効にすることで解決できるkeyがないか当てをつけて試したところcom.apple.security.cs.allow-unsigned-executable-memory
を有効にすると解決しました。
com.apple.security.cs.allow-unsigned-executable-memory
の具体的な内容は名前の通りで、未署名の実行可能メモリを有効にすることです。
上記URLにあるこの設定の説明を読むと、MAP_JIT
フラグを使用せずにアプリが書き込み可能および実行可能メモリを作成できるかどうかを示すブール値、とのことです。
同様にJITに関する設定で、com.apple.security.cs.allow-jit
という設定があります。
こちらは先ほどのkeyとは逆に、MAP_JIT
フラグを使用した上でアプリが書き込み可能および実行可能メモリを作成できるかどうかを示すブール値、となります。
これでも出来そうと言えば出来そうなのですが、これを有効にしても出来ませんでした。
そこでそもそもMAP_JIT
とは何なのだろうかと思い調べました。
ですが、明確にこういったものだといった説明は出てこず、分かったのはmmapに使用されるmacOS 10.14
で追加されたらしいMac特有のフラグだろうということです。
10.14となるとリリース日は2018年9月24日であり、今回公証の対象となったアプリで使用しているQtWebEngineのバージョンはそれよりも古いため、このMAP_JIT
フラグには対応していない。
そのためMAP_JIT
を使用するcom.apple.security.cs.allow-jit
を有効にしても意味がなく、MAP_JIT
を使用しないcom.apple.security.cs.allow-unsigned-executable-memory
を有効にすることで正しく動作するようになったようです。
まとめ
com.apple.security.cs.allow-unsigned-executable-memory
で解決するよ、終わり。という訳にはいかないので、解決までの道のりをざっと書いてみましたが思ったより長くなってしまいました。
簡単にまとめると、MacアプリでHardened Runtimeの設定を行った結果としてBus error: 10
などのメモリ関連のエラーが出て、なおかつアプリ内でJITコンパイラを使用している場合は、Hardened Runtimeの設定で解決できる場合がある、ということです。
今回は以上になります。読んでいただきありがとうございました。
今回のフェア企画中にもう一つ何か書こうと考えているので、そちらももし良ければ読んでいただけると嬉しいです。