Crashpadシグネチャを読み解き、緩和策を選択する

概要

  • Playwrightのヘッドレス実行、Playwrightのヘッド付き実行、依存関係のない生のCDP実行で得られたCrashpadダンプは、いずれもEdge 152のmsedge.dll内の同一オフセットを示していました。
  • 例外は読み取りアクセス違反0xC0000005で、0x18から0x21までのnull近傍アドレスに対するものでした。
  • この一致は、共通のネイティブ障害経路を裏付けます。ただし、内部のソース行、リグレッションの範囲、セキュリティへの影響は特定できません。
  • 観測上最も信頼性の高い回避策は、--remote-debugging-portを指定してEdgeを起動し、connect_over_cdp()を介して接続することでした。

Crashpadの収集

EdgeとChromiumは、プロセスの異常終了をCrashpadミニダンプに記録できます。これらのファイルには、例外の種類、命令アドレス、読み込まれたモジュール、その他のプロセス状態が含まれる場合があります。

それぞれ独立して生成された3つのダンプを比較しました。

A: Playwright + Edge / ヘッドレス
B: Playwright + Edge / ヘッド付き
C: 生のCDPパイプ + Edge / Playwrightなし

3つすべてに、次の同一の関連シグネチャが含まれていました。

例外コード:       0xC0000005
アクセス種別:     読み取り
モジュール:        msedge.dll 152.0.4191.53
モジュールオフセット: 0x9D5C88B
無効なアドレス:   0x18-0x21

0xC0000005の解釈

Windowsでは、アクセス違反、すなわちプロセスが無効なメモリアドレスを読み取り、書き込み、または実行しようとした場合に0xC0000005が使用されます。

記録されたアクセス種別はreadでした。アドレスはnull近傍範囲の非常に小さな値でした。これはnullポインターに関連するアクセスと整合しますが、ダンプ情報だけではEdge内部の特定の変数やソース式を識別するには不十分です。

そのレベルまで原因を特定するには、対応するシンボル、完全なスタック、ブラウザベンダーによるソースレベルの解析が必要です。

モジュールオフセットの比較

アドレス空間配置のランダム化により、プロセスごとにmsedge.dllが異なる絶対メモリアドレスへ配置される場合があります。したがって、比較可能な値はモジュールベースからの相対位置です。

モジュールオフセット = 例外アドレス - モジュールベース

絶対アドレスは異なっていましたが、3つのダンプすべてでモジュールオフセットは0x9D5C88Bでした。このことから、同じPlaywrightエラーとして表面化した無関係な3つの終了である可能性よりも、共通のネイティブ障害経路である可能性が大幅に高まります。

この結果から確認できること

直接観測された事実

  • Edgeは0xC0000005で終了しました。
  • 障害を起こしたアクセスは、無効なアドレスからの読み取りでした。
  • 命令はバージョン152.0.4191.53のmsedge.dll内にありました。
  • 3つの実行経路で同一のモジュールオフセットが生成されました。
  • パイプ経路では問題が再現し、対照となるポート経路では8 / 8回の実行が完了しました。

裏付けられる解釈

  • TargetClosedErrorはEdgeプロセスの終了後に発生したものであり、元の障害ではありませんでした。
  • ヘッド付き、ヘッドレス、生のCDPによる実行は、同一のネイティブ障害に到達した可能性が高いと考えられます。
  • CDPのトランスポート経路は、記録された再現における重要な変数でした。

依然として不明な点

  • 原因となったEdgeのソースコンポーネント
  • Edge 152でこの挙動が導入されたかどうか
  • 他のオペレーティングシステムのビルドやマシンで発生する頻度
  • セキュリティへの影響があるかどうか

この問題は安定性のバグとして報告されています。現時点のデータは、脆弱性またはCVEであるとの主張を正当化するものではありません。

ポートベースの緩和策

観測上最も信頼性の高い回避策では、次の構成を使用します。

  1. 自動化専用のプロファイルを使用します。
  2. --remote-debugging-portを指定してEdgeを独立して起動します。
  3. エンドポイントをlocalhostにバインドしたままにします。
  4. Playwrightからconnect_over_cdp()を使用して接続します。
  5. 既存のブラウザコンテキストとページを使用します。

Playwrightからの接続は簡単です。

browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
context = browser.contexts[0]
page = context.pages[0]

Playwrightのドキュメントでは、CDP接続はネイティブプロトコルよりも忠実度が低くなる可能性があるとされています。また、Edgeを独立して起動すると、ブラウザの引数やライフサイクルの挙動が変わる場合もあります。したがって、ポート経路は観測された緩和策であり、根本原因の修正ではありません。

その他の実用的な選択肢

選択肢 適している場合 トレードオフ
Edge + ポート + connect_over_cdp() Edge固有の挙動と保持されたプロファイル状態が必要 接続の忠実度と起動方法の違いを検証する必要がある
Playwright同梱のChromium Edge固有の挙動が不要 本番環境のEdgeブラウザ自体はテストされない
プロセスごとに新しいプロファイル 認証状態を永続化する必要がない 実行のたびに認証とセットアップを繰り返す必要がある

公開資料

クラッシュダンプにはプロセスメモリやローカル情報が含まれる可能性があるため、公開していません。

結論

調査は、アプリケーションレベルの例外から、依存関係のない生のCDPによる再現、制御されたパイプ経路とポート経路の比較、そして繰り返し確認されたネイティブクラッシュシグネチャへと進展しました。

恒久的な成果は、Edgeの内部ソースコードに関する主張ではありません。それは、範囲を限定した再現手順、対照マトリックス、実用的な緩和策、そして別のエンジニアが調査して再実行できるアップストリームへの報告です。