Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WebAssembly in Android Apps 〜 WASMはJNIの夢を見るか

WebAssembly in Android Apps 〜 WASMはJNIの夢を見るか

画像デコーダーなどの実績あるC/C++ライブラリをAndroidアプリに組み込む際、従来のJNI(Java Native Interface)はバッファオーバーフローやRCE(任意コード実行)といった深刻なセキュリティリスクを孕んでいます。しかし、巨大なコーデックをメモリ安全な言語でゼロから再実装することは、コストの観点から極めて困難です。

本セッションでは、その現実的な解決策としてWebAssembly(WASM)を取り上げます。Jetpack JavaScriptEngine を用いて隔離されたV8サンドボックス内でWASMを動作させ、多層防御アーキテクチャ、Binderの1MB制限に起因するプロセス間データ転送(IPC)のボトルネック(JSArray、Base64Url、Base85、Base32768のベンチマーク比較)、そして JavaScriptEngine 1.1.0 における MessagePort(ArrayBuffer直接転送)やファイルディスクリプタ(FD)を用いたパフォーマンス最適化について検証します。

Avatar for ARIYAMA Keiji

ARIYAMA Keiji

September 05, 2026

More Decks by ARIYAMA Keiji

Other Decks in Programming

Transcript

  1. OpenJPEGで報告された脆弱性 Threat Level Explanation CWE-457: Use of Uninitialized Variable Critical

    OpenJPEG 2.5.1〜2.5.3 で、opj_jp2_read_header 呼び出し時にデータストリーム p_stream が短く p_image が未初期化だと OOB (Out-of-Bounds / メモリ境界外)ヒープ書き込みが発生。NIST 評価の CVSS 3.1 スコアは 9.8。CISA の SSVC で technicalImpact=total・exploitation=poc と評されており、RCE の蓋然性が高い CVE202550952 CWE-476: NULL Pointer Dereference Medium OpenJPEG 2.5.0 で、/openjp2/dwt.c 経由の NULL ポインタ参照。無効な JP2 画像を処理すると即クラッシュし得るサービス拒 否。CISA-ADP の CVSS v3.1 6.5(中程度 Medium)、SSVC technicalImpact=partial CVE20208112 CWE-787: Out-ofbounds Write High OpenJPEG 2.3.1〜2020-01-28 の opj_t1_clbl_decode_processor(openjp2/t1.c の qmfbid==1 分岐)にヒープバッファオーバーフ ロー。悪意をもって作成された JP2 画像を解析する段階でヒープ領域を破壊し、DoS(クラッシュ)または任意コード実行(RCE) に至りうる。Oracle・Debian・Fedora・Red Hat 各ディストロの CPU/errata でも公開・修正されている高重要度の脆弱性 CVE20206851 CWE-787: Out-ofbounds Write(関連 CVE) High OpenJPEG 2.3.1 以下で、opj_j2k_update_image_dimensions の検証不足により opj_t1_clbl_decode_processor でヒープオーバー フロー発生。CVE-2020-8112 とは別 bug だが同関数領域。DoS の確実な導出、特定条件下でコード実行可能性 CVE※ Vulnerability Type CVE202554874 ※ CVE: Common Vulnerabilities and Exposures © 2026 ARIYAMA Keiji. All rights reserved.
  2. WASM(WebAssembly)とは ブラウザ上のマルチプラットフォームな実行環境。 機械語にコンパイルされたプログラムを実行する仕 組み。 W3C(World Wide Web Consortium)の WebAssembly Community

    Group による規格。 2017年 初版公開(主要なブラウザでのサポート開 始)。 © 2026 ARIYAMA Keiji. All rights reserved. WebAssembly logo by Carlos Baraza / CC0 1.0
  3. JPEG2000 Decoder for Android Android 向けに JPEG2000 画像をデコードするライブラリ。 OpenJPEG を

    WebAssembly 化して Jetpack JavaScriptEngine 内で実行し、隔離環境下で安全にデコード する。 JNIでの直接実行とくらべて脆弱性の範囲を限定して、安全に動作させることを目指している。 https://github.com/keiji/jp2k-decoder-android/tree/feature/droidkaigi2026 © 2026 ARIYAMA Keiji. All rights reserved.
  4. WASMバイナリの作成: C言語による実装 hello-wasm.c #include <stdio.h> char* hello_wasm(const char* name) {

    static char buffer[64]; sprintf(buffer, "Hello, %s!", name); return buffer; } © 2026 ARIYAMA Keiji. All rights reserved.
  5. WASMバイナリの作成: Emscriptenによるコンパイル Emscripten(emcc)で hello.wasm にコンパイル。 emcc hello-wasm.c -o hello.wasm \

    -s STANDALONE_WASM=1 \ -s EXPORTED_FUNCTIONS="['_hello_wasm', '_malloc', '_free']" \ --no-entry \ -O3 _hello_wasm, _malloc, _free © 2026 ARIYAMA Keiji. All rights reserved. 3つの関数を外部にエクスポート。
  6. JavaScriptの構築 private fun createHelloWasmJsCode(wasmBytes: ByteArray, name: String): String { val

    base64Wasm = Base64.encodeToString(wasmBytes, Base64.URL_SAFE or Base64.NO_WRAP) val nameBytes = (name + "\u0000").toByteArray(Charsets.US_ASCII) val base64Name = Base64.encodeToString( nameBytes, Base64.URL_SAFE or Base64.NO_WRAP ) // ... © 2026 ARIYAMA Keiji. All rights reserved.
  7. WASMのロード: WebAssembly.instantiate // ... return """ async function run() {

    const bytes = Uint8Array.fromBase64("$base64Wasm", { alphabet: "base64url" }) const importObject = { wasi_snapshot_preview1: { proc_exit: () => {}, fd_write: () => 0, fd_close: () => 0, fd_seek: () => 0, } }; const { instance } = await WebAssembly.instantiate(bytes, importObject); // 略 } run(); © 2026 ARIYAMA Keiji. All rights reserved.
  8. """.trimIndent() WASM関数の実行: メモリ割り当てと呼び出し } private fun createHelloWasmJsCode(wasmBytes: ByteArray, name: String):

    String { ... const nameBytes = Uint8Array.fromBase64("$base64Name", { alphabet: "base64url" }); const namePtr = exports.malloc(nameBytes.length); const memArray = new Uint8Array(memory.buffer); memArray.set(nameBytes, namePtr); const resultPtr = exports.hello_wasm(namePtr); let endPtr = resultPtr; while (memArray[endPtr] !== 0) { endPtr++; } © 2026 ARIYAMA Keiji. All rights reserved.
  9. WASM関数の実行: 結果の取得とメモリ解放 const resultBytes = memArray.subarray(resultPtr, endPtr); const resultBase64 =

    resultBytes.toBase64({ alphabet: "base64url" }); exports.free(namePtr); return resultBase64; } © 2026 ARIYAMA Keiji. All rights reserved.
  10. WASM関数の実行: JavaScriptの評価 val jsCode = createHelloWasmJsCode(wasmBytes, name) val jsResult =

    isolate.evaluateJavaScriptAsync(jsCode).await() createHelloWasmJsCode(wasmBytes, "Keiji") jsResult = "Hello, Keiji!" © 2026 ARIYAMA Keiji. All rights reserved.
  11. JavaScriptEngine JsSandboxService0 は WebView 実装が提供する アプリ専用の隔離プロセス(BIND_EXTERNAL_SERVICE で宣言・バインド)。 アプリプロセスとは別プロセスで走るが、当該 App 単一インスタンスで、Binder(IPC)越しにのみ通信す

    る。 アプリプロセス / クライアント JsSandboxService0(アプリ専用隔離プロセス・単一インスタンス) ListenableFuture(.await で文字列結果を返却) アプリ (WasmManager) JavaScriptSandbox JavaScriptIsolate evaluateJavaScriptAsync(jsCode) = AIDL IPC bindService(BIND_EXTERNAL_SERVICE) 直接バインド = 隔離プロセスを起動 Isolate(JS 実行・専用ヒープ) AIDL / IJsSandboxIsolate 外部パッケージ: org.chromium (提供元) org.chromium WebView 実装 © 2026 ARIYAMA Keiji. All rights reserved. JsSandboxService0 提供 入力 / 出力
  12. evaluateJavaScriptAsyncによる入力 実行するJavaScriptの中に入力データを含める。 private fun createHelloWasmJsCode(wasmBytes: ByteArray, name: String): String {

    return """ async function run() { const bytes = Uint8Array.fromBase64("$base64Wasm", { alphabet: "base64url" }) const nameBytes = Uint8Array.fromBase64("$base64Name", { alphabet: "base64url" }); ... """.trimIndent() val jsCode = createHelloWasmJsCode(wasmBytes) val jsResult = isolate.evaluateJavaScriptAsync(jsCode).await() 入力も出力も文字列。バイナリを文字列にエンコード・デコードするコストがかかる。 © 2026 ARIYAMA Keiji. All rights reserved.
  13. データ転送方式の比較 方式 入力 文字列 バイナリ evaluateJavaScriptAsync provideNamedData 出力 文字列 文字列

    Feature JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER provideNamedData は入力時にのみ機能する。実行結果は evaluateJavaScriptAsync を通じて文字列 provideNamedData はサポートしていない可能性がある。機能のサポート判定が必要。 で受け取る。 © 2026 ARIYAMA Keiji. All rights reserved.
  14. サポート判定 val supported = JavaScriptSandbox.isFeatureSupported( JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER ) if (supported) {

    // バイトデータの入力に対応している } else { // 未対応 } © 2026 ARIYAMA Keiji. All rights reserved.
  15. サンプルデータ 方式 データ形式 サイズ チャンネル ファイルサイズ Bitmapサイズ JPEG2000 Decoder for

    Android (https://github.com/keiji/jp2k-decoder-android) © 2026 ARIYAMA Keiji. All rights reserved. 入力 JPEG2000 640x480 3ch 147,041 bytes 1,228,854 bytes
  16. JSArray Byte配列をJavaScriptの中に直接配列として記述する JavaScript中に展開すると文法上の配列として取り扱われるので、デコードの必要がない [66,77,54,192,18,0,0,0,0,0,54,0,0,0,40,0,0,0,128,2,0,0,32,254,25 5,255,1,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2 ,3,4,255,5,6,5,255,9,10,7,255,9,8,7,255,9,5,7,255,8,4,6,255,7,5, 6,255,9,7,8,255,11,10,11,255,10,10,12,255,17,18,19,255,31,33,35, ... (truncated 70014

    lines) ... 5,115,101,99,255,135,115,119,255,140,116,126,255,50,34,43,255,24 ,19,24,255,22,21,23,255,26,27,28,255,30,32,32,255,32,35,36,255,3 1,31,34,255,34,32,36,255,32,37,34,255,49,59,51,255,127,132,130,2 55,112,112,117,255,100,99,106,255,92,91,98,255,84,83,91,255,72,7 2,78,255,42,42,47,255,35,35,40,255,37,37,41,255] © 2026 ARIYAMA Keiji. All rights reserved.
  17. JSArray: 結果 1,228,854 バイトのデータが 4,481,456 文字(約364%) Kotlin側のデコードが大きい 全体のイメージのデコードにかかった時間が方式の中でもっともも大きい Output encoded

    content length: 4481456 chars Output data length: 1228854 bytes Input transfer start delay (Kotlin -> JS start): 4.00 ms Output transfer delay (JS finish -> Kotlin receive): 47.00 ms Output Kotlin decode time: 157.80 ms Performance: inputSize=0B totalTime=889ms dataTransferTime=219ms jsDecodeTime=0ms jsEncodeTime=58ms wasmHeapSize=7MB outputImage=1228854B Pre-process: 0.0 ms, WASM: 113.0 ms, Post-process: 58.0 ms decodeImage() finished in 890 msec © 2026 ARIYAMA Keiji. All rights reserved.
  18. Base64/Base64Url バイナリを ASCII(American Standard Code for Information Interchange / 標準情報交換用

    7 ビット文字コード)・ 印刷可能なコードポイントに割り当てる JavaScriptEngineはbtoa/atobには非対応(bytes.toBase64/Uint8Array.fromBase64) Qk02wBIAAAAAADYAAAAoAAAAgAIAACD-__8BACAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAgME_wUGBf8JCgf_CQgH_wkFB_8IBAb_BwUG_wkHCP8LCgv_CgoM_xES E_8fISP_IyUn_ygpLP8sKy__MTEy_zc3Nf85Njn_QDtC_0A9Qf86Oj3_PkE-_zo0 VP-Chqj_o7LS_7DB1_-3yNL_ssbN_6q_xv-ovsP_oLa5_4yipf-LoKL_h5me_3mI ... (truncated 25593 lines) ... hP99fH__eX6F_3p-hv9-f4j_fn6E_3t5fv92b3v_d2x-_3Vqd_9zanD_cWxv_2xq a_9sa3D_a2ly_3Ftdv90bnX_e3J4_4F2e_-Adnv_fnV7_3Bob_93cHf_f3d9_3xy eP9_c3j_f3F1_3NlZf9zZWP_h3N3_4x0fv8yIiv_GBMY_xYVF_8aGxz_HiAg_yAj JP8fHyL_IiAk_yAlIv8xOzP_f4SC_3Bwdf9kY2r_XFti_1RTW_9ISE7_Kiov_yMj KP8lJSn_ © 2026 ARIYAMA Keiji. All rights reserved.
  19. Base64/Base64Url: 結果 1,228,854 バイトのデータが 1,638,472 文字(約133%) Base64 系(Base64Url)で目立った差はなかった Output encoded

    content length: 1638472 chars Output data length: 1228854 bytes Input transfer start delay (Kotlin -> JS start): 2.00 ms Output transfer delay (JS finish -> Kotlin receive): 16.00 ms Output Kotlin decode time: 35.04 ms Performance: inputSize=0B totalTime=513ms dataTransferTime=179ms jsDecodeTime=0ms jsEncodeTime=64ms wasmHeapSize=7MB outputImage=1228854B Pre-process: 0.0 ms, WASM: 97.0 ms, Post-process: 64.0 ms decodeImage() finished in 514 msec © 2026 ARIYAMA Keiji. All rights reserved.
  20. Base85 4バイトのバイナリデータを符号化し5文字のASCIIコードを用いて文字を表す 派生形がPDFで利用されている(Ascii85) lqowN5&!@i001@S001zE004JJ001ev|nJ91aohxw000000000000000000000000 0000651PFSf1^)>o2MC8o2MC8l2MC5j2la^i2la~m2>%nt3;ZLv4f3@L6Aoc}bMw ~mc;UOzeDmmKfAi$Ygxfo>hubV_iSzualJoqllh{~bj^WYikl0V2rr4M8Sq;S~(* lL**z;t3(*lR?&hT*U#{A0K!~DyrX&mR!Rt>EDQw_1pP8T7>J{KDZI~NHlBn><=8 ... (truncated 23993

    lines) ... cVG#u+WGA3+!G#uEFFb$)yH5VEFHw~ZLI2R_MG#uyAE*BOfD;FqaE*BL9CMh#0Aq |^^z~TS+yYwi(Aq|Y*A{Qb{Ck>V4B^M*eC>JtrD;FRtD;FLqD;F4~z~U32CMiepE GaXnC>JnmC>JnkB^MI^w&D+!wb-s9CMiRzE*zeEeb_C182=u(7YLx?9rd65aPAMe b>X{dbl5;fb>X~kbl6wVgYI{TG8Y)eB^L~Zyx4Kyv*GzatMmk%px8(@fAimwde}O tdGo © 2026 ARIYAMA Keiji. All rights reserved.
  21. Base85: 結果 1,228,854 バイトのデータが 1,536,068 文字(約125%) Base64に比べて文字数が少ないにもかかわらず、デコードにかかった時間は大きくなった Output encoded content

    length: 1536068 Output data length: 1228854 bytes Input transfer start delay (Kotlin -> JS start): 4.00 ms Output transfer delay (JS finish -> Kotlin receive): 14.00 ms Output Kotlin decode time: 119.40 ms Performance: inputSize=0B totalTime=622ms dataTransferTime=199ms jsDecodeTime=0ms jsEncodeTime=74ms wasmHeapSize=7MB outputImage=1228854B Pre-process: 0.0 ms, WASM: 110.0 ms, Post-process: 74.0 ms decodeImage() finished in 623 msec © 2026 ARIYAMA Keiji. All rights reserved.
  22. Base32768 とは: エンコードデータの例 嘦芰㙀㐀㐀㓘㐀㐨㐀㐠㑀㐂㯷샼㘀唀㐀㐀㐀㐀㐀㐀㐀㐀㐀㐀㐀㐀㐀㰌㷾㤆㛿睂痿놐甿봤㸏쀈㘃瓁閠냰紸壼䨔㿿㤅㜿霢嘿맹㢏뽆娧삔㹋哥瞲샹秄髾氷 俿荍簿딃輗븀뮃쀺刞샏紧냳蚢裾㤍巿蛙榿뜘剿뺾塋쁥篍샕擱铵㿬哽㛚듿䂢蟿韨䡟륹臷뻦䔝쁻祅샚掮瓱䓐飼䀔㷿㜃㗿陡㒿릈浇뼼償삔㶊铩뗵䯺䤙䏾㸌 㧿甀甿녀唯봘㸑쀋㠆샃㔁铰鵈泼䨔㻿㢅睿阡族륈敇뼒㼑삄矄員斱䏸椰嫾䄌㱿硂雿뇰长뵀伟쀏㪇瓃皁샰륨磼匜䛿㨆㟿陁旟률椿뼜䐒삈㡅哢䔑瓸組棾䜑 䅿禄㛿눑㒧뵀唣쀐㰇샄坂哱䒠蓼唢䫿㬇㨿雡陯릀煗뼢䈘삉碆铢嗱瓸祀峾䘒㽿秄砟눑㒯뵀儡쀏㬇샄㗢䏱㱸糼崦䫿㶌糿馦墏먹緣뼦丟삇㠆哢䔁賸礼烾䘑 ... (truncated 10232

    lines) ... 歿诙承뛦恟빵痏쁛抲瓖㼬蓵枻䏽掲鯿崩赿鷉媟뭚癿뾜襟삥䣖瓩㤕泺䔱峾硊忿薑鿟딄得뷽㺟쀼嘤샎鴨샳廒㟼魮狿刟蘿鮇夏몁跳뽜椻삔膍샨䣅㯺㴝哾籆 广蘑齟뒤䙟븕冩쁃媨瓔後蓳닪都䢐釿妣詿鹉䥯뮂癷뾪豠삩誗瓩觵铺㤍廾穊惿衕肿뗥矯빉毁쁏怰瓖㾭蓵濫䯽淆髿昳避긍屯밓䗃뾼陬삶僞铯泸蓼㸚囿㦈 繿闡닿뤨楧뼂㤏삄睄瓠䍰哷겣볾㛳㟿础垿넯赯봼䨳쀍㦉샂阡铰崸峼㘊㯾삀睿钟斿룗饏뻽받쁻滁샢䎑哸礄壾㴃㣿磁闿뉐赏볫닿뿲뾅삽咡铯鳸系뫺㷾뱹 瑿銛끿롶飷뻕觯쁳樸瓜抍샶顓擽跖뇿檴醿꼭豯벣滗뿶델샀銞샰㭧铻뫕룾녨泿鋜䋿룷烯뻱駱쁿溼瓟꼮铷倫䯽鯊飿碹鋿늎糯몑㲯뼰䜘삋㥅샣嚱鳸릀盾唣 䙿糇饟댢㔧부罅쀱劙샟놐擷㞃賽緆鿿挭趿龊檿뭂嘻뽔弯삑緊哤蝒蓸℉ © 2026 ARIYAMA Keiji. All rights reserved.
  23. Base32768 - 結果 1,228,854 バイトのデータが 655,390 文字(=1,310,782 バイト, 約106%) Kotlinでのデコード時間が

    185.79ms と、もっとも長い Output encoded content length: 655390 Output data length: 1228854 bytes Input transfer start delay (Kotlin -> JS start): 1.00 ms Output transfer delay (JS finish -> Kotlin receive): 16.00 ms Output Kotlin decode time: 185.79 ms Performance: inputSize=0B totalTime=539ms dataTransferTime=188ms jsDecodeTime=0ms jsEncodeTime=67ms wasmHeapSize=7MB outputImage=1228854B Pre-process: 0.0 ms, WASM: 104.0 ms, Post-process: 67.0 ms decodeImage() finished in 540 msec © 2026 ARIYAMA Keiji. All rights reserved.
  24. 変換後の文字数 文字列に変換後の文字数 (chars) 4,500,000 4,000,000 3,500,000 3,000,000 )srahc( 数字文 2,500,000

    2,000,000 1,500,000 1,000,000 500,000 0 © 2026 ARIYAMA Keiji. All rights reserved. JsArray Base64Url Base85 DataChannel 方式 Base32768
  25. JavaScript側のエンコードにかかる時間 JS エンコード処理時間 (ms) 80 70 60 50 )sm( 間時

    40 30 20 10 0 © 2026 ARIYAMA Keiji. All rights reserved. JsArray Base64Url Base85 DataChannel 方式 Base32768
  26. Kotlin側のデコードにかかる時間 Kotlin デコード処理時間 (ms) 200 180 160 140 )sm( 間時

    120 100 80 60 40 20 0 © 2026 ARIYAMA Keiji. All rights reserved. JsArray Base64Url Base85 DataChannel 方式 Base32768
  27. MessagePort API WebViewとアプリのやりとりを双方向 ByteArray で行う。 Kotlin val client = MessagePortClient

    { message -> if (message.type == Message.TYPE_ARRAY_BUFFER) { val bytes = message.arrayBuffer messageQueue.offer(bytes) } } val messagePort = isolate.createMessageChannel("jp2k_binary_port", executor, client) messagePort.postMessage(Message.createArrayBufferMessage(wasmBytes)) JavaScript globalThis.receiveBinaryMessage() © 2026 ARIYAMA Keiji. All rights reserved.
  28. デコード速度 全体の総デコード時間比較 (ms) 900 800 700 )sm( 間時ドーコデ総 600 500

    400 300 200 100 0 © 2026 ARIYAMA Keiji. All rights reserved. JsArray Base64Url Base85 方式 Base32768 MessagePort
  29. データ転送方式の比較 方式 evaluateJavaScriptAsync provideNamedData MessagePort 入力 文字列 バイナリ バイナリ 出力

    文字列 文字列 バイナリ Feature JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER JS_FEATURE_MESSAGE_PORTS 対応していないケースに備えて、引き続き文字列でデータを取り扱うフォールバックが必要。 © 2026 ARIYAMA Keiji. All rights reserved.
  30. 主要なFeatureとその役割 Feature名 JS_FEATURE_WASM_COMPILATION JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER JS_FEATURE_EVALUATE_WITHOUT_TRANSACTION_LIMIT JS_FEATURE_ISOLATE_MAX_HEAP_SIZE JS_FEATURE_PROMISE_RETURN JS_FEATURE_CONSOLE_MESSAGING JS_FEATURE_MESSAGE_PORTS JS_FEATURE_EVALUATE_FROM_FD

    JS_FEATURE_ISOLATE_TERMINATION © 2026 ARIYAMA Keiji. All rights reserved. 有効化される機能・API WASMバイナリのロードとコンパイル provideNamedDataによるバイナリ直接転送 evaluateJavaScriptAsync による入力・戻り値のトランザクション制限 超え Isolateごとの最大ヒープサイズの指定 evaluateJavaScriptAsync からの Promise の戻り値処理 console.log などのコンソール出力のフック MessagePortを用いた双方向通信 ファイルディスクリプタからの直接スクリプト評価 実行中のIsolateの強制終了 備考・未サポート時の影響 WASM実行不可 未サポート時は代替経路へフォールバック 未サポート時は入出力の巨大データでクラッ シュ メモリ枯渇によるクラッシュを防ぐ 非同期処理のハンドリングに必須 デバッグ用途 javascriptengine 1.1.0で追加 巨大ファイルの処理に有効 無限ループやタイムアウト時の安全な破棄
  31. Graceful Degradation 機能が使えないブラウザのためにGraceful Degradation(段階的機能低下)の適用が望ましい。 Yes Yes スタート JS_FEATURE_MESSAGE_PORTS JS_FEATURE_WASM_COMPILATION No

    No © 2026 ARIYAMA Keiji. All rights reserved. MessagePort API WASM非対応 Yes provideNamedData No 文字列での入出力(Base64Url) JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER
  32. FD(File Descriptor)による 1MB 制限回避 App / WebView / JsSandboxService0 外部パッケージ:

    org.chromium (提供元) org.chromium WebView 実装 JsSandboxService0(アプリ専用隔離プロセス・単一インスタンス) JsSandboxService0 提供 AIDL / IJsSandboxService アプリプロセス / クライアント JavaScript 実行 FD で受取 / 返却 bindService(BIND_EXTERNAL_SERVICE) 直接バインド = 隔離プロセスを起動 アプリ (WasmManager) JavaScriptSandbox カーネル pipe / RAM JavaScriptIsolate FD で受取 / 返却 (双方向・1MB 超でも通過) anonymous pipe (createPipe) FD(File Descriptor) は Parcel を経由して binder 越しに渡る。ペイロードは pipe(RAM) =1MB の制限がない。 © 2026 ARIYAMA Keiji. All rights reserved.
  33. 各方式と FD(FileDescriptor) 方式 出力 文字列 fd 化の切替 Feature feature で全件

    pipe EVALUATE_WITHOUT_TRANSACTION_LIMIT - MessagePort 入力 文字列 バイナリ バイナリ evaluateJavaScriptAsync(FD) FD FD 常に pipe ≤64KiB=binder / >64KiB=FD 元から FD evaluateJavaScriptAsync provideNamedData バイナリ PROVIDE_CONSUME_ARRAY_BUFFER MESSAGE_PORTS EVALUATE_FROM_FD は feature に応じてデータのやり取りをFD経由に自動切り替え MessagePort はサイズに応じて切り替え binder は FD(Parcel)で参照を渡す ことで 1MB 上限回避 evaluateJavaScriptAsync © 2026 ARIYAMA Keiji. All rights reserved.
  34. セキュリティ: FD 化データの可読性 FD 化されるデータは、anonymous pipe(RAM) に載る。ディスク上のファイルには保存されない。 保護機構 UID 分離

    SELinux ptrace_scope 匿名 pipe 効く範囲 別 app が pipe FD を読めない 別 app が sandbox プロセスを触れない 別 app の /proc/<pid>/fd アクセスを制限 第三者が fd を共有していない ただし、データは平文。 保護は「誰が fd / メモリに触れるか」のアクセス制御であり暗号化で はない。 © 2026 ARIYAMA Keiji. All rights reserved.
  35. JavaScriptEngineの進化と最適化 Graceful Degradation(段階的機能低下)の実装 環境(WebViewのバージョン)によって利用可能な機能が異なる。 サポートしているFeatureを確認しながら、MessagePort → provideNamedData → 文字列(Base64)へと安 全にフォールバックする設計が不可欠である。

    セキュリティ要件との調整 アプリと隔離プロセスとの間でどのようにデータが送受信されるか。攻撃のサーフェスはどこかを常に確認、 適切な対応を検討する必要がある。 © 2026 ARIYAMA Keiji. All rights reserved.