AdGuard for Windows によるページ読み込み不具合(修正済み): 原因調査と再発防止策
最近、一部の AdGuard for Windows ユーザーの方がアプリの不具合に直面していました。AdGuard for Windows v7.22 をリリースした後、このアップデートによりブラウザでページ読み込みが時々うまく行かなくなる問題が発生していることが判明しました。Chrome については比較的早く問題を緩和できましたが、Firefox では、バグが非常に稀かつ再現が難しく、テスト時には検出されなかった要因の組み合わせによって発生していたため、より長い期間にわたって影響が続いていました。
11月13日時点では、問題を完全に解消するホットフィックス「AdGuard for Windows v7.22.1」をリリース済みです。 円滑にご利用いただくため、アプリを最新バージョンに更新していただくようお願いいたします。なお、他のプラットフォームで AdGuard をお使いの場合は、特に操作していただく必要はありません。通常どおり問題なく動作しているはずです。
ご不便とご迷惑をおかけしましたことを、心よりお詫び申し上げます。今回の件が、皆さまの AdGuard に対する全体的な印象やご体験を損なうことのないよう、強く願っております。今回の事象は一度きりのものであり、今後同じことが起こらないようにいたします。技術的な問題を解決するだけでなく、社内の各種プロセスを見直し、今後同様の事態を未然に防ぐための改善をすでにいくつも実施しました。
以下では、今回のインシデントに至るまでのタイムラインをご説明し、その後で、より技術的な詳細と、今後同様の問題を防ぐために講じている対策についてご紹介します。
事象のタイムライン
10月2日
AdGuard for Windows v7.22 をリリースしました。
10月4日
あるユーザーの方が、v7.22 にアップデートした後にページ読み込みの問題が発生し、最終的には Timed out エラーが表示されるようになった、という内容の GitHub Issue を作成しました。
10月6日
QA チームが本件の調査を開始しましたが、この時点では問題の再現に失敗しました。同日中に開発チームへエスカレーションが行われ、社内での議論が始まりました。
10月16日
GitHub 上のディスカッションにはさらに多くのユーザーが参加し、報告とアップボートが積み上がっていきました。残念ながらこの段階で、QA チームはこのレベルの問題に対する社内のエスカレーションガイドラインに沿った対応をできていませんでした。
ユーザーからの報告内容に一貫性がなく、バグの再現に非常に苦労したこともあり、「この問題は数バージョン前に導入されたものであり、緊急のホットフィックスは不要ではないか」という誤った判断につながってしまいました。
その結果、この問題は次回の定期リリースに回され、タスク自体は P1: Critical(最重要)としてマークされていたにもかかわらず、適切なエスカレーションが行われず、貴重な時間が失われてしまいました。
10月30日
最初の報告から 24 日後、社内で改めてこの問題が大きく取り上げられ、ユーザーへの影響の大きさがようやく明らかになりました。問題の再現に成功したことで、全体的な影響度をより正確に見積もることができました。直ちにホットフィックス(v7.22.1)に向けた作業を開始しましたが、工数の多くは、問題を安定して再現し、その主な原因を特定することに費やされました。この過程で複数の仮説を検証し、その多くを却下しています。
調査の途中で、ページ読み込みをさらに遅くし、デバッグを複雑にしていた QUIC 接続に関連する Firefox のバグ も発見しました。加えて、Wintun を無効にした互換モードで AdGuard と AdGuard VPN を併用している場合に、QUIC 接続が適切にフィルタリングされていないという 別の問題 も判明しました。
11月1日
原因の一部を特定し、再現手順も確立できました。当社の DNS フィルタリングエンジンである DnsLibs に修正を加え、テスト用の nightly ビルドをリリースしました。
11月5日
AdGuard のフィルタリングエンジンである CoreLibs のルーティングループ検出コンポーネントに存在する バグ こそが、主な原因であることを突き止めました。この問題はすぐに修正され、2つ目の nightly ビルドがリリースされました。
11月6日
nightly ビルドのテストの結果、最初の修正によって TCP 接続に関する問題のほとんどは解消されているものの、UDP に関連する問題の一部がまだ残っていることが分かりました。ユーザーへの影響を最小限に抑えるため、修正を 2 段階で展開する方針を取りました。
まず 3 つ目の nightly ビルドで、ライブラリの最終的な修正やドライバ関連の指示更新などを含む追加の問題に対処しました。そのうえで、v7.22.1 パッチを準備し、十分なテストを行った後、同日中にリリースしました。
技術的な詳細
バグの内容
AdGuard for Windows v7.22 に含まれていたバグにより、Firefox で特定のページを読み込む際に、まれに予測不能なフリーズが発生していました。Chrome ユーザーへの影響は、Firefox に比べるとずっと小さいものでした。
この問題は、トラフィックが自分自身へループして戻ることを防ぐための仕組みとして、CoreLibs v1.19 に「ルーティングループ保護」が導入されたことをきっかけに発生し始めました。
このバグには、主に 2 つの原因がありました。
1つ目は、ルーティングループチェックからポートが除外されてしまうというプログラミング上のミスです。その結果、ブラウザからのリクエストのような正当なフィルタリング対象の接続であっても、ブロックされてしまう場合がありました。これは、OCSP リクエストなど、AdGuard 自身から発生する特定のサービスリクエストをきっかけとして発生し、そのようなリクエストの直後に行われるブラウザ接続が中断されてしまう、という形で表面化していました。
2つ目は、このチェックが適用されるタイミングが不適切で、すでに確立されている接続に対して行われていたことです。そのため、本来ブロックされるべきでない接続が不要に遮断されることがありました。
なぜルーティングループ保護が必要なのか
ルーティングループ とは、トラフィックが元のアプリケーションにループして戻ってきてしまう状態のことです。このようなループが発生すると、接続が遅くなったり、CPU 使用率が異常に高くなったりする原因となります。通常の状況では起こらないはずの現象ですが、他のソフトウェアとの組み合わせによっては発生する可能性があります。
これを防ぐために、AdGuard は送信元アドレスごとに送信接続を追跡し、同じアドレスに戻ってきた接続をルーティングループとして検出し、終了させる仕組みを備えています。
なぜ Chrome への影響は小さかったのか
今回の問題では、サービスリクエストの直後に発生した 1 回分の接続だけが失敗する、という形で現れていました。Chrome には、接続がリセットされた場合に同じネットワークリクエストを自動的に再試行する仕組みがあるため、ユーザー側が問題に気付く可能性は非常に低くなっていました。
一方で Firefox には、ネットワークエラー発生時に自動でリトライするような設計がありません。そのため、Firefox ユーザーは、この問題の影響をより直接的かつ目に見える形で受けることになりました。
AdGuard for Linux や for Android ではどうだったのか
今回の問題は、CoreLibs v1.19 をまず最初に導入する AdGuard CLI の段階では検出されませんでした。通常、新しい機能は UI ベースのアプリケーションへ統合する前に、重大な問題を早期に発見・修正する目的で、まず CLI 版に導入されます。
しかし今回のケースでは、問題が発生しうるのは「Linux の自動モード」かつ「主に Firefox 使用時」という条件にほぼ限定されており、この条件に当てはまるユーザーはごくわずかでした。そのため、ユーザーからの報告も届いていませんでした。
同じ CoreLibs 1.19 を使用している AdGuard for Android v4.12 では、受信側と送信側の接続アドレスが異なっていたため、今回のバグが成立する条件が揃わず、問題は発生しませんでした。
診断の難しさ
この問題の診断は、特に困難でした。AdGuard が開くサービス接続の数は比較的少なく、再現を試みる際に OCSP リクエストがキャッシュから処理されてしまうことが多かったため、バグが表に出てこないケースがほとんどだったのです。
さらに、問題は一度に 1 件の「後続接続」にしか影響しませんでした。前述のとおり、Chrome は失敗した接続を自動的に再試行するため、Chrome ユーザーはほぼ影響を受けません。一方で Firefox はネットワークエラー発生時に接続を再試行しない設計のため、Firefox ユーザーにとっては問題が直接的な不具合として現れてしまいました。
調査中に判明した関連する Firefox のバグ
この見つけにくいバグを追っている最中、寄せられていた報告の約半数では症状が異なり、過去バージョンの AdGuard でも同様の問題が再現し始めていることに気付きました。その過程で、Windows 版 Firefox において HTTP/3 接続に影響を与える、別の バグ が存在することも判明しました。
簡単に言うと、Firefox は、あるサイトが HTTP/3 をサポートしていると広告している場合、たとえ実際には HTTP/3 接続がまだ利用できない状況であっても、すぐに HTTP/3 接続を試みてしまいます。現在の AdGuard ではデフォルトで HTTP/3 をフィルタリングしていないため、HTTPS フィルタリングが有効になっているアプリケーションに対しては、HTTP/3 がブロックされる形になります。
一般的にブラウザは、Happy Eyeballs のように「どのプロトコルが最適に動作するか」を選択するアルゴリズムを備えていますが、Windows 版 Firefox は、サイト側から(HTTPS タイプの DNS レコードなどを通じて)HTTP/3 の情報を受け取ると、すでに正常に稼働している HTTP/2 接続が存在していても、それを無視して確立されていない HTTP/3 接続側にリクエストを割り当ててしまいます。
もし HTTP/3 が利用できない場合、サイトの読み込みは 20〜30 秒程度も一時停止し、その後になってようやくリクエストが稼働中の HTTP/2 接続へ「振り替え」られる、という挙動になります。
この挙動については、すでに バグ報告 が行われています。予防的な対策として、AdGuard では、HTTP/3 フィルタリングが無効になっている場合に、HTTPS タイプの DNS レコードから h3 ALPN パラメータを除外するようにしました。こうすることで、どうせ AdGuard によってブロックされる状況では、ブラウザ側から HTTP/3 が利用可能である事実自体を隠すことができます。
修正内容
今回の修正は、2 つのフェーズに分けて行いました。
最初のフェーズでは、接続の照合ロジックを見直し、ポートも含めて正しくマッチングされるようにしました。これにより問題の大半は解決しましたが、Windows が最近クローズされた接続のポートを再利用する挙動のため、依然として一部で誤検知が発生していました。11月5日夜にリリースした nightly ビルドは、多くのユーザーにとってこの段階で大きな改善となりました。
しかし、AdGuard のログには依然として問題の痕跡が残っていました。接続マッチングのアルゴリズムを修正しただけでは不十分で、Windows がソケット解放後 1 秒以内に同じポート番号を別の外向き接続に再利用することがある、という点が新たな誤検知の原因になっていたのです。その結果、アドレスとポートが一致していても、実際には無関係な 2 つの接続がルーティングループとして誤って扱われるケースがありました。
明確な新規インシデント(「まだ動かない」という報告)は見られず、ユーザーの皆さまからは「問題なく動いている」との声が大半でしたが、潜在的には多くのユーザーが影響を受ける可能性が残されていました。そこで第 2 フェーズとして、こうしたケースにも対応する修正を行い、最終的なホットフィックスである v7.22.1 をリリースすることで、問題を完全に解消しました。
再発防止策
今回の問題が早期に検出できなかった主な理由は、通常のテスト条件では再現が非常に難しかったこと、そしてユーザーからのフィードバックに対する注意が十分ではなかったことにあります。
現在、QA プロセスと開発プロセスの両方を見直しており、より厳密なプロセス監視、オートメーションの強化、チーム間のテストおよびコミュニケーションの徹底といった点に特に力を入れています。これにより、今後同様のインシデントを未然に防ぎ、すべてのユーザーにとって AdGuard の信頼性をさらに高めていくことを目指しています。
QA チームのワークフロー変更
QA チームは今後、GitHub Issues におけるコメント数やアップボート数をこれまで以上に重視して監視していきます。人手による確認だけに依存しないようにするため、公開 Issue におけるアクティビティやアップボート数を自動的にトラッキングする仕組みも導入します。このアプローチが有効であると確認できた場合には、AdGuard のすべてのプロジェクトに関わる QA チームへとスケールさせていく予定です。
また、社内の Triage ガイドラインに基づいた追加ブリーフィングを実施し、Jira 上でのインシデント評価において、このガイドラインに沿った理由付けを必須とする運用を導入します。これにより、優先度の判断に一貫性と透明性を持たせることができます。
さらに、ユーザーからのフィードバックに基づいてフィルタリング関連の問題を特定・分析しやすくするため、ユーザーに確認する診断用質問リストも用意します。
最後に、同様の問題を将来防ぐため、いくつかの自動テストを新たに実装します。ブラウザごとのページフィルタリング速度を評価するベンチマークテスト用スクリプトを開発中で、基準となるパフォーマンスが定まった後は、以降のすべてのリリースでそれに対する比較を行う予定です。現時点では自動テストは Google Chrome のみを対象としていますが、今後は Firefox にも拡大する計画です。
さらに、あらかじめ選定した「問題が起きやすい」ページ群について、各ブラウザでの読み込み速度を測定するテストも作成します。このページリストは、既知の問題サイトをベースにしつつ、今回の調査範囲で特定されたサイト(例えば discord.com)から順次拡張していきます。
開発チームのワークフロー変更
開発チームは今後、新機能を追加する際に、可能な限り新しいテスト(統合テストを含む)を併せて追加することに、これまで以上に注意を払います。これにより、将来のリリースで不具合が紛れ込む可能性を低減させます。
あわせて、新機能に関する技術ドキュメントをより充実させること、そして、その機能に関わるすべてのチームが、潜在的な問題やコーナーケースの観点からテストの必要性を十分に理解できるよう情報共有を徹底することにも重点を置きます。
また、CoreLibs の新バージョンを各プロダクトへ統合する際には、今後は CoreLibs チームからの明示的な承認を得てから作業を進めるようにします。現在のプロセスでは、統合作業がやや「孤立した」形で進められており、そのことが問題を見逃すリスクを高めていました。
まとめ
今回のインシデントによって影響を受けたすべてのユーザーの皆さまに、改めてお詫び申し上げます。また、フィードバックをお寄せいただき、この困難な状況を乗り越えるうえで多大な助けとなってくださった皆さまに、心より感謝いたします。
今後は、ユーザーの皆さまに大きな影響を与えうる重大な問題について、より迅速かつ透明性の高い形で情報をお伝えしていけるよう努めてまいります。