
CVE-2026-36590(NanoMQ v0.24.9 のサービス拒否脆弱性)に関する公開勧告と技術分析。
このリポジトリには、EMQ NanoMQ v0.24.9 に影響するサービス拒否脆弱性 CVE-2026-36590 に関する公開アドバイザリと技術分析が含まれています。
このリポジトリは、CVE レビューのためのアクセス可能な技術リファレンスを提供する目的で公開されています。CVE-2026-36590 の最終的な分類は、CVE チームまたは該当する CNA によって決定されます。
NanoMQ チームはこの問題を争っており、設定に関連するものと見なしています。このリポジトリは、再現資料、ASAN 結果、ランタイムログ、ソースコード分析を含む技術的エビデンスに焦点を当てています。
/nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c/ nni_qos_db_setNanoMQ v0.24.9 が SQLite サポートなしでビルドされ、sqlite.enable が true に設定されている場合、QoS データベースポインタが NULL のままになる可能性があります。MQTT QoS メッセージ処理中に、nni_qos_db_set がクローンされたメッセージ参照を解放せずに早期リターンする可能性があります。QoS > 0 の MQTT PUBLISH メッセージが繰り返されると、継続的にメモリが消費され、最終的にサービス拒否に至ります。
この問題はデフォルト以外のランタイム設定に依存しますが、以下の事実はなぜさらなるレビューが必要かを示しています。
NanoMQ はランタイム設定を受け入れ、SQLite サポートが現在のビルドで利用できないことを報告せずに実行を継続します。
この設定の不一致は実際に発生する可能性があります。ユーザーは通常のビルドコマンドで NanoMQ をビルドした後、後でサンプル設定ファイルから SQLite 永続化セクションをコピーまたは有効にする場合があります。
影響を受けるコードパスは、この不整合な状態を完全に検証しません。ランタイム設定が SQLite を有効として扱いながらデータベースポインタが NULL である場合、nni_qos_db_set() は渡されたメッセージオブジェクトを解放せずに早期リターンします。
この設定が受け入れられた後、リモートの MQTT トラフィックによって問題がトリガーされる可能性があります。QoS > 0 の PUBLISH メッセージが繰り返されると、影響を受けるパスに入り、累積的なメモリリークを引き起こす可能性があります。
1回、50回、500回の再現ラウンドでの ASAN テストでは、リークされたバイト数とアロケーション数がトリガー試行回数に応じて増加することが示されています。これは、問題が一度きりの小さなリークではなく、入力数に依存することを示唆しています。
ユーザーは NanoMQ インスタンスへのアクセスを制限し、信頼できないクライアントに MQTT サービスを公開しないようにし、SQLite サポートなしのビルドで SQLite 永続化を有効にしないようにする必要があります。ベンダーは、SQLite 設定の起動時検証を追加し、nni_qos_db_set の db == NULL ブランチでメッセージ参照を解放する必要があります。
Docker/Docker_Reproduction_Guide.md概要 このリポジトリには、NanoMQ で見つかったメモリリーク脆弱性の Proof of Concept (PoC) と詳細な分析が含まれています。この問題により、リモート攻撃者が特定の QoS MQTT メッセージを介してシステムメモリを枯渇させ、サービス拒否 (DoS) を引き起こすことが可能です。
私は NanoMQ におけるメモリリーク現象の詳細な分析を実施しました。動的インストルメンテーションとコードレビューを通じて、ランタイム設定で SQLite が有効 (sqlite.enable=true) になっているが、バイナリが SQLite サポートなしでコンパイルされている (NNG_SUPP_SQLITE が未定義) 場合にトリガーされる重大なリソースリークパスを特定しました。
この問題を簡潔に保つため、以下に主要な発見事項をまとめました。完全な技術的内訳 (詳細な ASAN トレース、ライフサイクル図、インストルメンテーションログを含む) については、添付の Analysis_Report.md を参照してください。
AddressSanitizer を使用して、リークされたメモリの発生源が tcptran_pipe_recv_cb (nng/src/sp/transport/mqtt/broker_tcp.c) 内の nni_msg_alloc にあることを特定しました。メモリは割り当てられたものの、完全には解放されませんでした。
nni_msg の参照カウントメカニズムを分析しました。通常の QoS 1 メッセージのライフサイクルは以下のようになります。
GDB はこの高並行性シナリオには実用的でなかったため、動的インストルメンテーションを使用して特定のメモリオブジェクト (例: 0x60e00002ffa0) のライフサイクルをトレースしました。
発見: ログにより、ライフサイクルの不一致が確認されました。
nmq_pipe_send_start_v4 で生成された Clone 2 に対応します。実行フローをトレースすることで、3 番目の解放が欠落している理由が明らかになりました。
-DNNG_SUPP_SQLITE なしでコンパイルされたため、nano_sock_setdb 内の初期化ロジックが除去されました。しかし、nanomq.conf では sqlite.enable = true でした。これにより、db ポインタが NULL のままになりました。nni_qos_db_set に入ると、関数は NULL ポインタをチェックします:// File: /nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c
void nni_qos_db_set(..., void *db, ..., nng_msg *msg) {
if (db == NULL) {
// 重大な欠陥:
// db が NULL のため早期リターンが発生。
// 関数は 'msg' (Ref++) の所有権を保持するが、解放に失敗。
return;
}
// ...
}
関数は nni_msg_free(msg) を呼び出さずに黙ってリターンします。これにより、msg ポインタは回復不能に失われ、メモリブロックが実質的に孤立します。
nni_qos_db_set における防御的プログラミングの欠如。無効な DB 状態による早期リターンを実行する際に、msg ポインタの所有権を適切に処理していません。推奨事項:
nano_sock_setdb または main 関数) で検証ロジックを追加し、SQLite が正しく動作していることを確認します。conf->sqlite.enable == true が検出されたが NNG_SUPP_SQLITE マクロが未定義であるか SQLite が異常である場合、システムはエラーで終了するか、強制的に enable を false に設定し警告を出力する必要があります。nni_qos_db_set 関数の早期リターンブランチにリソース解放ロジックを追加します。db == NULL の場合、参照カウントをバランスさせメモリリークを防ぐために、nni_msg_free(msg) を呼び出す必要があります。